Merge "Remove llndk_headers module" am: aaf8e170a6 am: b86e54d22b am: 4078d3da96
Original change: https://android-review.googlesource.com/c/platform/frameworks/native/+/1687409
Change-Id: I0e3816057ecd94461f7e291fc9033b98bc8b9dc3
diff --git a/.gitignore b/.gitignore
index 0d20b64..ed653c6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,5 @@
+*.iml
*.pyc
+.idea/
+.vscode/
+*.code-workspace
diff --git a/Android.bp b/Android.bp
index 6fe0246..dec6716 100644
--- a/Android.bp
+++ b/Android.bp
@@ -81,3 +81,14 @@
":framework_native_aidl_gui",
],
}
+
+cc_library_headers{
+ name: "libandroid_headers_private",
+ export_include_dirs: ["include/private"],
+}
+
+filegroup {
+ name: "deviceproductinfoconstants_aidl",
+ srcs: ["aidl/android/hardware/display/IDeviceProductInfoConstants.aidl"],
+ path: "aidl",
+}
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 16ebf6f..8bcb1e5 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -7,19 +7,23 @@
cmds/idlcli/
cmds/servicemanager/
include/input/
+ include/powermanager/
libs/binder/fuzzer/
libs/binder/
libs/binderthreadstate/
libs/graphicsenv/
libs/gui/
libs/input/
+ libs/nativedisplay/
libs/renderengine/
libs/ui/
libs/vr/
opengl/libs/
services/bufferhub/
services/inputflinger/
+ services/powermanager/
services/surfaceflinger/
+ services/vibratorservice/
services/vr/
vulkan/
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 8173c89..307e21c 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -53,11 +53,22 @@
},
{
"include-filter": "*RelativeZTest.*"
+ },
+ {
+ "include-filter": "*RefreshRateOverlayTest.*"
}
]
},
{
"name": "libsurfaceflinger_unittest"
+ },
+ {
+ "name": "CtsGraphicsTestCases",
+ "options": [
+ {
+ "include-filter": "android.graphics.cts.VulkanPreTransformTest"
+ }
+ ]
}
]
}
diff --git a/aidl/android/hardware/display/IDeviceProductInfoConstants.aidl b/aidl/android/hardware/display/IDeviceProductInfoConstants.aidl
new file mode 100644
index 0000000..7cc272a
--- /dev/null
+++ b/aidl/android/hardware/display/IDeviceProductInfoConstants.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2021 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 android.hardware.display;
+
+/** @hide */
+interface IDeviceProductInfoConstants {
+ /** The device connection to the display sink is unknown. */
+ const int CONNECTION_TO_SINK_UNKNOWN = 0;
+
+ /** The device is built-in in the display sink. */
+ const int CONNECTION_TO_SINK_BUILT_IN = 1;
+
+ /** The device is directly connected to the display sink. */
+ const int CONNECTION_TO_SINK_DIRECT = 2;
+
+ /** The device is transitively connected to the display sink. */
+ const int CONNECTION_TO_SINK_TRANSITIVE = 3;
+}
\ No newline at end of file
diff --git a/aidl/gui/android/view/LayerMetadataKey.aidl b/aidl/gui/android/view/LayerMetadataKey.aidl
index 7026ca8..a1d8ce5 100644
--- a/aidl/gui/android/view/LayerMetadataKey.aidl
+++ b/aidl/gui/android/view/LayerMetadataKey.aidl
@@ -23,4 +23,7 @@
METADATA_WINDOW_TYPE = 2,
METADATA_TASK_ID = 3,
METADATA_MOUSE_CURSOR = 4,
+ METADATA_ACCESSIBILITY_ID = 5,
+ METADATA_OWNER_PID = 6,
+ METADATA_DEQUEUE_TIME = 7,
}
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 6f5f70d..79419d3 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -99,7 +99,9 @@
/* Tracing categories */
static const TracingCategory k_categories[] = {
- { "gfx", "Graphics", ATRACE_TAG_GRAPHICS, { } },
+ { "gfx", "Graphics", ATRACE_TAG_GRAPHICS, {
+ { OPT, "events/gpu_mem/gpu_mem_total/enable" },
+ } },
{ "input", "Input", ATRACE_TAG_INPUT, { } },
{ "view", "View System", ATRACE_TAG_VIEW, { } },
{ "webview", "WebView", ATRACE_TAG_WEBVIEW, { } },
@@ -242,6 +244,7 @@
{ OPT, "events/kmem/ion_heap_grow/enable" },
{ OPT, "events/kmem/ion_heap_shrink/enable" },
{ OPT, "events/ion/ion_stat/enable" },
+ { OPT, "events/gpu_mem/gpu_mem_total/enable" },
} },
{ "thermal", "Thermal event", 0, {
{ REQ, "events/thermal/thermal_temperature/enable" },
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index b8df99f..336c5a2 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1371,7 +1371,8 @@
static void DumpHals(int out_fd = STDOUT_FILENO) {
if (!ds.IsZipping()) {
RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all", "--debug"},
- CommandOptions::WithTimeout(60).AsRootIfAvailable().Build());
+ CommandOptions::WithTimeout(60).AsRootIfAvailable().Build(),
+ false, out_fd);
return;
}
RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all"},
@@ -2418,7 +2419,9 @@
static void Vibrate(int duration_ms) {
// clang-format off
- RunCommand("", {"cmd", "vibrator", "vibrate", "-f", std::to_string(duration_ms), "dumpstate"},
+ std::vector<std::string> args = {"cmd", "vibrator_manager", "synced", "-f", "-d", "dumpstate",
+ "oneshot", std::to_string(duration_ms)};
+ RunCommand("", args,
CommandOptions::WithTimeout(10)
.Log("Vibrate: '%s'\n")
.Always()
diff --git a/cmds/flatland/GLHelper.cpp b/cmds/flatland/GLHelper.cpp
index 3a3df08..01f7d30 100644
--- a/cmds/flatland/GLHelper.cpp
+++ b/cmds/flatland/GLHelper.cpp
@@ -19,7 +19,7 @@
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <gui/SurfaceComposerClient.h>
-#include <ui/DisplayConfig.h>
+#include <ui/DisplayMode.h>
namespace android {
@@ -227,15 +227,15 @@
return false;
}
- DisplayConfig config;
- status_t err = mSurfaceComposerClient->getActiveDisplayConfig(dpy, &config);
+ ui::DisplayMode mode;
+ status_t err = mSurfaceComposerClient->getActiveDisplayMode(dpy, &mode);
if (err != NO_ERROR) {
- fprintf(stderr, "SurfaceComposer::getActiveDisplayConfig failed: %#x\n", err);
+ fprintf(stderr, "SurfaceComposer::getActiveDisplayMode failed: %#x\n", err);
return false;
}
- float scaleX = static_cast<float>(config.resolution.getWidth()) / w;
- float scaleY = static_cast<float>(config.resolution.getHeight()) / h;
+ float scaleX = static_cast<float>(mode.resolution.getWidth()) / w;
+ float scaleY = static_cast<float>(mode.resolution.getHeight()) / h;
*scale = scaleX < scaleY ? scaleX : scaleY;
return true;
diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp
index 99e0f4c..ec3bc61 100644
--- a/cmds/idlcli/Android.bp
+++ b/cmds/idlcli/Android.bp
@@ -24,7 +24,7 @@
cc_defaults {
name: "idlcli-defaults",
shared_libs: [
- "android.hardware.vibrator-V1-ndk_platform",
+ "android.hardware.vibrator-V2-ndk_platform",
"android.hardware.vibrator@1.0",
"android.hardware.vibrator@1.1",
"android.hardware.vibrator@1.2",
@@ -46,10 +46,25 @@
defaults: ["idlcli-defaults"],
srcs: [
"CommandVibrator.cpp",
+ "vibrator/CommandAlwaysOnDisable.cpp",
+ "vibrator/CommandAlwaysOnEnable.cpp",
"vibrator/CommandCompose.cpp",
+ "vibrator/CommandComposePwle.cpp",
+ "vibrator/CommandGetBandwidthAmplitudeMap.cpp",
"vibrator/CommandGetCapabilities.cpp",
"vibrator/CommandGetCompositionDelayMax.cpp",
"vibrator/CommandGetCompositionSizeMax.cpp",
+ "vibrator/CommandGetFrequencyMinimum.cpp",
+ "vibrator/CommandGetFrequencyResolution.cpp",
+ "vibrator/CommandGetPrimitiveDuration.cpp",
+ "vibrator/CommandGetPwleCompositionSizeMax.cpp",
+ "vibrator/CommandGetPwlePrimitiveDurationMax.cpp",
+ "vibrator/CommandGetQFactor.cpp",
+ "vibrator/CommandGetResonantFrequency.cpp",
+ "vibrator/CommandGetSupportedAlwaysOnEffects.cpp",
+ "vibrator/CommandGetSupportedBraking.cpp",
+ "vibrator/CommandGetSupportedEffects.cpp",
+ "vibrator/CommandGetSupportedPrimitives.cpp",
"vibrator/CommandOff.cpp",
"vibrator/CommandOn.cpp",
"vibrator/CommandPerform.cpp",
diff --git a/cmds/idlcli/CommandVibrator.cpp b/cmds/idlcli/CommandVibrator.cpp
index a7a70c3..81bdbe2 100644
--- a/cmds/idlcli/CommandVibrator.cpp
+++ b/cmds/idlcli/CommandVibrator.cpp
@@ -22,7 +22,7 @@
class IdlCli;
class CommandVibrator : public CommandWithSubcommands<CommandVibrator> {
- std::string getDescription() const override { return "Invoke Vibrator HIDL APIs."; }
+ std::string getDescription() const override { return "Invoke Vibrator IDL APIs."; }
std::string getUsageSummary() const override { return "<api> [arguments]"; }
diff --git a/cmds/idlcli/IdlCli.h b/cmds/idlcli/IdlCli.h
index dd84304..24a40d9 100644
--- a/cmds/idlcli/IdlCli.h
+++ b/cmds/idlcli/IdlCli.h
@@ -25,14 +25,47 @@
class IdlCli : public CommandWithSubcommands<IdlCli> {
std::string getDescription() const override { return "Invoke IDL APIs."; }
- std::string getUsageSummary() const override { return "<idl> [arguments]"; }
+ std::string getUsageSummary() const override { return "<idl> [options] [arguments]"; }
UsageDetails getUsageDetails() const override {
UsageDetails details{
+ {"-n <name>", {"Get named service, rather than default."}},
{"<idl>", CommandRegistry<IdlCli>::List()},
};
return details;
}
+
+ Status doArgs(Args &args) override {
+ while (args.get<std::string>().value_or("").find("-") == 0) {
+ auto opt = *args.pop<std::string>();
+ if (opt == "--") {
+ break;
+ } else if (opt == "-n") {
+ if (auto name = args.pop<decltype(mName)>()) {
+ mName = *name;
+ } else {
+ std::cerr << "Missing Value for Name!" << std::endl;
+ return USAGE;
+ }
+ } else {
+ std::cerr << "Invalid Option '" << opt << "'!" << std::endl;
+ return USAGE;
+ }
+ }
+ return CommandWithSubcommands::doArgs(args);
+ }
+
+ IdlCli() {}
+
+ std::string mName;
+
+public:
+ static IdlCli &Get() {
+ static IdlCli instance;
+ return instance;
+ }
+
+ auto getName() { return mName; }
};
} // namespace idlcli
diff --git a/cmds/idlcli/main.cpp b/cmds/idlcli/main.cpp
index 9ed9d82..308f294 100644
--- a/cmds/idlcli/main.cpp
+++ b/cmds/idlcli/main.cpp
@@ -19,5 +19,5 @@
int main(const int argc, const char* const argv[]) {
using namespace ::android::idlcli;
- return IdlCli{}.main(Args{argc, argv});
+ return IdlCli::Get().main(Args{argc, argv});
}
diff --git a/cmds/idlcli/utils.h b/cmds/idlcli/utils.h
index a8e5954..262f2e5 100644
--- a/cmds/idlcli/utils.h
+++ b/cmds/idlcli/utils.h
@@ -17,6 +17,7 @@
#ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_
#define FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_
+#include <android/binder_enums.h>
#include <hidl/HidlSupport.h>
#include <iomanip>
@@ -66,7 +67,7 @@
} // namespace overrides
-template <typename T, typename R = hardware::hidl_enum_range<T>>
+template <typename T, typename R = ndk::enum_range<T>>
inline std::istream &operator>>(std::istream &stream, T &out) {
using overrides::operator>>;
auto validRange = R();
@@ -248,7 +249,7 @@
template <typename T>
class CommandWithSubcommands : public Command {
-private:
+protected:
Status doArgs(Args &args) override {
mCommand = CommandRegistry<T>::Create(*args.get());
if (!mCommand) {
diff --git a/cmds/idlcli/vibrator.h b/cmds/idlcli/vibrator.h
index ca5142d..dfbb886 100644
--- a/cmds/idlcli/vibrator.h
+++ b/cmds/idlcli/vibrator.h
@@ -13,20 +13,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
-#define FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
+#pragma once
+#include <future>
+
+#include <aidl/android/hardware/vibrator/BnVibratorCallback.h>
#include <aidl/android/hardware/vibrator/IVibrator.h>
+#include <aidl/android/hardware/vibrator/IVibratorManager.h>
#include <android/binder_manager.h>
+#include <android/binder_process.h>
#include <android/hardware/vibrator/1.3/IVibrator.h>
+#include "IdlCli.h"
#include "utils.h"
-#include "log/log.h"
-
namespace android {
using hardware::Return;
+using idlcli::IdlCli;
static constexpr int NUM_TRIES = 2;
@@ -43,20 +47,34 @@
}
template <typename I>
-inline auto getService() {
- return I::getService();
+inline auto getService(std::string name) {
+ const auto instance = std::string() + I::descriptor + "/" + name;
+ auto vibBinder = ndk::SpAIBinder(AServiceManager_getService(instance.c_str()));
+ return I::fromBinder(vibBinder);
}
template <>
-inline auto getService<aidl::android::hardware::vibrator::IVibrator>() {
- const auto instance =
- std::string() + aidl::android::hardware::vibrator::IVibrator::descriptor + "/default";
- auto vibBinder = ndk::SpAIBinder(AServiceManager_getService(instance.c_str()));
- return aidl::android::hardware::vibrator::IVibrator::fromBinder(vibBinder);
+inline auto getService<android::hardware::vibrator::V1_0::IVibrator>(std::string name) {
+ return android::hardware::vibrator::V1_0::IVibrator::getService(name);
+}
+
+template <>
+inline auto getService<android::hardware::vibrator::V1_1::IVibrator>(std::string name) {
+ return android::hardware::vibrator::V1_1::IVibrator::getService(name);
+}
+
+template <>
+inline auto getService<android::hardware::vibrator::V1_2::IVibrator>(std::string name) {
+ return android::hardware::vibrator::V1_2::IVibrator::getService(name);
+}
+
+template <>
+inline auto getService<android::hardware::vibrator::V1_3::IVibrator>(std::string name) {
+ return android::hardware::vibrator::V1_3::IVibrator::getService(name);
}
template <typename I>
-using shared_ptr = std::result_of_t<decltype(getService<I>)&()>;
+using shared_ptr = std::result_of_t<decltype(getService<I>)&(std::string)>;
template <typename I>
class HalWrapper {
@@ -64,7 +82,8 @@
static std::unique_ptr<HalWrapper> Create() {
// Assume that if getService returns a nullptr, HAL is not available on the
// device.
- auto hal = getService<I>();
+ const auto name = IdlCli::Get().getName();
+ auto hal = getService<I>(name.empty() ? "default" : name);
return hal ? std::unique_ptr<HalWrapper>(new HalWrapper(std::move(hal))) : nullptr;
}
@@ -101,9 +120,19 @@
namespace V1_3 = ::android::hardware::vibrator::V1_3;
namespace aidl = ::aidl::android::hardware::vibrator;
+class VibratorCallback : public aidl::BnVibratorCallback {
+public:
+ ndk::ScopedAStatus onComplete() override {
+ mPromise.set_value();
+ return ndk::ScopedAStatus::ok();
+ }
+ void waitForComplete() { mPromise.get_future().wait(); }
+
+private:
+ std::promise<void> mPromise;
+};
+
} // namespace vibrator
} // namespace idlcli
} // namespace android
-
-#endif // FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
diff --git a/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp b/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp
new file mode 100644
index 0000000..9afa300
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2020 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandAlwaysOnDisable : public Command {
+ std::string getDescription() const override { return "Disarm always-on haptic source."; }
+
+ std::string getUsageSummary() const override { return "<id>"; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{
+ {"<id>", {"Source ID (device-specific)."}},
+ };
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (auto id = args.pop<decltype(mId)>()) {
+ mId = *id;
+ std::cout << "Source ID: " << mId << std::endl;
+ } else {
+ std::cerr << "Missing or Invalid Source ID!" << std::endl;
+ return USAGE;
+ }
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::alwaysOnDisable, mId);
+
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+
+ return ret;
+ }
+
+ int32_t mId;
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandAlwaysOnDisable>("alwaysOnDisable");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp b/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp
new file mode 100644
index 0000000..bb7f9f2
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2020 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::Effect;
+using aidl::EffectStrength;
+
+class CommandAlwaysOnEnable : public Command {
+ std::string getDescription() const override {
+ return "Arm always-on haptic source with an effect.";
+ }
+
+ std::string getUsageSummary() const override { return "<id> <effect> <strength>"; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{
+ {"<id>", {"Source ID (device-specific)."}},
+ {"<effect>", {"Effect ID."}},
+ {"<strength>", {"0-2."}},
+ };
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (auto id = args.pop<decltype(mId)>()) {
+ mId = *id;
+ std::cout << "Source ID: " << mId << std::endl;
+ } else {
+ std::cerr << "Missing or Invalid Source ID!" << std::endl;
+ return USAGE;
+ }
+ if (auto effect = args.pop<decltype(mEffect)>()) {
+ mEffect = *effect;
+ std::cout << "Effect: " << toString(mEffect) << std::endl;
+ } else {
+ std::cerr << "Missing or Invalid Effect!" << std::endl;
+ return USAGE;
+ }
+ if (auto strength = args.pop<decltype(mStrength)>()) {
+ mStrength = *strength;
+ std::cout << "Strength: " << toString(mStrength) << std::endl;
+ } else {
+ std::cerr << "Missing or Invalid Strength!" << std::endl;
+ return USAGE;
+ }
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::alwaysOnEnable, mId, mEffect, mStrength);
+
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+
+ return ret;
+ }
+
+ int32_t mId;
+ Effect mEffect;
+ EffectStrength mStrength;
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandAlwaysOnEnable>("alwaysOnEnable");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandCompose.cpp b/cmds/idlcli/vibrator/CommandCompose.cpp
index 4721a5f..eb9008b 100644
--- a/cmds/idlcli/vibrator/CommandCompose.cpp
+++ b/cmds/idlcli/vibrator/CommandCompose.cpp
@@ -28,19 +28,33 @@
class CommandCompose : public Command {
std::string getDescription() const override { return "Compose vibration."; }
- std::string getUsageSummary() const override { return "<delay> <primitive> <scale> ..."; }
+ std::string getUsageSummary() const override {
+ return "[options] <delay> <primitive> <scale> ...";
+ }
UsageDetails getUsageDetails() const override {
UsageDetails details{
+ {"-b", {"Block for duration of vibration."}},
{"<delay>", {"In milliseconds"}},
{"<primitive>", {"Primitive ID."}},
- {"<scale>", {"0.0 (exclusive) - 1.0 (inclusive)."}},
+ {"<scale>", {"0.0 (inclusive) - 1.0 (inclusive)."}},
{"...", {"May repeat multiple times."}},
};
return details;
}
Status doArgs(Args &args) override {
+ while (args.get<std::string>().value_or("").find("-") == 0) {
+ auto opt = *args.pop<std::string>();
+ if (opt == "--") {
+ break;
+ } else if (opt == "-b") {
+ mBlocking = true;
+ } else {
+ std::cerr << "Invalid Option '" << opt << "'!" << std::endl;
+ return USAGE;
+ }
+ }
while (!args.empty()) {
CompositeEffect effect;
if (auto delay = args.pop<decltype(effect.delayMs)>()) {
@@ -50,16 +64,15 @@
std::cerr << "Missing or Invalid Delay!" << std::endl;
return USAGE;
}
- // TODO: Use range validation when supported by AIDL
- if (auto primitive = args.pop<std::underlying_type_t<decltype(effect.primitive)>>()) {
- effect.primitive = static_cast<decltype(effect.primitive)>(*primitive);
+ if (auto primitive = args.pop<decltype(effect.primitive)>()) {
+ effect.primitive = *primitive;
std::cout << "Primitive: " << toString(effect.primitive) << std::endl;
} else {
std::cerr << "Missing or Invalid Primitive!" << std::endl;
return USAGE;
}
if (auto scale = args.pop<decltype(effect.scale)>();
- scale && *scale > 0.0 && scale <= 1.0) {
+ scale && *scale >= 0.0 && scale <= 1.0) {
effect.scale = *scale;
std::cout << "Scale: " << effect.scale << std::endl;
} else {
@@ -76,21 +89,33 @@
}
Status doMain(Args && /*args*/) override {
- std::string statusStr;
- Status ret;
- if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::compose, mComposite, nullptr);
- statusStr = status.getDescription();
- ret = status.isOk() ? OK : ERROR;
- } else {
+ auto hal = getHal<aidl::IVibrator>();
+
+ if (!hal) {
return UNAVAILABLE;
}
- std::cout << "Status: " << statusStr << std::endl;
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
- return ret;
+ std::shared_ptr<VibratorCallback> callback;
+
+ if (mBlocking) {
+ callback = ndk::SharedRefBase::make<VibratorCallback>();
+ }
+
+ auto status = hal->call(&aidl::IVibrator::compose, mComposite, callback);
+
+ if (status.isOk() && callback) {
+ callback->waitForComplete();
+ }
+
+ std::cout << "Status: " << status.getDescription() << std::endl;
+
+ return status.isOk() ? OK : ERROR;
}
+ bool mBlocking;
std::vector<CompositeEffect> mComposite;
};
diff --git a/cmds/idlcli/vibrator/CommandComposePwle.cpp b/cmds/idlcli/vibrator/CommandComposePwle.cpp
new file mode 100644
index 0000000..b8308ce
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandComposePwle.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2019 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 <stdlib.h>
+
+#include <charconv>
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::ActivePwle;
+using aidl::Braking;
+using aidl::BrakingPwle;
+using aidl::PrimitivePwle;
+
+class CommandComposePwle : public Command {
+ std::string getDescription() const override { return "Compose PWLE vibration."; }
+
+ std::string getUsageSummary() const override {
+ return "[options] a <active pwle params> b <braking pwle params> ...";
+ }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{
+ {"-b", {"Block for duration of vibration."}},
+ {"a <startAmplitude> <startFrequency> <endAmplitude> <endFrequency> <duration>",
+ {"Enter the active PWLE segment parameters"}},
+ {"b <brakingMethod> <duration>", {"Enter the braking PWLE segment parameters"}},
+ {"...", {"May repeat multiple times."}},
+ };
+ return details;
+ }
+
+ int getIntFromString(std::string input, int *output) {
+ int rc = 0;
+ int value;
+ const auto res = std::from_chars(input.data(), input.data() + input.size(), value);
+ if (res.ec == std::errc::invalid_argument) {
+ std::cerr << "Invalid int argument: " << input << std::endl;
+ rc = (int)std::errc::invalid_argument;
+ } else if (res.ec == std::errc::result_out_of_range) {
+ std::cerr << "Result out of range: " << input << std::endl;
+ rc = (int)std::errc::result_out_of_range;
+ }
+ *output = value;
+ return rc;
+ }
+
+ float getFloatFromString(std::string_view input, float *output) {
+ int rc = 0;
+ errno = 0;
+ // from_chars doesn't support conversion to float so we need to first
+ // convert the string_view to string and use the C-string for strtof
+ float value = strtof(std::string(input).c_str(), NULL);
+
+ if (input == "0.0" || input == "0") {
+ return rc;
+ }
+
+ if (value <= 0.0) {
+ std::cerr << "Invalid float argument: " << input << std::endl;
+ rc = EINVAL;
+ } else if (errno == ERANGE) {
+ std::cerr << "Result out of range: " << input << std::endl;
+ rc = errno;
+ } else {
+ *output = value;
+ }
+ return rc;
+ }
+
+ Status doArgs(Args &args) override {
+ while (args.get<std::string>().value_or("").find("-") == 0) {
+ auto opt = *args.pop<std::string>();
+ if (opt == "--") {
+ break;
+ } else if (opt == "-b") {
+ mBlocking = true;
+ } else {
+ std::cerr << "Invalid Option '" << opt << "'!" << std::endl;
+ return USAGE;
+ }
+ }
+ if (args.empty()) {
+ std::cerr << "Missing arguments! Please see usage" << std::endl;
+ return USAGE;
+ }
+ while (!args.empty()) {
+ PrimitivePwle pwle;
+ auto nextArg = args.pop();
+
+ if (*nextArg == "a") {
+ auto startAmplitude = args.pop();
+ float startAmp;
+ if (getFloatFromString(*startAmplitude, &startAmp))
+ return USAGE;
+
+ auto startFrequency = args.pop();
+ float startFreq;
+ if (getFloatFromString(*startFrequency, &startFreq))
+ return USAGE;
+
+ auto endAmplitude = args.pop();
+ float endAmp;
+ if (getFloatFromString(*endAmplitude, &endAmp))
+ return USAGE;
+
+ auto endFrequency = args.pop();
+ float endFreq;
+ if (getFloatFromString(*endFrequency, &endFreq))
+ return USAGE;
+
+ auto duration = args.pop();
+ int dur;
+ if (getIntFromString(*duration, &dur))
+ return USAGE;
+
+ ActivePwle active = {startAmp, startFreq, endAmp, endFreq, dur};
+ pwle = active;
+ } else if (*nextArg == "b") {
+ auto brakingMethod = args.pop();
+ Braking brakingMeth;
+ if (getIntFromString(*brakingMethod, (int *)&brakingMeth))
+ return USAGE;
+
+ auto duration = args.pop();
+ int dur;
+ if (getIntFromString(*duration, &dur))
+ return USAGE;
+
+ BrakingPwle braking = {brakingMeth, dur};
+ pwle = braking;
+ } else {
+ std::cerr << "Invalid arguments! Please see usage" << std::endl;
+ return USAGE;
+ }
+ mCompositePwle.emplace_back(std::move(pwle));
+ }
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ auto hal = getHal<aidl::IVibrator>();
+
+ if (!hal) {
+ return UNAVAILABLE;
+ }
+
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+
+ std::shared_ptr<VibratorCallback> callback;
+
+ if (mBlocking) {
+ callback = ndk::SharedRefBase::make<VibratorCallback>();
+ }
+
+ auto status = hal->call(&aidl::IVibrator::composePwle, mCompositePwle, callback);
+
+ if (status.isOk() && callback) {
+ callback->waitForComplete();
+ }
+
+ std::cout << "Status: " << status.getDescription() << std::endl;
+
+ return status.isOk() ? OK : ERROR;
+ }
+
+ bool mBlocking;
+ std::vector<PrimitivePwle> mCompositePwle;
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandComposePwle>("composePwle");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetBandwidthAmplitudeMap.cpp b/cmds/idlcli/vibrator/CommandGetBandwidthAmplitudeMap.cpp
new file mode 100644
index 0000000..aa01a11
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetBandwidthAmplitudeMap.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2021 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandGetBandwidthAmplitudeMap : public Command {
+ std::string getDescription() const override {
+ return "Retrieves vibrator bandwidth amplitude map.";
+ }
+
+ std::string getUsageSummary() const override { return ""; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{};
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ std::vector<float> bandwidthAmplitude;
+ float frequencyMinimumHz;
+ float frequencyResolutionHz;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status =
+ hal->call(&aidl::IVibrator::getBandwidthAmplitudeMap, &bandwidthAmplitude);
+ statusStr = status.getDescription();
+ ret = (status.isOk() ? OK : ERROR);
+
+ status = hal->call(&aidl::IVibrator::getFrequencyMinimum, &frequencyMinimumHz);
+ ret = (status.isOk() ? OK : ERROR);
+
+ status =
+ hal->call(&aidl::IVibrator::getFrequencyResolution, &frequencyResolutionHz);
+ ret = (status.isOk() ? OK : ERROR);
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Bandwidth Amplitude Map: " << std::endl;
+ float frequency = frequencyMinimumHz;
+ for (auto &e : bandwidthAmplitude) {
+ std::cout << frequency << ":" << e << std::endl;
+ frequency += frequencyResolutionHz;
+ }
+
+ return ret;
+ }
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandGetBandwidthAmplitudeMap>(
+ "getBandwidthAmplitudeMap");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetFrequencyMinimum.cpp b/cmds/idlcli/vibrator/CommandGetFrequencyMinimum.cpp
new file mode 100644
index 0000000..504c648
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetFrequencyMinimum.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2021 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandGetFrequencyMinimum : public Command {
+ std::string getDescription() const override {
+ return "Retrieves vibrator minimum frequency in Hz.";
+ }
+
+ std::string getUsageSummary() const override { return ""; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{};
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ float frequencyMinimumHz;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::getFrequencyMinimum, &frequencyMinimumHz);
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Minimum Frequency: " << frequencyMinimumHz << " Hz" << std::endl;
+
+ return ret;
+ }
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandGetFrequencyMinimum>("getFrequencyMinimum");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetFrequencyResolution.cpp b/cmds/idlcli/vibrator/CommandGetFrequencyResolution.cpp
new file mode 100644
index 0000000..de35838
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetFrequencyResolution.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandGetFrequencyResolution : public Command {
+ std::string getDescription() const override {
+ return "Retrieves vibrator frequency resolution in Hz.";
+ }
+
+ std::string getUsageSummary() const override { return ""; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{};
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ float frequencyResolutionHz;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status =
+ hal->call(&aidl::IVibrator::getFrequencyResolution, &frequencyResolutionHz);
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Frequency Resolution: " << frequencyResolutionHz << " Hz" << std::endl;
+
+ return ret;
+ }
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandGetFrequencyResolution>(
+ "getFrequencyResolution");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp b/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp
new file mode 100644
index 0000000..460d39e
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2020 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 <future>
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::CompositePrimitive;
+
+class CommandGetPrimitiveDuration : public Command {
+ std::string getDescription() const override {
+ return "Retrieve effect primitive's duration in milliseconds.";
+ }
+
+ std::string getUsageSummary() const override { return "<primitive>"; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{
+ {"<primitive>", {"Primitive ID."}},
+ };
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (auto primitive = args.pop<decltype(mPrimitive)>()) {
+ mPrimitive = *primitive;
+ std::cout << "Primitive: " << toString(mPrimitive) << std::endl;
+ } else {
+ std::cerr << "Missing or Invalid Primitive!" << std::endl;
+ return USAGE;
+ }
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ int32_t duration;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::getPrimitiveDuration, mPrimitive, &duration);
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Duration: " << duration << std::endl;
+
+ return ret;
+ }
+
+ CompositePrimitive mPrimitive;
+};
+
+static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandGetPrimitiveDuration>(
+ "getPrimitiveDuration");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetPwleCompositionSizeMax.cpp b/cmds/idlcli/vibrator/CommandGetPwleCompositionSizeMax.cpp
new file mode 100644
index 0000000..b2c3551
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetPwleCompositionSizeMax.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandGetPwleCompositionSizeMax : public Command {
+ std::string getDescription() const override {
+ return "Retrieves vibrator PWLE composition size max.";
+ }
+
+ std::string getUsageSummary() const override { return ""; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{};
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ int32_t maxSize;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::getPwleCompositionSizeMax, &maxSize);
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Max Size: " << maxSize << std::endl;
+
+ return ret;
+ }
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandGetPwleCompositionSizeMax>(
+ "getPwleCompositionSizeMax");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetPwlePrimitiveDurationMax.cpp b/cmds/idlcli/vibrator/CommandGetPwlePrimitiveDurationMax.cpp
new file mode 100644
index 0000000..9081973
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetPwlePrimitiveDurationMax.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandGetPwlePrimitiveDurationMax : public Command {
+ std::string getDescription() const override {
+ return "Retrieves vibrator PWLE primitive duration size max in milliseconds.";
+ }
+
+ std::string getUsageSummary() const override { return ""; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{};
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ int32_t maxDurationMs;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::getPwlePrimitiveDurationMax, &maxDurationMs);
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Primitive duration max: " << maxDurationMs << " ms" << std::endl;
+
+ return ret;
+ }
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandGetPwlePrimitiveDurationMax>(
+ "getPwlePrimitiveDurationMax");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetQFactor.cpp b/cmds/idlcli/vibrator/CommandGetQFactor.cpp
new file mode 100644
index 0000000..a2681e9
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetQFactor.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2021 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandGetQFactor : public Command {
+ std::string getDescription() const override { return "Retrieves vibrator Q factor."; }
+
+ std::string getUsageSummary() const override { return ""; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{};
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ float qFactor;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::getQFactor, &qFactor);
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Q Factor: " << qFactor << std::endl;
+
+ return ret;
+ }
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandGetQFactor>("getQFactor");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetResonantFrequency.cpp b/cmds/idlcli/vibrator/CommandGetResonantFrequency.cpp
new file mode 100644
index 0000000..81a6391
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetResonantFrequency.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2021 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandGetResonantFrequency : public Command {
+ std::string getDescription() const override {
+ return "Retrieves vibrator resonant frequency in Hz.";
+ }
+
+ std::string getUsageSummary() const override { return ""; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{};
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ float resonantFrequencyHz;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::getResonantFrequency, &resonantFrequencyHz);
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Resonant Frequency: " << resonantFrequencyHz << " Hz" << std::endl;
+
+ return ret;
+ }
+};
+
+static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandGetResonantFrequency>(
+ "getResonantFrequency");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp b/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp
new file mode 100644
index 0000000..edfcd91
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2020 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::Effect;
+
+class CommandGetSupportedAlwaysOnEffects : public Command {
+ std::string getDescription() const override { return "List of supported always-on effects."; }
+
+ std::string getUsageSummary() const override { return ""; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{};
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ std::vector<Effect> effects;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::getSupportedAlwaysOnEffects, &effects);
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Effects:" << std::endl;
+ for (auto &e : effects) {
+ std::cout << " " << toString(e) << std::endl;
+ }
+
+ return ret;
+ }
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandGetSupportedAlwaysOnEffects>(
+ "getSupportedAlwaysOnEffects");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetSupportedBraking.cpp b/cmds/idlcli/vibrator/CommandGetSupportedBraking.cpp
new file mode 100644
index 0000000..b326e07
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetSupportedBraking.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::Braking;
+
+class CommandGetSupportedBraking : public Command {
+ std::string getDescription() const override { return "List of supported braking mechanisms."; }
+
+ std::string getUsageSummary() const override { return ""; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{};
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ std::vector<Braking> braking;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::getSupportedBraking, &braking);
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Braking Mechanisms:" << std::endl;
+ for (auto &e : braking) {
+ std::cout << " " << toString(e) << std::endl;
+ }
+
+ return ret;
+ }
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandGetSupportedBraking>("getSupportedBraking");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp b/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp
new file mode 100644
index 0000000..7658f22
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::Effect;
+
+class CommandGetSupportedEffects : public Command {
+ std::string getDescription() const override { return "List supported effects."; }
+
+ std::string getUsageSummary() const override { return ""; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{};
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ std::vector<Effect> effects;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::getSupportedEffects, &effects);
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Effects:" << std::endl;
+ for (auto &e : effects) {
+ std::cout << " " << toString(e) << std::endl;
+ }
+
+ return ret;
+ }
+};
+
+static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandGetSupportedEffects>(
+ "getSupportedEffects");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp b/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp
new file mode 100644
index 0000000..d101681
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2020 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::CompositePrimitive;
+
+class CommandGetSupportedPrimitives : public Command {
+ std::string getDescription() const override { return "List of supported effect primitive."; }
+
+ std::string getUsageSummary() const override { return ""; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{};
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ std::vector<CompositePrimitive> primitives;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::getSupportedPrimitives, &primitives);
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Primitives:" << std::endl;
+ for (auto &e : primitives) {
+ std::cout << " " << toString(e) << std::endl;
+ }
+
+ return ret;
+ }
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandGetSupportedPrimitives>(
+ "getSupportedPrimitives");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandOn.cpp b/cmds/idlcli/vibrator/CommandOn.cpp
index 4e7e493..8212fc1 100644
--- a/cmds/idlcli/vibrator/CommandOn.cpp
+++ b/cmds/idlcli/vibrator/CommandOn.cpp
@@ -13,9 +13,14 @@
* limitations under the License.
*/
+#include <thread>
+
#include "utils.h"
#include "vibrator.h"
+using std::chrono::milliseconds;
+using std::this_thread::sleep_for;
+
namespace android {
namespace idlcli {
@@ -26,16 +31,28 @@
class CommandOn : public Command {
std::string getDescription() const override { return "Turn on vibrator."; }
- std::string getUsageSummary() const override { return "<duration>"; }
+ std::string getUsageSummary() const override { return "[options] <duration>"; }
UsageDetails getUsageDetails() const override {
UsageDetails details{
+ {"-b", {"Block for duration of vibration."}},
{"<duration>", {"In milliseconds."}},
};
return details;
}
Status doArgs(Args &args) override {
+ while (args.get<std::string>().value_or("").find("-") == 0) {
+ auto opt = *args.pop<std::string>();
+ if (opt == "--") {
+ break;
+ } else if (opt == "-b") {
+ mBlocking = true;
+ } else {
+ std::cerr << "Invalid Option '" << opt << "'!" << std::endl;
+ return USAGE;
+ }
+ }
if (auto duration = args.pop<decltype(mDuration)>()) {
mDuration = *duration;
} else {
@@ -52,9 +69,21 @@
Status doMain(Args && /*args*/) override {
std::string statusStr;
Status ret;
+ std::shared_ptr<VibratorCallback> callback;
if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::on, mDuration, nullptr);
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+
+ int32_t cap;
+ hal->call(&aidl::IVibrator::getCapabilities, &cap);
+
+ if (mBlocking && (cap & aidl::IVibrator::CAP_ON_CALLBACK)) {
+ callback = ndk::SharedRefBase::make<VibratorCallback>();
+ }
+
+ auto status = hal->call(&aidl::IVibrator::on, mDuration, callback);
+
statusStr = status.getDescription();
ret = status.isOk() ? OK : ERROR;
} else if (auto hal = getHal<V1_0::IVibrator>()) {
@@ -65,11 +94,20 @@
return UNAVAILABLE;
}
+ if (ret == OK && mBlocking) {
+ if (callback) {
+ callback->waitForComplete();
+ } else {
+ sleep_for(milliseconds(mDuration));
+ }
+ }
+
std::cout << "Status: " << statusStr << std::endl;
return ret;
}
+ bool mBlocking;
uint32_t mDuration;
};
diff --git a/cmds/idlcli/vibrator/CommandPerform.cpp b/cmds/idlcli/vibrator/CommandPerform.cpp
index 69c7e37..c897686 100644
--- a/cmds/idlcli/vibrator/CommandPerform.cpp
+++ b/cmds/idlcli/vibrator/CommandPerform.cpp
@@ -13,9 +13,14 @@
* limitations under the License.
*/
+#include <thread>
+
#include "utils.h"
#include "vibrator.h"
+using std::chrono::milliseconds;
+using std::this_thread::sleep_for;
+
namespace android {
namespace idlcli {
@@ -51,16 +56,17 @@
static_assert(static_cast<uint8_t>(V1_3::Effect::TEXTURE_TICK) ==
static_cast<uint8_t>(aidl::Effect::TEXTURE_TICK));
-using V1_0::EffectStrength;
-using V1_3::Effect;
+using aidl::Effect;
+using aidl::EffectStrength;
class CommandPerform : public Command {
std::string getDescription() const override { return "Perform vibration effect."; }
- std::string getUsageSummary() const override { return "<effect> <strength>"; }
+ std::string getUsageSummary() const override { return "[options] <effect> <strength>"; }
UsageDetails getUsageDetails() const override {
UsageDetails details{
+ {"-b", {"Block for duration of vibration."}},
{"<effect>", {"Effect ID."}},
{"<strength>", {"0-2."}},
};
@@ -68,6 +74,17 @@
}
Status doArgs(Args &args) override {
+ while (args.get<std::string>().value_or("").find("-") == 0) {
+ auto opt = *args.pop<std::string>();
+ if (opt == "--") {
+ break;
+ } else if (opt == "-b") {
+ mBlocking = true;
+ } else {
+ std::cerr << "Invalid Option '" << opt << "'!" << std::endl;
+ return USAGE;
+ }
+ }
if (auto effect = args.pop<decltype(mEffect)>()) {
mEffect = *effect;
std::cout << "Effect: " << toString(mEffect) << std::endl;
@@ -93,12 +110,23 @@
std::string statusStr;
uint32_t lengthMs;
Status ret;
+ std::shared_ptr<VibratorCallback> callback;
if (auto hal = getHal<aidl::IVibrator>()) {
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+
+ int32_t cap;
+ hal->call(&aidl::IVibrator::getCapabilities, &cap);
+
+ if (mBlocking && (cap & aidl::IVibrator::CAP_PERFORM_CALLBACK)) {
+ callback = ndk::SharedRefBase::make<VibratorCallback>();
+ }
+
int32_t aidlLengthMs;
- auto status =
- hal->call(&aidl::IVibrator::perform, static_cast<aidl::Effect>(mEffect),
- static_cast<aidl::EffectStrength>(mStrength), nullptr, &aidlLengthMs);
+ auto status = hal->call(&aidl::IVibrator::perform, mEffect, mStrength, callback,
+ &aidlLengthMs);
+
statusStr = status.getDescription();
lengthMs = static_cast<uint32_t>(aidlLengthMs);
ret = status.isOk() ? OK : ERROR;
@@ -111,17 +139,20 @@
};
if (auto hal = getHal<V1_3::IVibrator>()) {
- hidlRet = hal->call(&V1_3::IVibrator::perform_1_3,
- static_cast<V1_3::Effect>(mEffect), mStrength, callback);
+ hidlRet =
+ hal->call(&V1_3::IVibrator::perform_1_3, static_cast<V1_3::Effect>(mEffect),
+ static_cast<V1_0::EffectStrength>(mStrength), callback);
} else if (auto hal = getHal<V1_2::IVibrator>()) {
- hidlRet = hal->call(&V1_2::IVibrator::perform_1_2,
- static_cast<V1_2::Effect>(mEffect), mStrength, callback);
+ hidlRet =
+ hal->call(&V1_2::IVibrator::perform_1_2, static_cast<V1_2::Effect>(mEffect),
+ static_cast<V1_0::EffectStrength>(mStrength), callback);
} else if (auto hal = getHal<V1_1::IVibrator>()) {
hidlRet = hal->call(&V1_1::IVibrator::perform_1_1,
- static_cast<V1_1::Effect_1_1>(mEffect), mStrength, callback);
+ static_cast<V1_1::Effect_1_1>(mEffect),
+ static_cast<V1_0::EffectStrength>(mStrength), callback);
} else if (auto hal = getHal<V1_0::IVibrator>()) {
hidlRet = hal->call(&V1_0::IVibrator::perform, static_cast<V1_0::Effect>(mEffect),
- mStrength, callback);
+ static_cast<V1_0::EffectStrength>(mStrength), callback);
} else {
return UNAVAILABLE;
}
@@ -130,12 +161,21 @@
ret = hidlRet.isOk() && status == V1_0::Status::OK ? OK : ERROR;
}
+ if (ret == OK && mBlocking) {
+ if (callback) {
+ callback->waitForComplete();
+ } else {
+ sleep_for(milliseconds(lengthMs));
+ }
+ }
+
std::cout << "Status: " << statusStr << std::endl;
std::cout << "Length: " << lengthMs << std::endl;
return ret;
}
+ bool mBlocking;
Effect mEffect;
EffectStrength mStrength;
};
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index d09d3e8..a546236 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -197,8 +197,7 @@
filegroup {
name: "installd_aidl",
srcs: [
- "binder/android/os/IInstalld.aidl",
- "binder/android/os/storage/CrateMetadata.aidl",
+ "binder/**/*.aidl",
],
path: "binder",
}
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index c55fc6a..818804a 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -424,33 +424,6 @@
return true;
}
-binder::Status InstalldNativeService::createAppDataBatched(
- const std::optional<std::vector<std::optional<std::string>>>& uuids,
- const std::optional<std::vector<std::optional<std::string>>>& packageNames,
- int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
- const std::vector<std::string>& seInfos, const std::vector<int32_t>& targetSdkVersions,
- int64_t* _aidl_return) {
- ENFORCE_UID(AID_SYSTEM);
- std::lock_guard<std::recursive_mutex> lock(mLock);
-
- ATRACE_BEGIN("createAppDataBatched");
- binder::Status ret;
- for (size_t i = 0; i < uuids->size(); i++) {
- std::optional<std::string> packageName = packageNames->at(i);
- if (!packageName) {
- continue;
- }
- ret = createAppData(uuids->at(i), *packageName, userId, flags, appIds[i],
- seInfos[i], targetSdkVersions[i], _aidl_return);
- if (!ret.isOk()) {
- ATRACE_END();
- return ret;
- }
- }
- ATRACE_END();
- return ok();
-}
-
binder::Status InstalldNativeService::createAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return) {
@@ -532,6 +505,38 @@
return ok();
}
+
+binder::Status InstalldNativeService::createAppData(
+ const android::os::CreateAppDataArgs& args,
+ android::os::CreateAppDataResult* _aidl_return) {
+ ENFORCE_UID(AID_SYSTEM);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+
+ int64_t ceDataInode = -1;
+ auto status = createAppData(args.uuid, args.packageName, args.userId, args.flags, args.appId,
+ args.seInfo, args.targetSdkVersion, &ceDataInode);
+ _aidl_return->ceDataInode = ceDataInode;
+ _aidl_return->exceptionCode = status.exceptionCode();
+ _aidl_return->exceptionMessage = status.exceptionMessage();
+ return ok();
+}
+
+binder::Status InstalldNativeService::createAppDataBatched(
+ const std::vector<android::os::CreateAppDataArgs>& args,
+ std::vector<android::os::CreateAppDataResult>* _aidl_return) {
+ ENFORCE_UID(AID_SYSTEM);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+
+ std::vector<android::os::CreateAppDataResult> results;
+ for (auto arg : args) {
+ android::os::CreateAppDataResult result;
+ createAppData(arg, &result);
+ results.push_back(result);
+ }
+ *_aidl_return = results;
+ return ok();
+}
+
binder::Status InstalldNativeService::migrateAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags) {
ENFORCE_UID(AID_SYSTEM);
@@ -2202,9 +2207,6 @@
auto obbPath = StringPrintf("%s/Android/obb",
create_data_media_path(uuid_, userId).c_str());
calculate_tree_size(obbPath, &obbSize);
- if (!(flags & FLAG_USE_QUOTA)) {
- totalSize -= obbSize;
- }
ATRACE_END();
}
@@ -2256,7 +2258,7 @@
#if CRATE_DEBUG
LOG(WARNING) << "retVector.size() =" << retVector.size();
for (auto& item : retVector) {
- CrateManager::dump(item);
+ CrateManager::dump(*item);
}
#endif
@@ -2288,7 +2290,7 @@
if (cratedFolder == nullptr) {
return;
}
- retVector->push_back(std::move(crateMetadata));
+ retVector.push_back(std::move(crateMetadata));
};
std::function<void(FTSENT*)> onHandingPackage = [&](FTSENT* packageDir) -> void {
@@ -2300,7 +2302,7 @@
#if CRATE_DEBUG
LOG(DEBUG) << "retVector.size() =" << retVector.size();
for (auto& item : retVector) {
- CrateManager::dump(item);
+ CrateManager::dump(*item);
}
#endif
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 9819327..4966b96 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -44,15 +44,18 @@
int32_t userSerial, int32_t flags);
binder::Status destroyUserData(const std::optional<std::string>& uuid, int32_t userId,
int32_t flags);
- binder::Status createAppDataBatched(
- const std::optional<std::vector<std::optional<std::string>>>& uuids,
- const std::optional<std::vector<std::optional<std::string>>>& packageNames,
- int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
- const std::vector<std::string>& seInfos, const std::vector<int32_t>& targetSdkVersions,
- int64_t* _aidl_return);
+
binder::Status createAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return);
+
+ binder::Status createAppData(
+ const android::os::CreateAppDataArgs& args,
+ android::os::CreateAppDataResult* _aidl_return);
+ binder::Status createAppDataBatched(
+ const std::vector<android::os::CreateAppDataArgs>& args,
+ std::vector<android::os::CreateAppDataResult>* _aidl_return);
+
binder::Status restoreconAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
const std::string& seInfo);
diff --git a/libs/ui/UiConfig.cpp b/cmds/installd/binder/android/os/CreateAppDataArgs.aidl
similarity index 65%
copy from libs/ui/UiConfig.cpp
copy to cmds/installd/binder/android/os/CreateAppDataArgs.aidl
index 0ac863d..96d7faa 100644
--- a/libs/ui/UiConfig.cpp
+++ b/cmds/installd/binder/android/os/CreateAppDataArgs.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-#include <ui/UiConfig.h>
+package android.os;
-namespace android {
-
-void appendUiConfigString(std::string& configStr) {
- static const char* config =
- " [libui]";
- configStr.append(config);
+/** {@hide} */
+parcelable CreateAppDataArgs {
+ @nullable @utf8InCpp String uuid;
+ @utf8InCpp String packageName;
+ int userId;
+ int flags;
+ int appId;
+ @utf8InCpp String seInfo;
+ int targetSdkVersion;
}
-
-
-}; // namespace android
diff --git a/libs/ui/UiConfig.cpp b/cmds/installd/binder/android/os/CreateAppDataResult.aidl
similarity index 67%
copy from libs/ui/UiConfig.cpp
copy to cmds/installd/binder/android/os/CreateAppDataResult.aidl
index 0ac863d..3b8fa6b 100644
--- a/libs/ui/UiConfig.cpp
+++ b/cmds/installd/binder/android/os/CreateAppDataResult.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,15 +14,11 @@
* limitations under the License.
*/
-#include <ui/UiConfig.h>
+package android.os;
-namespace android {
-
-void appendUiConfigString(std::string& configStr) {
- static const char* config =
- " [libui]";
- configStr.append(config);
+/** {@hide} */
+parcelable CreateAppDataResult {
+ long ceDataInode;
+ int exceptionCode;
+ @utf8InCpp String exceptionMessage;
}
-
-
-}; // namespace android
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 4ac70a4..2538e22 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -21,11 +21,9 @@
void createUserData(@nullable @utf8InCpp String uuid, int userId, int userSerial, int flags);
void destroyUserData(@nullable @utf8InCpp String uuid, int userId, int flags);
- long createAppData(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName,
- int userId, int flags, int appId, in @utf8InCpp String seInfo, int targetSdkVersion);
- long createAppDataBatched(in @nullable @utf8InCpp String[] uuids,
- in @nullable @utf8InCpp String[] packageNames, in int userId, int flags, in int[] appIds,
- in @utf8InCpp String[] seInfos, in int[] targetSdkVersions);
+ android.os.CreateAppDataResult createAppData(in android.os.CreateAppDataArgs args);
+ android.os.CreateAppDataResult[] createAppDataBatched(in android.os.CreateAppDataArgs[] args);
+
void restoreconAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
int userId, int flags, int appId, @utf8InCpp String seInfo);
void migrateAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
@@ -117,10 +115,11 @@
int userId, int snapshotId, int storageFlags);
void restoreAppDataSnapshot(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName,
int appId, @utf8InCpp String seInfo, int user, int snapshotId, int storageflags);
- void destroyCeSnapshotsNotSpecified(@nullable @utf8InCpp String uuid, int userId,
- in int[] retainSnapshotIds);
void destroyAppDataSnapshot(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
int userId, long ceSnapshotInode, int snapshotId, int storageFlags);
+ void destroyCeSnapshotsNotSpecified(@nullable @utf8InCpp String uuid, int userId,
+ in int[] retainSnapshotIds);
+
void tryMountDataMirror(@nullable @utf8InCpp String volumeUuid);
void onPrivateVolumeRemoved(@nullable @utf8InCpp String volumeUuid);
diff --git a/cmds/surfacereplayer/proto/Android.bp b/cmds/surfacereplayer/proto/Android.bp
index dae976e..23b54ee 100644
--- a/cmds/surfacereplayer/proto/Android.bp
+++ b/cmds/surfacereplayer/proto/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
index b574098..3798ba7 100644
--- a/cmds/surfacereplayer/proto/src/trace.proto
+++ b/cmds/surfacereplayer/proto/src/trace.proto
@@ -25,8 +25,10 @@
repeated SurfaceChange surface_change = 1;
repeated DisplayChange display_change = 2;
- required bool synchronous = 3;
- required bool animation = 4;
+ required bool synchronous = 3;
+ required bool animation = 4;
+ optional Origin origin = 5;
+ optional uint64 id = 6;
}
message SurfaceChange {
@@ -39,20 +41,17 @@
LayerChange layer = 5;
CropChange crop = 6;
MatrixChange matrix = 8;
- OverrideScalingModeChange override_scaling_mode = 9;
TransparentRegionHintChange transparent_region_hint = 10;
LayerStackChange layer_stack = 11;
HiddenFlagChange hidden_flag = 12;
OpaqueFlagChange opaque_flag = 13;
SecureFlagChange secure_flag = 14;
- DeferredTransactionChange deferred_transaction = 15;
CornerRadiusChange corner_radius = 16;
ReparentChange reparent = 17;
RelativeParentChange relative_parent = 18;
- DetachChildrenChange detach_children = 19;
- ReparentChildrenChange reparent_children = 20;
- BackgroundBlurRadiusChange background_blur_radius = 21;
- ShadowRadiusChange shadow_radius = 22;
+ BackgroundBlurRadiusChange background_blur_radius = 20;
+ ShadowRadiusChange shadow_radius = 21;
+ BlurRegionsChange blur_regions = 22;
}
}
@@ -93,10 +92,6 @@
required float dtdy = 4;
}
-message OverrideScalingModeChange {
- required int32 override_scaling_mode = 1;
-}
-
message TransparentRegionHintChange {
repeated Rectangle region = 1;
}
@@ -117,11 +112,6 @@
required bool secure_flag = 1;
}
-message DeferredTransactionChange {
- required int32 layer_id = 1;
- required uint64 frame_number = 2;
-}
-
message DisplayChange {
required int32 id = 1;
@@ -193,19 +183,33 @@
required int32 parent_id = 1;
}
-message ReparentChildrenChange {
- required int32 parent_id = 1;
-}
-
message RelativeParentChange {
required int32 relative_parent_id = 1;
required int32 z = 2;
}
-message DetachChildrenChange {
- required bool detach_children = 1;
-}
-
message ShadowRadiusChange {
required float radius = 1;
+}
+
+message BlurRegionsChange {
+ repeated BlurRegionChange blur_regions = 1;
+}
+
+message BlurRegionChange {
+ required uint32 blur_radius = 1;
+ required float corner_radius_tl = 2;
+ required float corner_radius_tr = 3;
+ required float corner_radius_bl = 4;
+ required float corner_radius_br = 5;
+ required float alpha = 6;
+ required int32 left = 7;
+ required int32 top = 8;
+ required int32 right = 9;
+ required int32 bottom = 10;
+}
+
+message Origin {
+ required int32 pid = 1;
+ required int32 uid = 2;
}
\ No newline at end of file
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index 2b5667d..cfd42fe 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -28,7 +28,6 @@
#include <gui/Surface.h>
#include <private/gui/ComposerService.h>
-#include <ui/DisplayInfo.h>
#include <utils/Log.h>
#include <utils/String8.h>
#include <utils/Trace.h>
@@ -387,10 +386,6 @@
case SurfaceChange::SurfaceChangeCase::kMatrix:
setMatrix(transaction, change.id(), change.matrix());
break;
- case SurfaceChange::SurfaceChangeCase::kOverrideScalingMode:
- setOverrideScalingMode(transaction, change.id(),
- change.override_scaling_mode());
- break;
case SurfaceChange::SurfaceChangeCase::kTransparentRegionHint:
setTransparentRegionHint(transaction, change.id(),
change.transparent_region_hint());
@@ -407,26 +402,18 @@
case SurfaceChange::SurfaceChangeCase::kSecureFlag:
setSecureFlag(transaction, change.id(), change.secure_flag());
break;
- case SurfaceChange::SurfaceChangeCase::kDeferredTransaction:
- waitUntilDeferredTransactionLayerExists(change.deferred_transaction(), lock);
- setDeferredTransaction(transaction, change.id(),
- change.deferred_transaction());
- break;
case SurfaceChange::SurfaceChangeCase::kReparent:
setReparentChange(transaction, change.id(), change.reparent());
break;
- case SurfaceChange::SurfaceChangeCase::kReparentChildren:
- setReparentChildrenChange(transaction, change.id(), change.reparent_children());
- break;
case SurfaceChange::SurfaceChangeCase::kRelativeParent:
setRelativeParentChange(transaction, change.id(), change.relative_parent());
break;
- case SurfaceChange::SurfaceChangeCase::kDetachChildren:
- setDetachChildrenChange(transaction, change.id(), change.detach_children());
- break;
case SurfaceChange::SurfaceChangeCase::kShadowRadius:
setShadowRadiusChange(transaction, change.id(), change.shadow_radius());
break;
+ case SurfaceChange::SurfaceChangeCase::kBlurRegions:
+ setBlurRegionsChange(transaction, change.id(), change.blur_regions());
+ break;
default:
status = 1;
break;
@@ -500,7 +487,7 @@
Rect r = Rect(cc.rectangle().left(), cc.rectangle().top(), cc.rectangle().right(),
cc.rectangle().bottom());
- t.setCrop_legacy(mLayers[id], r);
+ t.setCrop(mLayers[id], r);
}
void Replayer::setCornerRadius(SurfaceComposerClient::Transaction& t,
@@ -525,12 +512,6 @@
t.setMatrix(mLayers[id], mc.dsdx(), mc.dtdx(), mc.dsdy(), mc.dtdy());
}
-void Replayer::setOverrideScalingMode(SurfaceComposerClient::Transaction& t,
- layer_id id, const OverrideScalingModeChange& osmc) {
- ALOGV("Layer %d: Setting Override Scaling Mode -- mode=%d", id, osmc.override_scaling_mode());
- t.setOverrideScalingMode(mLayers[id], osmc.override_scaling_mode());
-}
-
void Replayer::setTransparentRegionHint(SurfaceComposerClient::Transaction& t,
layer_id id, const TransparentRegionHintChange& trhc) {
ALOGV("Setting Transparent Region Hint");
@@ -574,21 +555,6 @@
t.setFlags(mLayers[id], flag, layer_state_t::eLayerSecure);
}
-void Replayer::setDeferredTransaction(SurfaceComposerClient::Transaction& t,
- layer_id id, const DeferredTransactionChange& dtc) {
- ALOGV("Layer %d: Setting Deferred Transaction -- layer_id=%d, "
- "frame_number=%llu",
- id, dtc.layer_id(), dtc.frame_number());
- if (mLayers.count(dtc.layer_id()) == 0 || mLayers[dtc.layer_id()] == nullptr) {
- ALOGE("Layer %d not found in Deferred Transaction", dtc.layer_id());
- return;
- }
-
- auto handle = mLayers[dtc.layer_id()]->getHandle();
-
- t.deferTransactionUntil_legacy(mLayers[id], handle, dtc.frame_number());
-}
-
void Replayer::setDisplaySurface(SurfaceComposerClient::Transaction& t,
display_id id, const DispSurfaceChange& /*dsc*/) {
sp<IGraphicBufferProducer> outProducer;
@@ -692,13 +658,6 @@
std::this_thread::sleep_for(std::chrono::nanoseconds(timestamp - mCurrentTime));
}
-void Replayer::waitUntilDeferredTransactionLayerExists(
- const DeferredTransactionChange& dtc, std::unique_lock<std::mutex>& lock) {
- if (mLayers.count(dtc.layer_id()) == 0 || mLayers[dtc.layer_id()] == nullptr) {
- mLayerCond.wait(lock, [&] { return (mLayers[dtc.layer_id()] != nullptr); });
- }
-}
-
status_t Replayer::loadSurfaceComposerClient() {
mComposerClient = new SurfaceComposerClient;
return mComposerClient->initCheck();
@@ -706,11 +665,11 @@
void Replayer::setReparentChange(SurfaceComposerClient::Transaction& t,
layer_id id, const ReparentChange& c) {
- sp<IBinder> newParentHandle = nullptr;
+ sp<SurfaceControl> newSurfaceControl = nullptr;
if (mLayers.count(c.parent_id()) != 0 && mLayers[c.parent_id()] != nullptr) {
- newParentHandle = mLayers[c.parent_id()]->getHandle();
+ newSurfaceControl = mLayers[c.parent_id()];
}
- t.reparent(mLayers[id], newParentHandle);
+ t.reparent(mLayers[id], newSurfaceControl);
}
void Replayer::setRelativeParentChange(SurfaceComposerClient::Transaction& t,
@@ -719,24 +678,31 @@
ALOGE("Layer %d not found in set relative parent transaction", c.relative_parent_id());
return;
}
- t.setRelativeLayer(mLayers[id], mLayers[c.relative_parent_id()]->getHandle(), c.z());
-}
-
-void Replayer::setDetachChildrenChange(SurfaceComposerClient::Transaction& t,
- layer_id id, const DetachChildrenChange& c) {
- t.detachChildren(mLayers[id]);
-}
-
-void Replayer::setReparentChildrenChange(SurfaceComposerClient::Transaction& t,
- layer_id id, const ReparentChildrenChange& c) {
- if (mLayers.count(c.parent_id()) == 0 || mLayers[c.parent_id()] == nullptr) {
- ALOGE("Layer %d not found in reparent children transaction", c.parent_id());
- return;
- }
- t.reparentChildren(mLayers[id], mLayers[c.parent_id()]->getHandle());
+ t.setRelativeLayer(mLayers[id], mLayers[c.relative_parent_id()], c.z());
}
void Replayer::setShadowRadiusChange(SurfaceComposerClient::Transaction& t,
layer_id id, const ShadowRadiusChange& c) {
t.setShadowRadius(mLayers[id], c.radius());
}
+
+void Replayer::setBlurRegionsChange(SurfaceComposerClient::Transaction& t,
+ layer_id id, const BlurRegionsChange& c) {
+ std::vector<BlurRegion> regions;
+ for(size_t i=0; i < c.blur_regions_size(); i++) {
+ auto protoRegion = c.blur_regions(i);
+ regions.push_back(BlurRegion{
+ .blurRadius = protoRegion.blur_radius(),
+ .alpha = protoRegion.alpha(),
+ .cornerRadiusTL = protoRegion.corner_radius_tl(),
+ .cornerRadiusTR = protoRegion.corner_radius_tr(),
+ .cornerRadiusBL = protoRegion.corner_radius_bl(),
+ .cornerRadiusBR = protoRegion.corner_radius_br(),
+ .left = protoRegion.left(),
+ .top = protoRegion.top(),
+ .right = protoRegion.right(),
+ .bottom = protoRegion.bottom()
+ });
+ }
+ t.setBlurRegions(mLayers[id], regions);
+}
diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h
index 95857e1..d62522a 100644
--- a/cmds/surfacereplayer/replayer/Replayer.h
+++ b/cmds/surfacereplayer/replayer/Replayer.h
@@ -96,10 +96,10 @@
layer_id id, const CornerRadiusChange& cc);
void setBackgroundBlurRadius(SurfaceComposerClient::Transaction& t,
layer_id id, const BackgroundBlurRadiusChange& cc);
+ void setBlurRegions(SurfaceComposerClient::Transaction& t,
+ layer_id id, const BlurRegionsChange& cc);
void setMatrix(SurfaceComposerClient::Transaction& t,
layer_id id, const MatrixChange& mc);
- void setOverrideScalingMode(SurfaceComposerClient::Transaction& t,
- layer_id id, const OverrideScalingModeChange& osmc);
void setTransparentRegionHint(SurfaceComposerClient::Transaction& t,
layer_id id, const TransparentRegionHintChange& trgc);
void setLayerStack(SurfaceComposerClient::Transaction& t,
@@ -110,18 +110,14 @@
layer_id id, const OpaqueFlagChange& ofc);
void setSecureFlag(SurfaceComposerClient::Transaction& t,
layer_id id, const SecureFlagChange& sfc);
- void setDeferredTransaction(SurfaceComposerClient::Transaction& t,
- layer_id id, const DeferredTransactionChange& dtc);
void setReparentChange(SurfaceComposerClient::Transaction& t,
layer_id id, const ReparentChange& c);
void setRelativeParentChange(SurfaceComposerClient::Transaction& t,
layer_id id, const RelativeParentChange& c);
- void setDetachChildrenChange(SurfaceComposerClient::Transaction& t,
- layer_id id, const DetachChildrenChange& c);
- void setReparentChildrenChange(SurfaceComposerClient::Transaction& t,
- layer_id id, const ReparentChildrenChange& c);
void setShadowRadiusChange(SurfaceComposerClient::Transaction& t,
layer_id id, const ShadowRadiusChange& c);
+ void setBlurRegionsChange(SurfaceComposerClient::Transaction& t,
+ layer_id id, const BlurRegionsChange& c);
void setDisplaySurface(SurfaceComposerClient::Transaction& t,
display_id id, const DispSurfaceChange& dsc);
@@ -133,8 +129,6 @@
display_id id, const ProjectionChange& pc);
void waitUntilTimestamp(int64_t timestamp);
- void waitUntilDeferredTransactionLayerExists(
- const DeferredTransactionChange& dtc, std::unique_lock<std::mutex>& lock);
status_t loadSurfaceComposerClient();
Trace mTrace;
diff --git a/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py b/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py
index d63d97f..58bfbf3 100644
--- a/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py
+++ b/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py
@@ -69,7 +69,6 @@
print ("5. Crop Change")
print ("6. Final Crop Change")
print ("7. Matrix Change")
- print ("8. Override Scaling Mode Change")
print ("9. Transparent Region Hint Change")
print ("10. Layer Stack Change")
print ("11. Hidden Flag Change")
@@ -128,9 +127,6 @@
change.matrix.dtdx,\
change.matrix.dsdy,\
change.matrix.dtdy = layer()
- elif option == 8:
- change.override_scaling_mode.override_scaling_mode \
- = override_scaling_mode()
elif option == 9:
for rect in transparent_region_hint():
new = increment.transparent_region_hint.region.add()
@@ -227,11 +223,6 @@
return float(dsdx)
-def override_scaling_mode():
- mode = input("Enter override scaling mode: ")
-
- return int(mode)
-
def transparent_region_hint():
num = input("Enter number of rectangles in region: ")
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
new file mode 100644
index 0000000..9d88ca6
--- /dev/null
+++ b/data/etc/Android.bp
@@ -0,0 +1,16 @@
+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"],
+}
+
+prebuilt_etc {
+ name: "android.hardware.biometrics.face.xml",
+ product_specific: true,
+ sub_dir: "permissions",
+ src: "android.hardware.biometrics.face.xml",
+ filename_from_src: true,
+}
diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml
index 6ffb947..adfd6e2 100644
--- a/data/etc/car_core_hardware.xml
+++ b/data/etc/car_core_hardware.xml
@@ -34,6 +34,8 @@
<feature name="android.hardware.microphone" />
<!-- Feature to specify if the device is a car -->
<feature name="android.hardware.type.automotive" />
+ <!-- Indicate support for the Android security model per the CDD. -->
+ <feature name="android.hardware.security.model.compatible" />
<!-- basic system services -->
<feature name="android.software.connectionservice" />
@@ -43,6 +45,13 @@
<feature name="android.software.autofill" />
<feature name="android.software.cant_save_state" />
<feature name="android.software.secure_lock_screen" />
+ <feature name="android.software.input_methods" />
+
+
+ <!-- Feature to support device admins -->
+ <!-- TODO(b/178412797): not fully supported yet, CTS tests are still
+ failing. -->
+ <feature name="android.software.device_admin" />
<!-- devices with GPS must include android.hardware.location.gps.xml -->
<!-- devices with an autofocus camera and/or flash must include either
diff --git a/data/etc/go_handheld_core_hardware.xml b/data/etc/go_handheld_core_hardware.xml
index 915e579..e6db4ad 100644
--- a/data/etc/go_handheld_core_hardware.xml
+++ b/data/etc/go_handheld_core_hardware.xml
@@ -33,6 +33,8 @@
<feature name="android.hardware.microphone" />
<feature name="android.hardware.screen.portrait" />
<feature name="android.hardware.screen.landscape" />
+ <!-- Indicate support for the Android security model per the CDD. -->
+ <feature name="android.hardware.security.model.compatible" />
<!-- basic system services -->
<feature name="android.software.connectionservice" />
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index dc6963f..41f1514 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -37,6 +37,8 @@
<feature name="android.hardware.microphone" />
<feature name="android.hardware.screen.portrait" />
<feature name="android.hardware.screen.landscape" />
+ <!-- Indicate support for the Android security model per the CDD. -->
+ <feature name="android.hardware.security.model.compatible" />
<!-- basic system services -->
<feature name="android.software.app_widgets" />
diff --git a/data/etc/pc_core_hardware.xml b/data/etc/pc_core_hardware.xml
index c62da0a..b490ba0 100644
--- a/data/etc/pc_core_hardware.xml
+++ b/data/etc/pc_core_hardware.xml
@@ -33,6 +33,8 @@
<feature name="android.hardware.screen.landscape" />
<feature name="android.hardware.location" />
<feature name="android.hardware.location.network" />
+ <!-- Indicate support for the Android security model per the CDD. -->
+ <feature name="android.hardware.security.model.compatible" />
<!-- basic system services -->
<feature name="android.software.app_widgets" />
diff --git a/data/etc/tablet_core_hardware.xml b/data/etc/tablet_core_hardware.xml
index e878f86..8c369de 100644
--- a/data/etc/tablet_core_hardware.xml
+++ b/data/etc/tablet_core_hardware.xml
@@ -38,6 +38,8 @@
<feature name="android.hardware.microphone" />
<feature name="android.hardware.screen.portrait" />
<feature name="android.hardware.screen.landscape" />
+ <!-- Indicate support for the Android security model per the CDD. -->
+ <feature name="android.hardware.security.model.compatible" />
<!-- basic system services -->
<feature name="android.software.app_widgets" />
diff --git a/data/etc/wearable_core_hardware.xml b/data/etc/wearable_core_hardware.xml
index 0f364c1..855b110 100644
--- a/data/etc/wearable_core_hardware.xml
+++ b/data/etc/wearable_core_hardware.xml
@@ -32,6 +32,8 @@
<feature name="android.hardware.bluetooth" />
<feature name="android.hardware.touchscreen" />
<feature name="android.hardware.microphone" />
+ <!-- Indicate support for the Android security model per the CDD. -->
+ <feature name="android.hardware.security.model.compatible" />
<!-- basic system services -->
<feature name="android.software.home_screen" />
diff --git a/headers/media_plugin/media/openmax/OMX_Core.h b/headers/media_plugin/media/openmax/OMX_Core.h
index 9ff934e..4b69130 100644
--- a/headers/media_plugin/media/openmax/OMX_Core.h
+++ b/headers/media_plugin/media/openmax/OMX_Core.h
@@ -556,6 +556,12 @@
*/
OMX_EventConfigUpdate,
+ /**
+ * Event fired by a tunneled decoder when the first frame is decoded and
+ * ready to be rendered.
+ */
+ OMX_EventOnFirstTunnelFrameReady,
+
OMX_EventMax = 0x7FFFFFFF
} OMX_EVENTTYPE;
diff --git a/headers/media_plugin/media/openmax/OMX_IndexExt.h b/headers/media_plugin/media/openmax/OMX_IndexExt.h
index a427db7..07bd749 100644
--- a/headers/media_plugin/media/openmax/OMX_IndexExt.h
+++ b/headers/media_plugin/media/openmax/OMX_IndexExt.h
@@ -103,6 +103,7 @@
OMX_IndexParamConsumerUsageBits, /**< reference: OMX_PARAM_U32TYPE */
OMX_IndexConfigLatency, /**< reference: OMX_PARAM_U32TYPE */
OMX_IndexConfigLowLatency, /**< reference: OMX_CONFIG_BOOLEANTYPE */
+ OMX_IndexConfigAndroidTunnelPeek, /**< reference: OMX_CONFIG_BOOLEANTYPE */
OMX_IndexExtOtherEndUnused,
/* Time configurations */
diff --git a/include/android/bitmap.h b/include/android/bitmap.h
index a70dffd..6704a1d 100644
--- a/include/android/bitmap.h
+++ b/include/android/bitmap.h
@@ -241,6 +241,7 @@
*
* Available since API level 30.
*
+ * @param env Handle to the JNI environment pointer.
* @param bitmap Handle to an android.graphics.Bitmap.
* @param outBuffer On success, is set to a pointer to the
* {@link AHardwareBuffer} associated with bitmap. This acquires
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index 8039bb0..b743f49 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -32,6 +32,11 @@
__BEGIN_DECLS
struct AChoreographer;
+/**
+ * Opaque type that provides access to an AChoreographer object.
+ *
+ * A pointer can be obtained using {@link AChoreographer_getInstance()}.
+ */
typedef struct AChoreographer AChoreographer;
/**
@@ -127,6 +132,10 @@
* to listen directly to DisplayManager.DisplayListener#onDisplayChanged events
* instead.
*
+ * As of API level 31, this api is guaranteed to have a consistent view with DisplayManager;
+ * Display#getRefreshRate is guaranteed to not return a stale refresh rate when invoked from this
+ * callback.
+ *
* Available since API level 30.
*/
void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer,
diff --git a/include/android/font.h b/include/android/font.h
index a172618..8a3a474 100644
--- a/include/android/font.h
+++ b/include/android/font.h
@@ -189,7 +189,7 @@
* Available since API level 29.
*
* \param font a font object. Passing NULL is not allowed.
- * \return a positive integer less than or equal to {@link ASYSTEM_FONT_MAX_WEIGHT} is returned.
+ * \return a positive integer less than or equal to {@link AFONT_WEIGHT_MAX} is returned.
*/
uint16_t AFont_getWeight(const AFont* _Nonnull font) __INTRODUCED_IN(29);
@@ -241,7 +241,7 @@
* In this case, AFont_getAxisCount returns 2 and AFont_getAxisTag
* and AFont_getAxisValue will return following values.
* \code{.cpp}
- * AFont* font = AFontIterator_next(ite);
+ * AFont* font = ASystemFontIterator_next(ite);
*
* // Returns the number of axes
* AFont_getAxisCount(font); // Returns 2
@@ -289,7 +289,7 @@
*
* \param font a font object. Passing NULL is not allowed.
* \param axisIndex an index to the font variation settings. Passing value larger than or
- * equal to {@link ASYstemFont_getAxisCount} is not allwed.
+ * equal to {@link AFont_getAxisCount} is not allowed.
* \return a float value for the given font variation setting.
*/
float AFont_getAxisValue(const AFont* _Nonnull font, uint32_t axisIndex)
diff --git a/include/android/font_matcher.h b/include/android/font_matcher.h
index 49e478c..4417422 100644
--- a/include/android/font_matcher.h
+++ b/include/android/font_matcher.h
@@ -36,7 +36,7 @@
* // Simple font query for the ASCII character.
* std::vector<uint16_t> text = { 'A' };
* AFontMatcher* matcher = AFontMatcher_create("sans-serif");
- * ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
+ * AFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
* // runLength will be 1 and the font will points a valid font file.
* AFontMatcher_destroy(matcher);
*
@@ -44,17 +44,17 @@
* std::vector<uint16_t> text = { 0x9AA8 };
* AFontMatcher* matcher = AFontMatcher_create("sans-serif");
* AFontMatcher_setLocales(matcher, "zh-CN,ja-JP");
- * ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
+ * AFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
* // runLength will be 1 and the font will points a Simplified Chinese font.
* AFontMatcher_setLocales(matcher, "ja-JP,zh-CN");
- * ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
+ * AFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
* // runLength will be 1 and the font will points a Japanese font.
* AFontMatcher_destroy(matcher);
*
* // Querying font for text/color emoji
* std::vector<uint16_t> text = { 0xD83D, 0xDC68, 0x200D, 0x2764, 0xFE0F, 0x200D, 0xD83D, 0xDC68 };
* AFontMatcher* matcher = AFontMatcher_create("sans-serif");
- * ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
+ * AFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
* // runLength will be 8 and the font will points a color emoji font.
* AFontMatcher_destroy(matcher);
*
@@ -62,7 +62,7 @@
* // 0x05D0 is a Hebrew character and 0x0E01 is a Thai character.
* std::vector<uint16_t> text = { 0x05D0, 0x0E01 };
* AFontMatcher* matcher = AFontMatcher_create("sans-serif");
- * ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
+ * AFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
* // runLength will be 1 and the font will points a Hebrew font.
* AFontMatcher_destroy(matcher);
* \endcode
@@ -146,7 +146,7 @@
/**
* Set font style to matcher.
*
- * If this function is not called, the matcher performs with {@link ASYSTEM_FONT_WEIGHT_NORMAL}
+ * If this function is not called, the matcher performs with {@link AFONT_WEIGHT_NORMAL}
* with non-italic style.
*
* Available since API level 29.
@@ -206,7 +206,7 @@
* \param textLength a length of the given text buffer. This must not be zero.
* \param runLengthOut if not null, the font run length will be filled.
* \return a font to be used for given text and params. You need to release the returned font by
- * ASystemFont_close when it is no longer needed.
+ * AFont_close when it is no longer needed.
*/
AFont* _Nonnull AFontMatcher_match(
const AFontMatcher* _Nonnull matcher,
diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h
index 1298c62..c3d3a4b 100644
--- a/include/android/imagedecoder.h
+++ b/include/android/imagedecoder.h
@@ -15,7 +15,7 @@
*/
/**
- * @defgroup ImageDecoder
+ * @defgroup ImageDecoder Android Image Decoder
*
* Functions for converting encoded images into RGBA pixels.
*
@@ -62,10 +62,15 @@
struct AAsset;
/**
- * {@link AImageDecoder} functions result code. Many functions will return one of these
- * to indicate success ({@link ANDROID_IMAGE_DECODER_SUCCESS}) or the reason
- * for the failure. On failure, any out-parameters should be considered
- * uninitialized, except where specified.
+ * {@link AImageDecoder} functions result code.
+ *
+ * Introduced in API 30.
+ *
+ * Many functions will return this to indicate success
+ * ({@link ANDROID_IMAGE_DECODER_SUCCESS}) or the reason for the failure. On
+ * failure, any out-parameters should be considered uninitialized, except where
+ * specified. Use {@link AImageDecoder_resultToString} for a readable
+ * version of the result code.
*/
enum {
/**
@@ -109,14 +114,42 @@
/**
* AImageDecoder did not recognize the format.
*/
- ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT = -9
+ ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT = -9,
+ /**
+ * The animation has reached the end.
+ */
+ ANDROID_IMAGE_DECODER_FINISHED = -10,
+ /**
+ * This method cannot be called while the AImageDecoder is in its current
+ * state. For example, various setters (like {@link AImageDecoder_setTargetSize})
+ * can only be called while the AImageDecoder is set to decode the first
+ * frame of an animation. This ensures that any blending and/or restoring
+ * prior frames works correctly.
+ */
+ ANDROID_IMAGE_DECODER_INVALID_STATE = -11,
};
+/**
+ * Return a constant string value representing the error code.
+ *
+ * Introduced in API 31.
+ *
+ * Pass the return value from an {@link AImageDecoder} method (e.g.
+ * {@link AImageDecoder_decodeImage}) for a text string representing the error
+ * code.
+ *
+ * Errors:
+ * - Returns null for a value out of range.
+ */
+const char* _Nullable AImageDecoder_resultToString(int)__INTRODUCED_IN(31);
+
struct AImageDecoder;
/**
* Opaque handle for decoding images.
*
+ * Introduced in API 30
+ *
* Create using one of the following:
* - {@link AImageDecoder_createFromAAsset}
* - {@link AImageDecoder_createFromFd}
@@ -125,7 +158,7 @@
* After creation, {@link AImageDecoder_getHeaderInfo} can be used to retrieve
* information about the encoded image. Other functions, like
* {@link AImageDecoder_setTargetSize}, can be used to specify how to decode, and
- * {@link AImageDecoder_decode} will decode into client provided memory.
+ * {@link AImageDecoder_decodeImage} will decode into client provided memory.
*
* {@link AImageDecoder} objects are NOT thread-safe, and should not be shared across
* threads.
@@ -160,7 +193,8 @@
* - {@link ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT}: The format is not
* supported.
*/
-int AImageDecoder_createFromAAsset(struct AAsset* asset, AImageDecoder** outDecoder)
+int AImageDecoder_createFromAAsset(struct AAsset* _Nonnull asset,
+ AImageDecoder* _Nullable * _Nonnull outDecoder)
__INTRODUCED_IN(30);
/**
@@ -191,7 +225,8 @@
* - {@link ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT}: The format is not
* supported.
*/
-int AImageDecoder_createFromFd(int fd, AImageDecoder** outDecoder) __INTRODUCED_IN(30);
+int AImageDecoder_createFromFd(int fd, AImageDecoder* _Nullable * _Nonnull outDecoder)
+ __INTRODUCED_IN(30);
/**
* Create a new AImageDecoder from a buffer.
@@ -220,22 +255,29 @@
* - {@link ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT}: The format is not
* supported.
*/
-int AImageDecoder_createFromBuffer(const void* buffer, size_t length,
- AImageDecoder** outDecoder) __INTRODUCED_IN(30);
+int AImageDecoder_createFromBuffer(const void* _Nonnull buffer, size_t length,
+ AImageDecoder* _Nullable * _Nonnull outDecoder)
+ __INTRODUCED_IN(30);
/**
* Delete the AImageDecoder.
- *
+ * @param decoder {@link AImageDecoder} object created with one of AImageDecoder_createFrom...
+ * functions.
* Available since API level 30.
*/
-void AImageDecoder_delete(AImageDecoder* decoder) __INTRODUCED_IN(30);
+void AImageDecoder_delete(AImageDecoder* _Nullable decoder) __INTRODUCED_IN(30);
/**
* Choose the desired output format.
*
+ * If the encoded image represents an animation, this must be called while on
+ * the first frame (e.g. before calling {@link AImageDecoder_advanceFrame} or
+ * after calling {@link AImageDecoder_rewind}).
+ *
* Available since API level 30.
*
* @param format {@link AndroidBitmapFormat} to use for the output.
+ * @param decoder an {@link AImageDecoder} object.
* @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
* indicating the reason for the failure. On failure, the
* {@link AImageDecoder} uses the format it was already planning
@@ -248,8 +290,10 @@
* {@link AndroidBitmapFormat}.
* - {@link ANDROID_IMAGE_DECODER_INVALID_CONVERSION}: The
* {@link AndroidBitmapFormat} is incompatible with the image.
+ * - {@link ANDROID_IMAGE_DECODER_INVALID_STATE}: The animation is not on
+ * the first frame.
*/
-int AImageDecoder_setAndroidBitmapFormat(AImageDecoder*,
+int AImageDecoder_setAndroidBitmapFormat(AImageDecoder* _Nonnull decoder,
int32_t format) __INTRODUCED_IN(30);
/**
@@ -259,8 +303,13 @@
* Pass true to this method to leave them unpremultiplied. This has no effect on an
* opaque image.
*
+ * If the encoded image represents an animation, this must be called while on
+ * the first frame (e.g. before calling {@link AImageDecoder_advanceFrame} or
+ * after calling {@link AImageDecoder_rewind}).
+ *
* Available since API level 30.
*
+ * @param decoder an {@link AImageDecoder} object.
* @param unpremultipliedRequired Pass true to leave the pixels unpremultiplied.
* @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
* indicating the reason for the failure.
@@ -271,8 +320,10 @@
* {@link AImageDecoder_setTargetSize}.
* - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The
* {@link AImageDecoder} is null.
+ * - {@link ANDROID_IMAGE_DECODER_INVALID_STATE}: The animation is not on
+ * the first frame.
*/
-int AImageDecoder_setUnpremultipliedRequired(AImageDecoder*,
+int AImageDecoder_setUnpremultipliedRequired(AImageDecoder* _Nonnull decoder,
bool unpremultipliedRequired) __INTRODUCED_IN(30);
/**
@@ -281,8 +332,13 @@
* Ignored by {@link ANDROID_BITMAP_FORMAT_A_8}, which does not support
* an {@link ADataSpace}.
*
+ * If the encoded image represents an animation, this must be called while on
+ * the first frame (e.g. before calling {@link AImageDecoder_advanceFrame} or
+ * after calling {@link AImageDecoder_rewind}).
+ *
* Available since API level 30.
*
+ * @param decoder an {@link AImageDecoder} object.
* @param dataspace The {@link ADataSpace} to decode into. An ADataSpace
* specifies how to interpret the colors. By default,
* AImageDecoder will decode into the ADataSpace specified by
@@ -296,8 +352,11 @@
* - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The
* {@link AImageDecoder} is null or |dataspace| does not correspond to an
* {@link ADataSpace} value.
+ * - {@link ANDROID_IMAGE_DECODER_INVALID_STATE}: The animation is not on
+ * the first frame.
*/
-int AImageDecoder_setDataSpace(AImageDecoder*, int32_t dataspace) __INTRODUCED_IN(30);
+int AImageDecoder_setDataSpace(AImageDecoder* _Nonnull decoder, int32_t dataspace)
+ __INTRODUCED_IN(30);
/**
* Specify the output size for a decoded image.
@@ -308,8 +367,17 @@
* specified by width and height, and the output image will be the size of the
* crop rect.
*
+ * If the encoded image represents an animation, this must be called while on
+ * the first frame (e.g. before calling {@link AImageDecoder_advanceFrame} or
+ * after calling {@link AImageDecoder_rewind}).
+ *
+ * It is strongly recommended to use setTargetSize only for downscaling, as it
+ * is often more efficient to scale-up when rendering than up-front due to
+ * reduced overall memory.
+ *
* Available since API level 30.
*
+ * @param decoder an {@link AImageDecoder} object.
* @param width Width of the output (prior to cropping).
* This will affect future calls to
* {@link AImageDecoder_getMinimumStride}, which will now return
@@ -322,12 +390,14 @@
* - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The
* {@link AImageDecoder} is null.
* - {@link ANDROID_IMAGE_DECODER_INVALID_SCALE}: |width| or |height| is <= 0,
- * the size is too big, any existing crop is not contained by the new image dimensions,
- * or the scale is incompatible with a previous call to
+ * the size is too big, any existing crop is not contained by the new image
+ * dimensions, or the scale is incompatible with a previous call to
* {@link AImageDecoder_setUnpremultipliedRequired}(true).
+ * - {@link ANDROID_IMAGE_DECODER_INVALID_STATE}: The animation is not on
+ * the first frame.
*/
-int AImageDecoder_setTargetSize(AImageDecoder*, int32_t width, int32_t height) __INTRODUCED_IN(30);
-
+int AImageDecoder_setTargetSize(AImageDecoder* _Nonnull decoder, int32_t width,
+ int32_t height) __INTRODUCED_IN(30);
/**
* Compute the dimensions to use for a given sampleSize.
@@ -339,6 +409,7 @@
*
* Available since API level 30.
*
+ * @param decoder an {@link AImageDecoder} object.
* @param sampleSize A subsampling rate of the original image. Must be greater
* than or equal to 1. A sampleSize of 2 means to skip every
* other pixel/line, resulting in a width and height that are
@@ -355,8 +426,10 @@
* - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The
* {@link AImageDecoder}, |width| or |height| is null or |sampleSize| is < 1.
*/
-int AImageDecoder_computeSampledSize(const AImageDecoder*, int sampleSize,
- int32_t* width, int32_t* height) __INTRODUCED_IN(30);
+int AImageDecoder_computeSampledSize(const AImageDecoder* _Nonnull decoder, int sampleSize,
+ int32_t* _Nonnull width, int32_t* _Nonnull height)
+ __INTRODUCED_IN(30);
+
/**
* Specify how to crop the output after scaling (if any).
*
@@ -364,8 +437,13 @@
* the specified {@link ARect}. Clients will only need to allocate enough memory
* for the cropped ARect.
*
+ * If the encoded image represents an animation, this must be called while on
+ * the first frame (e.g. before calling {@link AImageDecoder_advanceFrame} or
+ * after calling {@link AImageDecoder_rewind}).
+ *
* Available since API level 30.
*
+ * @param decoder an {@link AImageDecoder} object.
* @param crop Rectangle describing a crop of the decode. It must be contained inside of
* the (possibly scaled, by {@link AImageDecoder_setTargetSize})
* image dimensions. This will affect future calls to
@@ -379,16 +457,21 @@
*
* Errors:
* - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The
- * {@link AImageDecoder} is null or the crop is not contained by the
+ * {@link AImageDecoder} is null, or the crop is not contained by the
* (possibly scaled) image dimensions.
+ * - {@link ANDROID_IMAGE_DECODER_INVALID_STATE}: The animation is not on
+ * the first frame.
*/
-int AImageDecoder_setCrop(AImageDecoder*, ARect crop) __INTRODUCED_IN(30);
+int AImageDecoder_setCrop(AImageDecoder* _Nonnull decoder, ARect crop) __INTRODUCED_IN(30);
struct AImageDecoderHeaderInfo;
/**
- * Opaque handle for representing information about the encoded image. Retrieved
- * using {@link AImageDecoder_getHeaderInfo} and passed to methods like
- * {@link AImageDecoderHeaderInfo_getWidth} and
+ * Opaque handle for representing information about the encoded image.
+ *
+ * Introduced in API 30
+ *
+ * Retrieved using {@link AImageDecoder_getHeaderInfo} and passed to methods
+ * like {@link AImageDecoderHeaderInfo_getWidth} and
* {@link AImageDecoderHeaderInfo_getHeight}.
*/
typedef struct AImageDecoderHeaderInfo AImageDecoderHeaderInfo;
@@ -399,10 +482,12 @@
* This is owned by the {@link AImageDecoder} and will be destroyed when the
* AImageDecoder is destroyed via {@link AImageDecoder_delete}.
*
+ * @param decoder an {@link AImageDecoder} object.
+ *
* Available since API level 30.
*/
-const AImageDecoderHeaderInfo* AImageDecoder_getHeaderInfo(
- const AImageDecoder*) __INTRODUCED_IN(30);
+const AImageDecoderHeaderInfo* _Nonnull AImageDecoder_getHeaderInfo(
+ const AImageDecoder* _Nonnull decoder) __INTRODUCED_IN(30);
/**
* Report the native width of the encoded image. This is also the logical
@@ -412,7 +497,8 @@
*
* Available since API level 30.
*/
-int32_t AImageDecoderHeaderInfo_getWidth(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+int32_t AImageDecoderHeaderInfo_getWidth(const AImageDecoderHeaderInfo* _Nonnull)
+ __INTRODUCED_IN(30);
/**
* Report the native height of the encoded image. This is also the logical
@@ -422,7 +508,8 @@
*
* Available since API level 30.
*/
-int32_t AImageDecoderHeaderInfo_getHeight(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+int32_t AImageDecoderHeaderInfo_getHeight(const AImageDecoderHeaderInfo* _Nonnull)
+ __INTRODUCED_IN(30);
/**
* Report the mimeType of the encoded image.
@@ -431,8 +518,8 @@
*
* @return a string literal describing the mime type.
*/
-const char* AImageDecoderHeaderInfo_getMimeType(
- const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+const char* _Nonnull AImageDecoderHeaderInfo_getMimeType(
+ const AImageDecoderHeaderInfo* _Nonnull) __INTRODUCED_IN(30);
/**
* Report the {@link AndroidBitmapFormat} the AImageDecoder will decode to
@@ -443,7 +530,7 @@
* Available since API level 30.
*/
int32_t AImageDecoderHeaderInfo_getAndroidBitmapFormat(
- const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+ const AImageDecoderHeaderInfo* _Nonnull) __INTRODUCED_IN(30);
/**
* Report how the {@link AImageDecoder} will handle alpha by default. If the image
@@ -453,9 +540,13 @@
* {@link AImageDecoder_decodeImage} will premultiply pixels by default.
*
* Available since API level 30.
+ *
+ * Starting in API level 31, an AImageDecoder may contain multiple frames of an
+ * animation, but this method still only reports whether the first frame has
+ * alpha.
*/
int AImageDecoderHeaderInfo_getAlphaFlags(
- const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+ const AImageDecoderHeaderInfo* _Nonnull) __INTRODUCED_IN(30);
/**
* Report the dataspace the AImageDecoder will decode to by default.
@@ -476,11 +567,11 @@
* no corresponding {@link ADataSpace}.
*/
int32_t AImageDecoderHeaderInfo_getDataSpace(
- const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+ const AImageDecoderHeaderInfo* _Nonnull) __INTRODUCED_IN(30);
/**
* Return the minimum stride that can be used in
- * {@link AImageDecoder_decodeImage).
+ * {@link AImageDecoder_decodeImage}.
*
* This stride provides no padding, meaning it will be exactly equal to the
* width times the number of bytes per pixel for the {@link AndroidBitmapFormat}
@@ -489,15 +580,58 @@
* If the output is scaled (via {@link AImageDecoder_setTargetSize}) and/or
* cropped (via {@link AImageDecoder_setCrop}), this takes those into account.
*
+ * @param decoder an {@link AImageDecoder} object.
+ *
* Available since API level 30.
*/
-size_t AImageDecoder_getMinimumStride(AImageDecoder*) __INTRODUCED_IN(30);
+size_t AImageDecoder_getMinimumStride(AImageDecoder* _Nonnull decoder) __INTRODUCED_IN(30);
/**
* Decode the image into pixels, using the settings of the {@link AImageDecoder}.
*
* Available since API level 30.
*
+ * Starting in API level 31, it can be used to decode all of the frames of an
+ * animated image (i.e. GIF, WebP) using new APIs. Internally,
+ * AImageDecoder keeps track of its "current frame" - that is, the frame that
+ * will be decoded by a call to AImageDecoder_decodeImage. At creation time, the
+ * current frame is always the first frame, and multiple calls to this method
+ * will each decode the first frame. {@link AImageDecoder_advanceFrame} advances
+ * the current frame to the following frame, so that future calls to this method
+ * will decode that frame. Some frames may update only part of the image. They
+ * may only update a sub-rectangle (see {@link
+ * AImageDecoderFrameInfo_getFrameRect}), or they may have alpha (see
+ * {@link AImageDecoderFrameInfo_hasAlphaWithinBounds}). In these cases, this
+ * method assumes that the prior frame is still residing in the |pixels| buffer,
+ * decodes only the new portion, and blends it with the buffer. Frames that change
+ * the entire |pixels| buffer are "independent", and do not require the prior
+ * frame to remain in the buffer. The first frame is always independent. A
+ * sophisticated client can use information from the {@link AImageDecoderFrameInfo}
+ * to determine whether other frames are independent, or what frames they rely on.
+ *
+ * If the current frame is marked {@link ANDROID_IMAGE_DECODER_DISPOSE_OP_PREVIOUS},
+ * AImageDecoder_decodeImage will store the |pixels| buffer prior to decoding
+ * (note: this only happens for the first in a string of consecutive
+ * ANDROID_IMAGE_DECODER_DISPOSE_OP_PREVIOUS frames). After advancing to the
+ * following frame, AImageDecoder_decodeImage will restore that buffer prior to
+ * decoding that frame. This is the default behavior, but it can be disabled
+ * by passing false to {@link AImageDecoder_setInternallyHandleDisposePrevious}.
+ *
+ * Ignoring timing information, display, etc, a client wishing to decode all
+ * frames of an animated image may conceptually use code like the following:
+ *
+ * while (true) {
+ * int result = AImageDecoder_decodeImage(decoder, pixels, stride, size);
+ * if (result != ANDROID_IMAGE_DECODER_SUCCESS) break;
+ *
+ * // Display or save the image in |pixels|, keeping the buffer intact for
+ * // AImageDecoder to decode the next frame correctly.
+ * Application_viewImage(pixels);
+ *
+ * result = AImageDecoder_advanceFrame(decoder);
+ * if (result != ANDROID_IMAGE_DECODER_SUCCESS) break;
+ * }
+ *
* @param decoder Opaque object representing the decoder.
* @param pixels On success, will be filled with the result
* of the decode. Must be large enough to hold |size| bytes.
@@ -524,11 +658,381 @@
* failed to seek.
* - {@link ANDROID_IMAGE_DECODER_INTERNAL_ERROR}: Some other error, like a
* failure to allocate memory.
+ * - {@link ANDROID_IMAGE_DECODER_FINISHED}: The input contains no
+ * more frames. No decoding occurred. The client must call
+ * {@link AImageDecoder_rewind} before calling
+ * {@link AImageDecoder_decodeImage} again.
*/
-int AImageDecoder_decodeImage(AImageDecoder* decoder,
- void* pixels, size_t stride,
+int AImageDecoder_decodeImage(AImageDecoder* _Nonnull decoder,
+ void* _Nonnull pixels, size_t stride,
size_t size) __INTRODUCED_IN(30);
+/**
+ * Return true iff the image is animated - i.e. has multiple frames.
+ *
+ * Introduced in API 31.
+ *
+ * A single frame GIF is considered to *not* be animated. This may require
+ * seeking past the first frame to verify whether there is a following frame.
+ *
+ * @param decoder an {@link AImageDecoder} object.
+ *
+ * Errors:
+ * - returns false if |decoder| is null.
+ */
+bool AImageDecoder_isAnimated(AImageDecoder* _Nonnull decoder)
+ __INTRODUCED_IN(31);
+
+enum {
+ /**
+ * Reported by {@link AImageDecoder_getRepeatCount} if the
+ * animation should repeat forever.
+ *
+ * Introduced in API 31
+ */
+ ANDROID_IMAGE_DECODER_INFINITE = INT32_MAX,
+};
+
+/**
+ * Report how many times the animation should repeat.
+ *
+ * Introduced in API 31.
+ *
+ * This does not include the first play through. e.g. a repeat
+ * count of 4 means that each frame is played 5 times.
+ *
+ * {@link ANDROID_IMAGE_DECODER_INFINITE} means to repeat forever.
+ *
+ * This may require seeking.
+ *
+ * For non-animated formats, this returns 0. It may return non-zero for
+ * an image with only one frame (i.e. {@link AImageDecoder_isAnimated} returns
+ * false) if the encoded image contains a repeat count.
+ *
+ * @param decoder an {@link AImageDecoder} object.
+ * @return Number of times to repeat on success or a value
+ * indicating the reason for the failure.
+ *
+ * Errors:
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The AImageDecoder
+ * is null.
+ */
+int32_t AImageDecoder_getRepeatCount(AImageDecoder* _Nonnull decoder)
+ __INTRODUCED_IN(31);
+
+/**
+ * Advance to the next frame in the animation.
+ *
+ * Introduced in API 31.
+ *
+ * The AImageDecoder keeps track internally which frame it is ready to decode
+ * (the "current frame"). Initially it is set to decode the first frame, and
+ * each call to {@link AImageDecoder_decodeImage} will continue to decode
+ * the same frame until this method (or {@link AImageDecoder_rewind})
+ * is called.
+ *
+ * Note that this can be used to skip a frame without decoding it. But
+ * some frames depend on (i.e. blend with) prior frames, and
+ * AImageDecoder_decodeImage assumes that the prior frame is in the
+ * |pixels| buffer. In addition, AImageDecoder_decodeImage handles caching and
+ * restoring frames (see {@link ANDROID_IMAGE_DECODER_DISPOSE_OP_PREVIOUS}), so
+ * skipping frames in an image with such frames may not produce the correct
+ * results.
+ *
+ * Only supported by {@link ANDROID_BITMAP_FORMAT_RGBA_8888} and
+ * {@link ANDROID_BITMAP_FORMAT_RGBA_F16}.
+ *
+ * @param decoder an {@link AImageDecoder} object.
+ * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
+ * indicating the reason for the failure.
+ *
+ * Errors:
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The AImageDecoder
+ * represents an image that is not animated (see
+ * {@link AImageDecoder_isAnimated}) or the AImageDecoder is null.
+ * - {@link ANDROID_IMAGE_DECODER_INVALID_STATE): The requested
+ * {@link AndroidBitmapFormat} does not support animation.
+ * - {@link ANDROID_IMAGE_DECODER_INCOMPLETE}: The input appears
+ * to be truncated. The client must call {@link AImageDecoder_rewind}
+ * before calling {@link AImageDecoder_decodeImage} again.
+ * - {@link ANDROID_IMAGE_DECODER_ERROR}: The input contains an error.
+ * The client must call {@link AImageDecoder_rewind} before
+ * calling {@link AImageDecoder_decodeImage} again.
+ * - {@link ANDROID_IMAGE_DECODER_FINISHED}: The input contains no
+ * more frames. The client must call {@link AImageDecoder_rewind}
+ * before calling {@link AImageDecoder_decodeImage} again.
+ */
+int AImageDecoder_advanceFrame(AImageDecoder* _Nonnull decoder)
+ __INTRODUCED_IN(31);
+
+/**
+ * Return to the beginning of the animation.
+ *
+ * Introduced in API 31.
+ *
+ * After this call, the AImageDecoder will be ready to decode the
+ * first frame of the animation. This can be called after reaching
+ * the end of the animation or an error or in the middle of the
+ * animation.
+ *
+ * @param decoder an {@link AImageDecoder} object.
+ * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
+ * indicating the reason for the failure.
+ *
+ * Errors:
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The AImageDecoder
+ * represents an image that is not animated (see
+ * {@link AImageDecoder_isAnimated}) or the AImageDecoder is
+ * null.
+ * - {@link ANDROID_IMAGE_DECODER_SEEK_ERROR}: The asset or file
+ * descriptor failed to seek.
+ */
+int AImageDecoder_rewind(AImageDecoder* _Nonnull decoder)
+ __INTRODUCED_IN(31);
+
+struct AImageDecoderFrameInfo;
+
+/**
+ * Opaque handle to animation information about a single frame.
+ *
+ * Introduced in API 31
+ *
+ * The duration (retrieved with {@link AImageDecoderFrameInfo_getDuration}) is
+ * necessary for clients to display the animation at the proper speed. The other
+ * information is helpful for a client that wants to determine what frames are
+ * independent (or what frames they depend on), but is unnecessary for
+ * a simple client that wants to sequentially display all frames.
+ */
+typedef struct AImageDecoderFrameInfo AImageDecoderFrameInfo;
+
+/**
+ * Create an uninitialized AImageDecoderFrameInfo.
+ *
+ * Introduced in API 31.
+ *
+ * This can be passed to {@link AImageDecoder_getFrameInfo} to fill
+ * in information about the current frame. It may be reused.
+ *
+ * Must be deleted with {@link AImageDecoderFrameInfo_delete}.
+ */
+AImageDecoderFrameInfo* _Nullable AImageDecoderFrameInfo_create()
+ __INTRODUCED_IN(31);
+
+/**
+ * Delete an AImageDecoderFrameInfo.
+ *
+ * Introduced in API 31.
+ */
+void AImageDecoderFrameInfo_delete(
+ AImageDecoderFrameInfo* _Nullable info) __INTRODUCED_IN(31);
+
+/**
+ * Fill |info| with information about the current frame.
+ *
+ * Introduced in API 31.
+ *
+ * Initially, this will return information about the first frame.
+ * {@link AImageDecoder_advanceFrame} and
+ * {@link AImageDecoder_rewind} can be used to change which frame
+ * is the current frame.
+ *
+ * If the image only has one frame, this will fill the {@link
+ * AImageDecoderFrameInfo} with the encoded info, if any, or reasonable
+ * defaults.
+ *
+ * @param decoder Opaque object representing the decoder.
+ * @param info Opaque object to hold frame information. On success, will be
+ * filled with information regarding the current frame.
+ * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
+ * indicating the reason for the failure.
+ *
+ * Errors:
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: One of the parameters is null.
+ * - {@link ANDROID_IMAGE_DECODER_FINISHED}: The input contains no
+ * more frames. The client must call {@link AImageDecoder_rewind} to reset the
+ * current frame to a valid frame (0).
+ */
+int AImageDecoder_getFrameInfo(AImageDecoder* _Nonnull decoder,
+ AImageDecoderFrameInfo* _Nonnull info) __INTRODUCED_IN(31);
+
+/**
+ * Report the number of nanoseconds to show the current frame.
+ *
+ * Introduced in API 31.
+ *
+ * Errors:
+ * - returns 0 if |info| is null.
+ */
+int64_t AImageDecoderFrameInfo_getDuration(
+ const AImageDecoderFrameInfo* _Nonnull info) __INTRODUCED_IN(31);
+
+/**
+ * The rectangle of the image (within 0, 0,
+ * {@link AImageDecoderHeaderInfo_getWidth}, {@link AImageDecoderHeaderInfo_getHeight})
+ * updated by this frame.
+ *
+ * Introduced in API 31.
+ *
+ * Note that this is unaffected by calls to
+ * {@link AImageDecoder_setTargetSize} or
+ * {@link AImageDecoder_setCrop}.
+ *
+ * A frame may update only part of the image. This will always be
+ * contained by the image’s dimensions.
+ *
+ * This, along with other information in AImageDecoderFrameInfo,
+ * can be useful for determining whether a frame is independent, but
+ * the decoder handles blending frames, so a simple
+ * sequential client does not need this.
+ *
+ * Errors:
+ * - returns an empty ARect if |info| is null.
+ */
+ARect AImageDecoderFrameInfo_getFrameRect(
+ const AImageDecoderFrameInfo* _Nonnull info) __INTRODUCED_IN(31);
+
+/**
+ * Whether the new portion of this frame may contain alpha.
+ *
+ * Introduced in API 31.
+ *
+ * Note that this may differ from whether the composed frame has
+ * alpha. If this frame does not fill the entire image dimensions
+ * (see {@link AImageDecoderFrameInfo_getFrameRect}) or it blends
+ * with an opaque frame, for example, the composed frame’s alpha
+ * may not match. It is also conservative; for example, if a color
+ * index-based frame has a color with alpha but does not use it,
+ * this will still return true.
+ *
+ * This, along with other information in AImageDecoderFrameInfo,
+ * can be useful for determining whether a frame is independent, but
+ * the decoder handles blending frames, so a simple
+ * sequential client does not need this.
+ *
+ * Errors:
+ * - returns false if |info| is null.
+ */
+bool AImageDecoderFrameInfo_hasAlphaWithinBounds(
+ const AImageDecoderFrameInfo* _Nonnull info) __INTRODUCED_IN(31);
+
+/**
+ * How a frame is “disposed” before showing the next one.
+ *
+ * Introduced in API 31.
+ *
+ * This, along with other information in AImageDecoderFrameInfo,
+ * can be useful for determining whether a frame is independent, but
+ * the decoder handles disposing of frames, so a simple
+ * sequential client does not need this.
+ */
+enum {
+ /// No disposal. The following frame will be drawn directly
+ /// on top of this one.
+ ANDROID_IMAGE_DECODER_DISPOSE_OP_NONE = 1,
+ /// The frame’s rectangle is cleared to transparent (by AImageDecoder)
+ /// before decoding the next frame.
+ ANDROID_IMAGE_DECODER_DISPOSE_OP_BACKGROUND = 2,
+ /// The frame’s rectangle is reverted to the prior frame before decoding
+ /// the next frame. This is handled by AImageDecoder, unless
+ /// {@link AImageDecoder_setInternallyHandleDisposePrevious} is set to false.
+ ANDROID_IMAGE_DECODER_DISPOSE_OP_PREVIOUS = 3,
+};
+
+/**
+ * Return how this frame is “disposed” before showing the next one.
+ *
+ * Introduced in API 31.
+ *
+ * This, along with other information in AImageDecoderFrameInfo,
+ * can be useful for determining whether a frame is independent, but
+ * the decoder handles disposing of frames, so a simple
+ * sequential client does not need this.
+ *
+ * @return one of:
+ * - {@link ANDROID_IMAGE_DECODER_DISPOSE_OP_NONE}
+ * - {@link ANDROID_IMAGE_DECODER_DISPOSE_OP_BACKGROUND}
+ * - {@link ANDROID_IMAGE_DECODER_DISPOSE_OP_PREVIOUS}
+ * Errors:
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} if |info| is null.
+ */
+int32_t AImageDecoderFrameInfo_getDisposeOp(
+ const AImageDecoderFrameInfo* _Nonnull info) __INTRODUCED_IN(31);
+
+/**
+ * How a frame is blended with the previous frame.
+ *
+ * Introduced in API 31.
+ *
+ * This, along with other information in AImageDecoderFrameInfo,
+ * can be useful for determining whether a frame is independent, but
+ * the decoder handles blending frames, so a simple
+ * sequential client does not need this.
+ */
+enum {
+ /// This frame replaces existing content. This corresponds
+ /// to webp’s “do not blend”.
+ ANDROID_IMAGE_DECODER_BLEND_OP_SRC = 1,
+ /// This frame blends with the previous frame.
+ ANDROID_IMAGE_DECODER_BLEND_OP_SRC_OVER = 2,
+};
+
+/**
+ * Return how this frame is blended with the previous frame.
+ *
+ * Introduced in API 31.
+ *
+ * This, along with other information in AImageDecoderFrameInfo,
+ * can be useful for determining whether a frame is independent, but
+ * the decoder handles blending frames, so a simple
+ * sequential client does not need this.
+ *
+ * @return one of:
+ * - {@link ANDROID_IMAGE_DECODER_BLEND_OP_SRC}
+ * - {@link ANDROID_IMAGE_DECODER_BLEND_OP_SRC_OVER}
+ * Errors:
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} if |info| is null.
+ */
+int32_t AImageDecoderFrameInfo_getBlendOp(
+ const AImageDecoderFrameInfo* _Nonnull info)
+ __INTRODUCED_IN(31);
+
+/**
+ * Whether to have AImageDecoder store the frame prior to a
+ * frame marked {@link ANDROID_IMAGE_DECODER_DISPOSE_OP_PREVIOUS}.
+ *
+ * Introduced in API 31.
+ *
+ * The default is true. Many images will not have such a frame (it
+ * is not supported by WebP, and only some GIFs use it). But
+ * if frame i is ANDROID_IMAGE_DECODER_DISPOSE_OP_PREVIOUS, then i+1
+ * may depend on i-1. When this setting is true, AImageDecoder will
+ * defensively copy frame i-1 (i.e. the contents of |pixels| in
+ * {@link AImageDecoder_decodeImage}) into an internal buffer so that
+ * it can be used to decode i+1.
+ *
+ * AImageDecoder will only store a single frame, at the size specified
+ * by {@link AImageDecoder_setTargetSize} (or the original dimensions
+ * if that method has not been called), and will discard it when it is
+ * no longer necessary.
+ *
+ * A client that desires to manually store such frames may set this to
+ * false, so that AImageDecoder does not need to store this extra
+ * frame. Instead, when decoding the same
+ * ANDROID_IMAGE_DECODER_DISPOSE_OP_PREVIOUS frame i, AImageDecoder
+ * will decode directly into |pixels|, assuming the client stored i-1.
+ * When asked to decode frame i+1, AImageDecoder will now assume that
+ * the client provided i-1 in |pixels|.
+ *
+ * @param decoder an {@link AImageDecoder} object.
+ * @param handleInternally Whether AImageDecoder will internally
+ * handle ANDROID_IMAGE_DECODER_DISPOSE_OP_PREVIOUS
+ * frames.
+ */
+void AImageDecoder_setInternallyHandleDisposePrevious(
+ AImageDecoder* _Nonnull decoder, bool handleInternally)
+ __INTRODUCED_IN(31);
+
+
#ifdef __cplusplus
}
#endif
diff --git a/include/android/input.h b/include/android/input.h
index dbfd61e..bb98beb 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -55,6 +55,7 @@
#include <sys/types.h>
#include <android/keycodes.h>
#include <android/looper.h>
+#include <jni.h>
#if !defined(__INTRODUCED_IN)
#define __INTRODUCED_IN(__api_level) /* nothing */
@@ -162,6 +163,12 @@
/** Focus event */
AINPUT_EVENT_TYPE_FOCUS = 3,
+
+ /** Capture event */
+ AINPUT_EVENT_TYPE_CAPTURE = 4,
+
+ /** Drag event */
+ AINPUT_EVENT_TYPE_DRAG = 5,
};
/**
@@ -671,7 +678,7 @@
/**
* Axis constant: The movement of y position of a motion event.
*
- * Same as {@link RELATIVE_X}, but for y position.
+ * Same as {@link AMOTION_EVENT_AXIS_RELATIVE_X}, but for y position.
*/
AMOTION_EVENT_AXIS_RELATIVE_Y = 28,
/**
@@ -852,6 +859,10 @@
AINPUT_SOURCE_TOUCH_NAVIGATION = 0x00200000 | AINPUT_SOURCE_CLASS_NONE,
/** joystick */
AINPUT_SOURCE_JOYSTICK = 0x01000000 | AINPUT_SOURCE_CLASS_JOYSTICK,
+ /** HDMI */
+ AINPUT_SOURCE_HDMI = 0x02000000 | AINPUT_SOURCE_CLASS_BUTTON,
+ /** sensor */
+ AINPUT_SOURCE_SENSOR = 0x04000000 | AINPUT_SOURCE_CLASS_NONE,
/** rotary encoder */
AINPUT_SOURCE_ROTARY_ENCODER = 0x00400000 | AINPUT_SOURCE_CLASS_NONE,
@@ -931,6 +942,15 @@
/** Get the input event source. */
int32_t AInputEvent_getSource(const AInputEvent* event);
+/**
+ * Releases interface objects created by {@link AKeyEvent_fromJava()}
+ * and {@link AMotionEvent_fromJava()}.
+ * After returning, the specified AInputEvent* object becomes invalid and should no longer be used.
+ * The underlying Java object remains valid and does not change its state.
+ */
+
+void AInputEvent_release(const AInputEvent* event);
+
/*** Accessors for key events only. ***/
/** Get the key event action. */
@@ -977,6 +997,13 @@
*/
int64_t AKeyEvent_getEventTime(const AInputEvent* key_event);
+/**
+ * Creates a native AInputEvent* object that is a copy of the specified Java android.view.KeyEvent.
+ * The result may be used with generic and KeyEvent-specific AInputEvent_* functions. The object
+ * returned by this function must be disposed using {@link AInputEvent_release()}.
+ */
+const AInputEvent* AKeyEvent_fromJava(JNIEnv* env, jobject keyEvent);
+
/*** Accessors for motion events only. ***/
/** Get the combined motion event action code and pointer index. */
@@ -1292,6 +1319,13 @@
float AMotionEvent_getHistoricalAxisValue(const AInputEvent* motion_event,
int32_t axis, size_t pointer_index, size_t history_index);
+/**
+ * Creates a native AInputEvent* object that is a copy of the specified Java
+ * android.view.MotionEvent. The result may be used with generic and MotionEvent-specific
+ * AInputEvent_* functions. The object returned by this function must be disposed using
+ * {@link AInputEvent_release()}.
+ */
+const AInputEvent* AMotionEvent_fromJava(JNIEnv* env, jobject motionEvent);
struct AInputQueue;
/**
diff --git a/include/android/multinetwork.h b/include/android/multinetwork.h
index 424299d..509ee0e 100644
--- a/include/android/multinetwork.h
+++ b/include/android/multinetwork.h
@@ -83,7 +83,7 @@
*
* To clear a previous process binding, invoke with NETWORK_UNSPECIFIED.
*
- * This is the equivalent of: [android.net.ConnectivityManager#setProcessDefaultNetwork()](https://developer.android.com/reference/android/net/ConnectivityManager.html#setProcessDefaultNetwork(android.net.Network))
+ * This is the equivalent of: [android.net.ConnectivityManager#bindProcessToNetwork()](https://developer.android.com/reference/android/net/ConnectivityManager.html#bindProcessToNetwork(android.net.Network))
*
* Available since API level 23.
*/
@@ -91,6 +91,41 @@
/**
+ * Gets the |network| bound to the current process, as per android_setprocnetwork.
+ *
+ * This is the equivalent of: [android.net.ConnectivityManager#getBoundNetworkForProcess()](https://developer.android.com/reference/android/net/ConnectivityManager.html#getBoundNetworkForProcess(android.net.Network))
+ * Returns 0 on success, or -1 setting errno to EINVAL if a null pointer is
+ * passed in.
+ *
+ *
+ * Available since API level 31.
+ */
+int android_getprocnetwork(net_handle_t *network) __INTRODUCED_IN(31);
+
+/**
+ * Binds domain name resolutions performed by this process to |network|.
+ * android_setprocnetwork takes precedence over this setting.
+ *
+ * To clear a previous process binding, invoke with NETWORK_UNSPECIFIED.
+ * On success 0 is returned. On error -1 is returned, and errno is set.
+ *
+ * Available since API level 31.
+ */
+int android_setprocdns(net_handle_t network) __INTRODUCED_IN(31);
+
+/**
+ * Gets the |network| to which domain name resolutions are bound on the
+ * current process.
+ *
+ * Returns 0 on success, or -1 setting errno to EINVAL if a null pointer is
+ * passed in.
+ *
+ * Available since API level 31.
+ */
+int android_getprocdns(net_handle_t *network) __INTRODUCED_IN(31);
+
+
+/**
* Perform hostname resolution via the DNS servers associated with |network|.
*
* All arguments (apart from |network|) are used identically as those passed
diff --git a/include/android/permission_manager.h b/include/android/permission_manager.h
new file mode 100644
index 0000000..753b6d1
--- /dev/null
+++ b/include/android/permission_manager.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+/**
+ * Structures and functions related to permission checks in native code.
+ *
+ * @addtogroup Permission
+ * @{
+ */
+
+/**
+ * @file permission_manager.h
+ */
+
+#ifndef ANDROID_PERMISSION_MANAGER_H
+#define ANDROID_PERMISSION_MANAGER_H
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+/**
+ * Permission check results.
+ *
+ * Introduced in API 31.
+ */
+enum {
+ /**
+ * This is returned by APermissionManager_checkPermission()
+ * if the permission has been granted to the given package.
+ */
+ PERMISSION_MANAGER_PERMISSION_GRANTED = 0,
+ /**
+ * This is returned by APermissionManager_checkPermission()
+ * if the permission has not been granted to the given package.
+ */
+ PERMISSION_MANAGER_PERMISSION_DENIED = -1,
+};
+
+/**
+ * Permission check return status values.
+ *
+ * Introduced in API 31.
+ */
+enum {
+ /**
+ * This is returned if the permission check completed without errors.
+ * The output result is valid and contains one of {::PERMISSION_MANAGER_PERMISSION_GRANTED,
+ * ::PERMISSION_MANAGER_PERMISSION_DENIED}.
+ */
+ PERMISSION_MANAGER_STATUS_OK = 0,
+ /**
+ * This is returned if the permission check encountered an unspecified error.
+ * The output result is unmodified.
+ */
+ PERMISSION_MANAGER_STATUS_ERROR_UNKNOWN = -1,
+ /**
+ * This is returned if the permission check failed because the service is
+ * unavailable. The output result is unmodified.
+ */
+ PERMISSION_MANAGER_STATUS_SERVICE_UNAVAILABLE = -2,
+};
+
+/**
+ * Checks whether the package with the given pid/uid has been granted a permission.
+ *
+ * Note that the Java API of Context#checkPermission() is usually faster due to caching,
+ * thus is preferred over this API wherever possible.
+ *
+ * @param permission the permission to be checked.
+ * @param pid the process id of the package to be checked.
+ * @param uid the uid of the package to be checked.
+ * @param outResult output of the permission check result.
+ *
+ * @return error codes if any error happened during the check.
+ */
+int32_t APermissionManager_checkPermission(const char* permission,
+ pid_t pid,
+ uid_t uid,
+ int32_t* outResult) __INTRODUCED_IN(31);
+
+__END_DECLS
+
+#endif // ANDROID_PERMISSION_MANAGER_H
+
+/** @} */
diff --git a/include/android/sensor.h b/include/android/sensor.h
index 6447844..9dc6983 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -228,8 +228,8 @@
*
* If a device supports the sensor additional information feature, it will
* report additional information events via {@link ASensorEvent} and will
- * have {@link ASensorEvent#type} set to
- * {@link ASENSOR_TYPE_ADDITIONAL_INFO} and {@link ASensorEvent#sensor} set
+ * have the type of {@link ASensorEvent} set to
+ * {@link ASENSOR_TYPE_ADDITIONAL_INFO} and the sensor of {@link ASensorEvent} set
* to the handle of the reporting sensor.
*
* Additional information reports consist of multiple frames ordered by
@@ -428,6 +428,10 @@
} ADynamicSensorEvent;
typedef struct AAdditionalInfoEvent {
+ /**
+ * Event type, such as ASENSOR_ADDITIONAL_INFO_BEGIN, ASENSOR_ADDITIONAL_INFO_END and others.
+ * Refer to {@link ASENSOR_TYPE_ADDITIONAL_INFO} for the expected reporting behavior.
+ */
int32_t type;
int32_t serial;
union {
@@ -436,24 +440,36 @@
};
} AAdditionalInfoEvent;
+/**
+ * Information that describes a sensor event, refer to
+ * <a href="/reference/android/hardware/SensorEvent">SensorEvent</a> for additional
+ * documentation.
+ */
/* NOTE: changes to this struct has to be backward compatible */
typedef struct ASensorEvent {
int32_t version; /* sizeof(struct ASensorEvent) */
- int32_t sensor;
- int32_t type;
- int32_t reserved0;
+ int32_t sensor; /** The sensor that generates this event */
+ int32_t type; /** Sensor type for the event, such as {@link ASENSOR_TYPE_ACCELEROMETER} */
+ int32_t reserved0; /** do not use */
+ /**
+ * The time in nanoseconds at which the event happened, and its behavior
+ * is identical to <a href="/reference/android/hardware/SensorEvent#timestamp">
+ * SensorEvent::timestamp</a> in Java API.
+ */
int64_t timestamp;
union {
union {
float data[16];
ASensorVector vector;
ASensorVector acceleration;
+ ASensorVector gyro;
ASensorVector magnetic;
float temperature;
float distance;
float light;
float pressure;
float relative_humidity;
+ AUncalibratedEvent uncalibrated_acceleration;
AUncalibratedEvent uncalibrated_gyro;
AUncalibratedEvent uncalibrated_magnetic;
AMetaDataEvent meta_data;
@@ -651,9 +667,10 @@
/**
* Destroy a direct channel
*
- * Destroy a direct channel previously created using {@link ASensorManager_createDirectChannel}.
- * The buffer used for creating direct channel does not get destroyed with
- * {@link ASensorManager_destroy} and has to be close or released separately.
+ * Destroy a direct channel previously created by using one of
+ * ASensorManager_create*DirectChannel() derivative functions.
+ * Note that the buffer used for creating the direct channel does not get destroyed with
+ * ASensorManager_destroyDirectChannel and has to be closed or released separately.
*
* Available since API level 26.
*
@@ -699,7 +716,7 @@
* \param channelId channel id (a positive integer) returned from
* {@link ASensorManager_createSharedMemoryDirectChannel} or
* {@link ASensorManager_createHardwareBufferDirectChannel}.
- *
+ * \param rate one of predefined ASENSOR_DIRECT_RATE_... that is supported by the sensor.
* \return positive token for success or negative error code.
*/
int ASensorManager_configureDirectReport(ASensorManager* manager,
@@ -716,7 +733,7 @@
* \param queue {@link ASensorEventQueue} for sensor event to be report to.
* \param sensor {@link ASensor} to be enabled.
* \param samplingPeriodUs sampling period of sensor in microseconds.
- * \param maxBatchReportLatencyus maximum time interval between two batch of sensor events are
+ * \param maxBatchReportLatencyUs maximum time interval between two batches of sensor events are
* delievered in microseconds. For sensor streaming, set to 0.
* \return 0 on success or a negative error code on failure.
*/
@@ -776,7 +793,7 @@
* Retrieve next available events from the queue to a specified event array.
*
* \param queue {@link ASensorEventQueue} to get events from
- * \param events pointer to an array of {@link ASensorEvents}.
+ * \param events pointer to an array of {@link ASensorEvent}.
* \param count max number of event that can be filled into array event.
* \return number of events returned on success; negative error code when
* no events are pending or an error has occurred.
@@ -796,7 +813,7 @@
* Request that {@link ASENSOR_TYPE_ADDITIONAL_INFO} events to be delivered on
* the given {@link ASensorEventQueue}.
*
- * Sensor data events are always delivered to the {@ASensorEventQueue}.
+ * Sensor data events are always delivered to the {@link ASensorEventQueue}.
*
* The {@link ASENSOR_TYPE_ADDITIONAL_INFO} events will be returned through
* {@link ASensorEventQueue_getEvents}. The client is responsible for checking
@@ -888,7 +905,7 @@
*
* \param sensor a {@link ASensor} to denote the sensor to be checked.
* \param channelType Channel type constant, either
- * {@ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY}
+ * {@link ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY}
* or {@link ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER}.
* \returns true if sensor supports the specified direct channel type.
*/
@@ -911,15 +928,15 @@
* Returns the sensor's handle.
*
* The handle identifies the sensor within the system and is included in the
- * {@link ASensorEvent#sensor} field of sensor events, including those sent with type
+ * sensor field of {@link ASensorEvent}, including those sent with type
* {@link ASENSOR_TYPE_ADDITIONAL_INFO}.
*
* A sensor's handle is able to be used to map {@link ASENSOR_TYPE_ADDITIONAL_INFO} events to the
* sensor that generated the event.
*
* It is important to note that the value returned by {@link ASensor_getHandle} is not the same as
- * the value returned by the Java API {@link android.hardware.Sensor#getId} and no mapping exists
- * between the values.
+ * the value returned by the Java API <a href="/reference/android/hardware/Sensor#getId()">
+ * android.hardware.Sensor's getId()</a> and no mapping exists between the values.
*
* Available since API level 29.
*/
diff --git a/include/android/sharedmem.h b/include/android/sharedmem.h
index 7994aa9..e0a8045 100644
--- a/include/android/sharedmem.h
+++ b/include/android/sharedmem.h
@@ -59,9 +59,10 @@
*
* Use close() to release the shared memory region.
*
- * Use {@link android.os.ParcelFileDescriptor} to pass the file descriptor to
- * another process. File descriptors may also be sent to other processes over a Unix domain
- * socket with sendmsg and SCM_RIGHTS. See sendmsg(3) and cmsg(3) man pages for more information.
+ * Use <a href="/reference/android/os/ParcelFileDescriptor">android.os.ParcelFileDescriptor</a>
+ * to pass the file descriptor to another process. File descriptors may also be sent to other
+ * processes over a Unix domain socket with sendmsg and SCM_RIGHTS. See sendmsg(3) and
+ * cmsg(3) man pages for more information.
*
* If you intend to share this file descriptor with a child process after
* calling exec(3), note that you will need to use fcntl(2) with FD_SETFD
@@ -71,7 +72,8 @@
*
* \param name an optional name.
* \param size size of the shared memory region
- * \return file descriptor that denotes the shared memory; -1 and sets errno on failure, or -EINVAL if the error is that size was 0.
+ * \return file descriptor that denotes the shared memory;
+ * -1 and sets errno on failure, or -EINVAL if the error is that size was 0.
*/
int ASharedMemory_create(const char *name, size_t size) __INTRODUCED_IN(26);
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index 27b880d..f6c2e55 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -46,7 +46,7 @@
/**
* Creates an ASurfaceControl with either ANativeWindow or an ASurfaceControl as its parent.
- * |debug_name| is a debug name associated with this surface. It can be used to
+ * \a debug_name is a debug name associated with this surface. It can be used to
* identify this surface in the SurfaceFlinger's layer tree. It must not be
* null.
*
@@ -67,9 +67,21 @@
__INTRODUCED_IN(29);
/**
- * Releases the |surface_control| object. After releasing the ASurfaceControl the caller no longer
- * has ownership of the AsurfaceControl. The surface and it's children may remain on display as long
- * as their parent remains on display.
+ * Acquires a reference on the given ASurfaceControl object. This prevents the object
+ * from being deleted until the reference is removed.
+ *
+ * To release the reference, use the ASurfaceControl_release function.
+ *
+ * Available since API level 31.
+ */
+void ASurfaceControl_acquire(ASurfaceControl* surface_control) __INTRODUCED_IN(31);
+
+/**
+ * Removes a reference that was previously acquired with one of the following functions:
+ * ASurfaceControl_createFromWindow
+ * ASurfaceControl_create
+ * ANativeWindow_acquire
+ * The surface and its children may remain on display as long as their parent remains on display.
*
* Available since API level 29.
*/
@@ -85,21 +97,21 @@
/**
* The caller takes ownership of the transaction and must release it using
- * ASurfaceControl_delete below.
+ * ASurfaceTransaction_delete() below.
*
* Available since API level 29.
*/
ASurfaceTransaction* ASurfaceTransaction_create() __INTRODUCED_IN(29);
/**
- * Destroys the |transaction| object.
+ * Destroys the \a transaction object.
*
* Available since API level 29.
*/
void ASurfaceTransaction_delete(ASurfaceTransaction* transaction) __INTRODUCED_IN(29);
/**
- * Applies the updates accumulated in |transaction|.
+ * Applies the updates accumulated in \a transaction.
*
* Note that the transaction is guaranteed to be applied atomically. The
* transactions which are applied on the same thread are also guaranteed to be
@@ -121,10 +133,10 @@
* ASurfaceTransaction_OnComplete callback can be used to be notified when a frame
* including the updates in a transaction was presented.
*
- * |context| is the optional context provided by the client that is passed into
+ * \param context Optional context provided by the client that is passed into
* the callback.
*
- * |stats| is an opaque handle that can be passed to ASurfaceTransactionStats functions to query
+ * \param stats Opaque handle that can be passed to ASurfaceTransactionStats functions to query
* information about the transaction. The handle is only valid during the callback.
*
* THREADING
@@ -135,6 +147,28 @@
typedef void (*ASurfaceTransaction_OnComplete)(void* context, ASurfaceTransactionStats* stats)
__INTRODUCED_IN(29);
+
+/**
+ * The ASurfaceTransaction_OnCommit callback is invoked when transaction is applied and the updates
+ * are ready to be presented. This callback will be invoked before the
+ * ASurfaceTransaction_OnComplete callback.
+ *
+ * \param context Optional context provided by the client that is passed into the callback.
+ *
+ * \param stats Opaque handle that can be passed to ASurfaceTransactionStats functions to query
+ * information about the transaction. The handle is only valid during the callback.
+ * Present and release fences are not available for this callback. Querying them using
+ * ASurfaceTransactionStats_getPresentFenceFd and ASurfaceTransactionStats_getPreviousReleaseFenceFd
+ * will result in failure.
+ *
+ * THREADING
+ * The transaction committed callback can be invoked on any thread.
+ *
+ * Available since API level 31.
+ */
+typedef void (*ASurfaceTransaction_OnCommit)(void* context, ASurfaceTransactionStats* stats)
+ __INTRODUCED_IN(31);
+
/**
* Returns the timestamp of when the frame was latched by the framework. Once a frame is
* latched by the framework, it is presented at the following hardware vsync.
@@ -149,20 +183,22 @@
* The recipient of the callback takes ownership of the fence and is responsible for closing
* it. If a device does not support present fences, a -1 will be returned.
*
+ * This query is not valid for ASurfaceTransaction_OnCommit callback.
+ *
* Available since API level 29.
*/
int ASurfaceTransactionStats_getPresentFenceFd(ASurfaceTransactionStats* surface_transaction_stats)
__INTRODUCED_IN(29);
/**
- * |outASurfaceControls| returns an array of ASurfaceControl pointers that were updated during the
+ * \a outASurfaceControls returns an array of ASurfaceControl pointers that were updated during the
* transaction. Stats for the surfaces can be queried through ASurfaceTransactionStats functions.
* When the client is done using the array, it must release it by calling
* ASurfaceTransactionStats_releaseASurfaceControls.
*
* Available since API level 29.
*
- * |outASurfaceControlsSize| returns the size of the ASurfaceControls array.
+ * \a outASurfaceControlsSize returns the size of the ASurfaceControls array.
*/
void ASurfaceTransactionStats_getASurfaceControls(ASurfaceTransactionStats* surface_transaction_stats,
ASurfaceControl*** outASurfaceControls,
@@ -170,7 +206,7 @@
__INTRODUCED_IN(29);
/**
* Releases the array of ASurfaceControls that were returned by
- * ASurfaceTransactionStats_getASurfaceControls.
+ * ASurfaceTransactionStats_getASurfaceControls().
*
* Available since API level 29.
*/
@@ -195,8 +231,8 @@
* buffer is already released. The recipient of the callback takes ownership of the
* previousReleaseFenceFd and is responsible for closing it.
*
- * Each time a buffer is set through ASurfaceTransaction_setBuffer()/_setCachedBuffer() on a
- * transaction which is applied, the framework takes a ref on this buffer. The framework treats the
+ * Each time a buffer is set through ASurfaceTransaction_setBuffer() on a transaction
+ * which is applied, the framework takes a ref on this buffer. The framework treats the
* addition of a buffer to a particular surface as a unique ref. When a transaction updates or
* removes a buffer from a surface, or removes the surface itself from the tree, this ref is
* guaranteed to be released in the OnComplete callback for this transaction. The
@@ -206,6 +242,8 @@
* The client must ensure that all pending refs on a buffer are released before attempting to reuse
* this buffer, otherwise synchronization errors may occur.
*
+ * This query is not valid for ASurfaceTransaction_OnCommit callback.
+ *
* Available since API level 29.
*/
int ASurfaceTransactionStats_getPreviousReleaseFenceFd(
@@ -224,10 +262,20 @@
ASurfaceTransaction_OnComplete func) __INTRODUCED_IN(29);
/**
- * Reparents the |surface_control| from its old parent to the |new_parent| surface control.
- * Any children of the* reparented |surface_control| will remain children of the |surface_control|.
+ * Sets the callback that will be invoked when the updates from this transaction are applied and are
+ * ready to be presented. This callback will be invoked before the ASurfaceTransaction_OnComplete
+ * callback.
*
- * The |new_parent| can be null. Surface controls with a null parent do not appear on the display.
+ * Available since API level 31.
+ */
+void ASurfaceTransaction_setOnCommit(ASurfaceTransaction* transaction, void* context,
+ ASurfaceTransaction_OnCommit func) __INTRODUCED_IN(31);
+
+/**
+ * Reparents the \a surface_control from its old parent to the \a new_parent surface control.
+ * Any children of the reparented \a surface_control will remain children of the \a surface_control.
+ *
+ * The \a new_parent can be null. Surface controls with a null parent do not appear on the display.
*
* Available since API level 29.
*/
@@ -235,14 +283,16 @@
ASurfaceControl* surface_control, ASurfaceControl* new_parent)
__INTRODUCED_IN(29);
-/* Parameter for ASurfaceTransaction_setVisibility */
+/**
+ * Parameter for ASurfaceTransaction_setVisibility().
+ */
enum {
ASURFACE_TRANSACTION_VISIBILITY_HIDE = 0,
ASURFACE_TRANSACTION_VISIBILITY_SHOW = 1,
};
/**
- * Updates the visibility of |surface_control|. If show is set to
- * ASURFACE_TRANSACTION_VISIBILITY_HIDE, the |surface_control| and all surfaces in its subtree will
+ * Updates the visibility of \a surface_control. If show is set to
+ * ASURFACE_TRANSACTION_VISIBILITY_HIDE, the \a surface_control and all surfaces in its subtree will
* be hidden.
*
* Available since API level 29.
@@ -252,7 +302,7 @@
__INTRODUCED_IN(29);
/**
- * Updates the z order index for |surface_control|. Note that the z order for a surface
+ * Updates the z order index for \a surface_control. Note that the z order for a surface
* is relative to other surfaces which are siblings of this surface. The behavior of sibilings with
* the same z order is undefined.
*
@@ -265,13 +315,16 @@
__INTRODUCED_IN(29);
/**
- * Updates the AHardwareBuffer displayed for |surface_control|. If not -1, the
+ * Updates the AHardwareBuffer displayed for \a surface_control. If not -1, the
* acquire_fence_fd should be a file descriptor that is signaled when all pending work
* for the buffer is complete and the buffer can be safely read.
*
- * The frameworks takes ownership of the |acquire_fence_fd| passed and is responsible
+ * The frameworks takes ownership of the \a acquire_fence_fd passed and is responsible
* for closing it.
*
+ * Note that the buffer must be allocated with AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE
+ * as the surface control might be composited using the GPU.
+ *
* Available since API level 29.
*/
void ASurfaceTransaction_setBuffer(ASurfaceTransaction* transaction,
@@ -279,9 +332,9 @@
int acquire_fence_fd = -1) __INTRODUCED_IN(29);
/**
- * Updates the color for |surface_control|. This will make the background color for the
- * ASurfaceControl visible in transparent regions of the surface. Colors |r|, |g|,
- * and |b| must be within the range that is valid for |dataspace|. |dataspace| and |alpha|
+ * Updates the color for \a surface_control. This will make the background color for the
+ * ASurfaceControl visible in transparent regions of the surface. Colors \a r, \a g,
+ * and \a b must be within the range that is valid for \a dataspace. \a dataspace and \a alpha
* will be the dataspace and alpha set for the background color layer.
*
* Available since API level 29.
@@ -292,15 +345,15 @@
__INTRODUCED_IN(29);
/**
- * |source| the sub-rect within the buffer's content to be rendered inside the surface's area
+ * \param source The sub-rect within the buffer's content to be rendered inside the surface's area
* The surface's source rect is clipped by the bounds of its current buffer. The source rect's width
* and height must be > 0.
*
- * |destination| specifies the rect in the parent's space where this surface will be drawn. The post
+ * \param destination Specifies the rect in the parent's space where this surface will be drawn. The post
* source rect bounds are scaled to fit the destination rect. The surface's destination rect is
* clipped by the bounds of its parent. The destination rect's width and height must be > 0.
*
- * |transform| the transform applied after the source rect is applied to the buffer. This parameter
+ * \param transform The transform applied after the source rect is applied to the buffer. This parameter
* should be set to 0 for no transform. To specify a transfrom use the NATIVE_WINDOW_TRANSFORM_*
* enum.
*
@@ -311,8 +364,56 @@
const ARect& destination, int32_t transform)
__INTRODUCED_IN(29);
+/**
+ * Bounds the surface and its children to the bounds specified. The crop and buffer size will be
+ * used to determine the bounds of the surface. If no crop is specified and the surface has no
+ * buffer, the surface bounds is only constrained by the size of its parent bounds.
+ *
+ * \param crop The bounds of the crop to apply.
+ *
+ * Available since API level 31.
+ */
+void ASurfaceTransaction_setCrop(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control, const ARect& crop)
+ __INTRODUCED_IN(31);
-/* Parameter for ASurfaceTransaction_setBufferTransparency */
+/**
+ * Specifies the position in the parent's space where the surface will be drawn.
+ *
+ * \param x The x position to render the surface.
+ * \param y The y position to render the surface.
+ *
+ * Available since API level 31.
+ */
+void ASurfaceTransaction_setPosition(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control, int32_t x, int32_t y)
+ __INTRODUCED_IN(31);
+
+/**
+ * \param transform The transform applied after the source rect is applied to the buffer. This
+ * parameter should be set to 0 for no transform. To specify a transform use the
+ * NATIVE_WINDOW_TRANSFORM_* enum.
+ *
+ * Available since API level 31.
+ */
+void ASurfaceTransaction_setBufferTransform(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control, int32_t transform)
+ __INTRODUCED_IN(31);
+
+/**
+ * Sets an x and y scale of a surface with (0, 0) as the centerpoint of the scale.
+ *
+ * \param xScale The scale in the x direction. Must be greater than 0.
+ * \param yScale The scale in the y direction. Must be greater than 0.
+ *
+ * Available since API level 31.
+ */
+void ASurfaceTransaction_setScale(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control, float xScale, float yScale)
+ __INTRODUCED_IN(31);
+/**
+ * Parameter for ASurfaceTransaction_setBufferTransparency().
+ */
enum {
ASURFACE_TRANSACTION_TRANSPARENCY_TRANSPARENT = 0,
ASURFACE_TRANSACTION_TRANSPARENCY_TRANSLUCENT = 1,
@@ -358,7 +459,7 @@
/**
* Sets the alpha for the buffer. It uses a premultiplied blending.
*
- * The |alpha| must be between 0.0 and 1.0.
+ * The \a alpha must be between 0.0 and 1.0.
*
* Available since API level 29.
*/
@@ -377,10 +478,10 @@
ASurfaceControl* surface_control, ADataSpace data_space)
__INTRODUCED_IN(29);
-/*
+/**
* SMPTE ST 2086 "Mastering Display Color Volume" static metadata
*
- * When |metadata| is set to null, the framework does not use any smpte2086 metadata when rendering
+ * When \a metadata is set to null, the framework does not use any smpte2086 metadata when rendering
* the surface's buffer.
*
* Available since API level 29.
@@ -390,10 +491,10 @@
struct AHdrMetadata_smpte2086* metadata)
__INTRODUCED_IN(29);
-/*
+/**
* Sets the CTA 861.3 "HDR Static Metadata Extension" static metadata on a surface.
*
- * When |metadata| is set to null, the framework does not use any cta861.3 metadata when rendering
+ * When \a metadata is set to null, the framework does not use any cta861.3 metadata when rendering
* the surface's buffer.
*
* Available since API level 29.
@@ -404,7 +505,19 @@
__INTRODUCED_IN(29);
/**
- * Sets the intended frame rate for |surface_control|.
+ * Same as ASurfaceTransaction_setFrameRateWithChangeStrategy(transaction, surface_control,
+ * frameRate, compatibility, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS).
+ *
+ * See ASurfaceTransaction_setFrameRateWithChangeStrategy().
+ *
+ * Available since API level 30.
+ */
+void ASurfaceTransaction_setFrameRate(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control, float frameRate,
+ int8_t compatibility) __INTRODUCED_IN(30);
+
+/**
+ * Sets the intended frame rate for \a surface_control.
*
* On devices that are capable of running the display at different refresh rates, the system may
* choose a display refresh rate to better match this surface's frame rate. Usage of this API won't
@@ -413,22 +526,59 @@
* callback timings, and changes to the time interval at which the system releases buffers back to
* the application.
*
- * |frameRate| is the intended frame rate of this surface, in frames per second. 0 is a special
+ * \param frameRate is the intended frame rate of this surface, in frames per second. 0 is a special
* value that indicates the app will accept the system's choice for the display frame rate, which is
* the default behavior if this function isn't called. The frameRate param does <em>not</em> need to
* be a valid refresh rate for this device's display - e.g., it's fine to pass 30fps to a device
* that can only run the display at 60fps.
*
- * |compatibility| The frame rate compatibility of this surface. The compatibility value may
+ * \param compatibility The frame rate compatibility of this surface. The compatibility value may
* influence the system's choice of display frame rate. To specify a compatibility use the
- * ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* enum.
+ * ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* enum. This parameter is ignored when frameRate is 0.
*
- * Available since API level 30.
+ * \param changeFrameRateStrategy Whether display refresh rate transitions caused by this
+ * surface should be seamless. A seamless transition is one that doesn't have any visual
+ * interruptions, such as a black screen for a second or two. See the
+ * ANATIVEWINDOW_CHANGE_FRAME_RATE_* values. This parameter is ignored when frameRate is 0.
+ *
+ * Available since API level 31.
*/
-void ASurfaceTransaction_setFrameRate(ASurfaceTransaction* transaction,
+void ASurfaceTransaction_setFrameRateWithChangeStrategy(ASurfaceTransaction* transaction,
ASurfaceControl* surface_control, float frameRate,
- int8_t compatibility) __INTRODUCED_IN(30);
+ int8_t compatibility, int8_t changeFrameRateStrategy)
+ __INTRODUCED_IN(31);
+
+/**
+ * Indicate whether to enable backpressure for buffer submission to a given SurfaceControl.
+ *
+ * By default backpressure is disabled, which means submitting a buffer prior to receiving
+ * a callback for the previous buffer could lead to that buffer being "dropped". In cases
+ * where you are selecting for latency, this may be a desirable behavior! We had a new buffer
+ * ready, why shouldn't we show it?
+ *
+ * When back pressure is enabled, each buffer will be required to be presented
+ * before it is released and the callback delivered
+ * (absent the whole SurfaceControl being removed).
+ *
+ * Most apps are likely to have some sort of backpressure internally, e.g. you are
+ * waiting on the callback from frame N-2 before starting frame N. In high refresh
+ * rate scenarios there may not be much time between SurfaceFlinger completing frame
+ * N-1 (and therefore releasing buffer N-2) and beginning frame N. This means
+ * your app may not have enough time to respond in the callback. Using this flag
+ * and pushing buffers earlier for server side queuing will be advantageous
+ * in such cases.
+ *
+ * \param transaction The transaction in which to make the change.
+ * \param surface_control The ASurfaceControl on which to control buffer backpressure behavior.
+ * \param enableBackPressure Whether to enable back pressure.
+ */
+void ASurfaceTransaction_setEnableBackPressure(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control,
+ bool enableBackPressure)
+ __INTRODUCED_IN(31);
__END_DECLS
#endif // ANDROID_SURFACE_CONTROL_H
+
+/** @} */
diff --git a/include/android/surface_texture.h b/include/android/surface_texture.h
index b227b32..9ae211e 100644
--- a/include/android/surface_texture.h
+++ b/include/android/surface_texture.h
@@ -86,8 +86,8 @@
/**
* Attach the SurfaceTexture to the OpenGL ES context that is current on the calling thread. A
* new OpenGL ES texture object is created and populated with the SurfaceTexture image frame
- * that was current at the time of the last call to {@link #detachFromGLContext}. This new
- * texture is bound to the GL_TEXTURE_EXTERNAL_OES texture target.
+ * that was current at the time of the last call to {@link ASurfaceTexture_detachFromGLContext}.
+ * This new texture is bound to the GL_TEXTURE_EXTERNAL_OES texture target.
*
* This can be used to access the SurfaceTexture image contents from multiple OpenGL ES
* contexts. Note, however, that the image contents are only accessible from one OpenGL ES
@@ -106,8 +106,8 @@
* Detach the SurfaceTexture from the OpenGL ES context that owns the OpenGL ES texture object.
* This call must be made with the OpenGL ES context current on the calling thread. The OpenGL
* ES texture object will be deleted as a result of this call. After calling this method all
- * calls to {@link #updateTexImage} will fail until a successful call to {@link #attachToGLContext}
- * is made.
+ * calls to {@link ASurfaceTexture_updateTexImage} will fail until a successful call to
+ * {@link ASurfaceTexture_attachToGLContext} is made.
*
* This can be used to access the SurfaceTexture image contents from multiple OpenGL ES
* contexts. Note, however, that the image contents are only accessible from one OpenGL ES
@@ -176,3 +176,5 @@
__END_DECLS
#endif /* ANDROID_NATIVE_SURFACE_TEXTURE_H */
+
+/** @} */
diff --git a/include/android/surface_texture_jni.h b/include/android/surface_texture_jni.h
index e40686d..8448d8c 100644
--- a/include/android/surface_texture_jni.h
+++ b/include/android/surface_texture_jni.h
@@ -53,3 +53,5 @@
__END_DECLS
#endif /* ANDROID_NATIVE_SURFACE_TEXTURE_JNI_H */
+
+/** @} */
diff --git a/include/android/thermal.h b/include/android/thermal.h
index 6ec68d9..32580ba 100644
--- a/include/android/thermal.h
+++ b/include/android/thermal.h
@@ -60,6 +60,10 @@
extern "C" {
#endif
+/**
+ * Thermal status used in function {@link AThermal_getCurrentThermalStatus} and
+ * {@link AThermal_StatusCallback}.
+ */
enum AThermalStatus {
/** Error in thermal status. */
ATHERMAL_STATUS_ERROR = -1,
@@ -177,6 +181,47 @@
int AThermal_unregisterThermalStatusListener(AThermalManager *manager,
AThermal_StatusCallback callback, void *data) __INTRODUCED_IN(30);
+/**
+ * Provides an estimate of how much thermal headroom the device currently has before
+ * hitting severe throttling.
+ *
+ * Note that this only attempts to track the headroom of slow-moving sensors, such as
+ * the skin temperature sensor. This means that there is no benefit to calling this function
+ * more frequently than about once per second, and attempted to call significantly
+ * more frequently may result in the function returning {@code NaN}.
+ *
+ * In addition, in order to be able to provide an accurate forecast, the system does
+ * not attempt to forecast until it has multiple temperature samples from which to
+ * extrapolate. This should only take a few seconds from the time of the first call,
+ * but during this time, no forecasting will occur, and the current headroom will be
+ * returned regardless of the value of {@code forecastSeconds}.
+ *
+ * The value returned is a non-negative float that represents how much of the thermal envelope
+ * is in use (or is forecasted to be in use). A value of 1.0 indicates that the device is
+ * (or will be) throttled at {@link #ATHERMAL_STATUS_SEVERE}. Such throttling can affect the
+ * CPU, GPU, and other subsystems. Values may exceed 1.0, but there is no implied mapping
+ * to specific thermal levels beyond that point. This means that values greater than 1.0
+ * may correspond to {@link #ATHERMAL_STATUS_SEVERE}, but may also represent heavier throttling.
+ *
+ * A value of 0.0 corresponds to a fixed distance from 1.0, but does not correspond to any
+ * particular thermal status or temperature. Values on (0.0, 1.0] may be expected to scale
+ * linearly with temperature, though temperature changes over time are typically not linear.
+ * Negative values will be clamped to 0.0 before returning.
+ *
+ * Available since API level 31.
+ *
+ * @param manager The manager instance to use.
+ * Acquired via {@link AThermal_acquireManager}.
+ * @param forecastSeconds how many seconds into the future to forecast. Given that device
+ * conditions may change at any time, forecasts from further in the
+ * future will likely be less accurate than forecasts in the near future.
+ * @return a value greater than equal to 0.0, where 1.0 indicates the SEVERE throttling threshold,
+ * as described above. Returns NaN if the device does not support this functionality or
+ * if this function is called significantly faster than once per second.
+ */
+float AThermal_getThermalHeadroom(AThermalManager *manager,
+ int forecastSeconds) __INTRODUCED_IN(31);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/android/trace.h b/include/android/trace.h
index dcefffb..d11158b 100644
--- a/include/android/trace.h
+++ b/include/android/trace.h
@@ -91,7 +91,7 @@
*
* Available since API level 29.
*
- * \param methodName The method name to appear in the trace.
+ * \param sectionName The method name to appear in the trace.
* \param cookie Unique identifier for distinguishing simultaneous events
*/
void ATrace_endAsyncSection(const char* sectionName, int32_t cookie) __INTRODUCED_IN(29);
diff --git a/include/android/window.h b/include/android/window.h
index 436bf3a..c144864 100644
--- a/include/android/window.h
+++ b/include/android/window.h
@@ -103,8 +103,9 @@
* bar) while this window is displayed. This allows the window to
* use the entire display space for itself -- the status bar will
* be hidden when an app window with this flag set is on the top
- * layer. A fullscreen window will ignore a value of {@link
- * AWINDOW_SOFT_INPUT_ADJUST_RESIZE}; the window will stay
+ * layer. A fullscreen window will ignore a value of
+ * <a href="/reference/android/view/WindowManager.LayoutParams#SOFT_INPUT_ADJUST_RESIZE">
+ * SOFT_INPUT_ADJUST_RESIZE</a>; the window will stay
* fullscreen and will not resize.
*/
AWINDOW_FLAG_FULLSCREEN = 0x00000400,
diff --git a/include/attestation/HmacKeyManager.h b/include/attestation/HmacKeyManager.h
new file mode 100644
index 0000000..571a361
--- /dev/null
+++ b/include/attestation/HmacKeyManager.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 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 <array>
+
+namespace android {
+/**
+ * Invalid value of HMAC - SHA256. Any events with this HMAC value will be marked as not verified.
+ */
+constexpr std::array<uint8_t, 32> INVALID_HMAC = {0};
+
+class HmacKeyManager {
+public:
+ HmacKeyManager();
+ std::array<uint8_t, 32> sign(const uint8_t* data, size_t size) const;
+private:
+ const std::array<uint8_t, 128> mHmacKey;
+};
+} // namespace android
\ No newline at end of file
diff --git a/include/audiomanager/AudioManager.h b/include/audiomanager/AudioManager.h
index 639df7a..4aa2f60 100644
--- a/include/audiomanager/AudioManager.h
+++ b/include/audiomanager/AudioManager.h
@@ -37,6 +37,7 @@
PLAYER_STATE_STARTED = 2,
PLAYER_STATE_PAUSED = 3,
PLAYER_STATE_STOPPED = 4,
+ PLAYER_UPDATE_DEVICE_ID = 5,
} player_state_t;
// must be kept in sync with definitions in AudioManager.java
diff --git a/include/audiomanager/IAudioManager.h b/include/audiomanager/IAudioManager.h
index 2f5ccb8..426e10c 100644
--- a/include/audiomanager/IAudioManager.h
+++ b/include/audiomanager/IAudioManager.h
@@ -39,6 +39,7 @@
TRACK_RECORDER = IBinder::FIRST_CALL_TRANSACTION + 4,
RECORDER_EVENT = IBinder::FIRST_CALL_TRANSACTION + 5,
RELEASE_RECORDER = IBinder::FIRST_CALL_TRANSACTION + 6,
+ PLAYER_SESSION_ID = IBinder::FIRST_CALL_TRANSACTION + 7,
};
DECLARE_META_INTERFACE(AudioManager)
@@ -46,14 +47,17 @@
// The parcels created by these methods must be kept in sync with the
// corresponding methods from IAudioService.aidl and objects it imports.
virtual audio_unique_id_t trackPlayer(player_type_t playerType, audio_usage_t usage,
- audio_content_type_t content, const sp<IBinder>& player) = 0;
+ audio_content_type_t content, const sp<IBinder>& player,
+ audio_session_t sessionId) = 0;
/*oneway*/ virtual status_t playerAttributes(audio_unique_id_t piid, audio_usage_t usage,
audio_content_type_t content)= 0;
- /*oneway*/ virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event) = 0;
+ /*oneway*/ virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event,
+ audio_port_handle_t deviceId) = 0;
/*oneway*/ virtual status_t releasePlayer(audio_unique_id_t piid) = 0;
virtual audio_unique_id_t trackRecorder(const sp<IBinder>& recorder) = 0;
/*oneway*/ virtual status_t recorderEvent(audio_unique_id_t riid, recorder_state_t event) = 0;
/*oneway*/ virtual status_t releaseRecorder(audio_unique_id_t riid) = 0;
+ /*oneway*/ virtual status_t playerSessionId(audio_unique_id_t piid, audio_session_t sessionId) = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/ftl/.clang-format b/include/ftl/.clang-format
new file mode 120000
index 0000000..86b1593
--- /dev/null
+++ b/include/ftl/.clang-format
@@ -0,0 +1 @@
+../../../../build/soong/scripts/system-clang-format-2
\ No newline at end of file
diff --git a/include/ftl/array_traits.h b/include/ftl/array_traits.h
new file mode 100644
index 0000000..1265fa1
--- /dev/null
+++ b/include/ftl/array_traits.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <iterator>
+#include <new>
+#include <type_traits>
+
+#define FTL_ARRAY_TRAIT(T, U) using U = typename ArrayTraits<T>::U
+
+namespace android::ftl {
+
+template <typename T>
+struct ArrayTraits {
+ using value_type = T;
+ using size_type = std::size_t;
+ using difference_type = std::ptrdiff_t;
+
+ using pointer = value_type*;
+ using reference = value_type&;
+ using iterator = pointer;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+
+ using const_pointer = const value_type*;
+ using const_reference = const value_type&;
+ using const_iterator = const_pointer;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+ template <typename... Args>
+ static pointer construct_at(const_iterator it, Args&&... args) {
+ void* const ptr = const_cast<void*>(static_cast<const void*>(it));
+ if constexpr (std::is_constructible_v<value_type, Args...>) {
+ // TODO: Replace with std::construct_at in C++20.
+ return new (ptr) value_type(std::forward<Args>(args)...);
+ } else {
+ // Fall back to list initialization.
+ return new (ptr) value_type{std::forward<Args>(args)...};
+ }
+ }
+};
+
+// CRTP mixin to define iterator functions in terms of non-const Self::begin and Self::end.
+template <typename Self, typename T>
+class ArrayIterators {
+ FTL_ARRAY_TRAIT(T, size_type);
+
+ FTL_ARRAY_TRAIT(T, reference);
+ FTL_ARRAY_TRAIT(T, iterator);
+ FTL_ARRAY_TRAIT(T, reverse_iterator);
+
+ FTL_ARRAY_TRAIT(T, const_reference);
+ FTL_ARRAY_TRAIT(T, const_iterator);
+ FTL_ARRAY_TRAIT(T, const_reverse_iterator);
+
+ Self& self() const { return *const_cast<Self*>(static_cast<const Self*>(this)); }
+
+ public:
+ const_iterator begin() const { return cbegin(); }
+ const_iterator cbegin() const { return self().begin(); }
+
+ const_iterator end() const { return cend(); }
+ const_iterator cend() const { return self().end(); }
+
+ reverse_iterator rbegin() { return std::make_reverse_iterator(self().end()); }
+ const_reverse_iterator rbegin() const { return crbegin(); }
+ const_reverse_iterator crbegin() const { return self().rbegin(); }
+
+ reverse_iterator rend() { return std::make_reverse_iterator(self().begin()); }
+ const_reverse_iterator rend() const { return crend(); }
+ const_reverse_iterator crend() const { return self().rend(); }
+
+ iterator last() { return self().end() - 1; }
+ const_iterator last() const { return self().last(); }
+
+ reference front() { return *self().begin(); }
+ const_reference front() const { return self().front(); }
+
+ reference back() { return *last(); }
+ const_reference back() const { return self().back(); }
+
+ reference operator[](size_type i) { return *(self().begin() + i); }
+ const_reference operator[](size_type i) const { return self()[i]; }
+};
+
+// Mixin to define comparison operators for an array-like template.
+// TODO: Replace with operator<=> in C++20.
+template <template <typename, std::size_t> class Array>
+struct ArrayComparators {
+ template <typename T, std::size_t N, std::size_t M>
+ friend bool operator==(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+ return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
+ }
+
+ template <typename T, std::size_t N, std::size_t M>
+ friend bool operator<(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+ return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
+ }
+
+ template <typename T, std::size_t N, std::size_t M>
+ friend bool operator>(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+ return rhs < lhs;
+ }
+
+ template <typename T, std::size_t N, std::size_t M>
+ friend bool operator!=(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+ return !(lhs == rhs);
+ }
+
+ template <typename T, std::size_t N, std::size_t M>
+ friend bool operator>=(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+ return !(lhs < rhs);
+ }
+
+ template <typename T, std::size_t N, std::size_t M>
+ friend bool operator<=(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+ return !(lhs > rhs);
+ }
+};
+
+} // namespace android::ftl
diff --git a/include/ftl/future.h b/include/ftl/future.h
new file mode 100644
index 0000000..dd6358f
--- /dev/null
+++ b/include/ftl/future.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <future>
+#include <type_traits>
+#include <utility>
+
+namespace android::ftl {
+
+// Creates a future that defers a function call until its result is queried.
+//
+// auto future = ftl::defer([](int x) { return x + 1; }, 99);
+// assert(future.get() == 100);
+//
+template <typename F, typename... Args>
+inline auto defer(F&& f, Args&&... args) {
+ return std::async(std::launch::deferred, std::forward<F>(f), std::forward<Args>(args)...);
+}
+
+// Creates a future that wraps a value.
+//
+// auto future = ftl::yield(42);
+// assert(future.get() == 42);
+//
+// auto ptr = std::make_unique<char>('!');
+// auto future = ftl::yield(std::move(ptr));
+// assert(*future.get() == '!');
+//
+template <typename T>
+inline std::future<T> yield(T&& v) {
+ return defer([](T&& v) { return std::forward<T>(v); }, std::forward<T>(v));
+}
+
+namespace details {
+
+template <typename T>
+struct future_result {
+ using type = T;
+};
+
+template <typename T>
+struct future_result<std::future<T>> {
+ using type = T;
+};
+
+template <typename T>
+using future_result_t = typename future_result<T>::type;
+
+// Attaches a continuation to a future. The continuation is a function that maps T to either R or
+// std::future<R>. In the former case, the chain wraps the result in a future as if by ftl::yield.
+//
+// auto future = ftl::yield(123);
+// std::future<char> futures[] = {ftl::yield('a'), ftl::yield('b')};
+//
+// std::future<char> chain =
+// ftl::chain(std::move(future))
+// .then([](int x) { return static_cast<std::size_t>(x % 2); })
+// .then([&futures](std::size_t i) { return std::move(futures[i]); });
+//
+// assert(chain.get() == 'b');
+//
+template <typename T>
+struct Chain {
+ // Implicit conversion.
+ Chain(std::future<T>&& f) : future(std::move(f)) {}
+ operator std::future<T>&&() && { return std::move(future); }
+
+ T get() && { return future.get(); }
+
+ template <typename F, typename R = std::invoke_result_t<F, T>>
+ auto then(F&& op) && -> Chain<future_result_t<R>> {
+ return defer(
+ [](auto&& f, F&& op) {
+ R r = op(f.get());
+ if constexpr (std::is_same_v<R, future_result_t<R>>) {
+ return r;
+ } else {
+ return r.get();
+ }
+ },
+ std::move(future), std::forward<F>(op));
+ }
+
+ std::future<T> future;
+};
+
+} // namespace details
+
+template <typename T>
+inline auto chain(std::future<T>&& f) -> details::Chain<T> {
+ return std::move(f);
+}
+
+} // namespace android::ftl
diff --git a/include/ftl/initializer_list.h b/include/ftl/initializer_list.h
new file mode 100644
index 0000000..769c09f
--- /dev/null
+++ b/include/ftl/initializer_list.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <tuple>
+#include <utility>
+
+namespace android::ftl {
+
+// Compile-time counterpart of std::initializer_list<T> that stores per-element constructor
+// arguments with heterogeneous types. For a container with elements of type T, given Sizes
+// (S0, S1, ..., SN), N elements are initialized: the first element is initialized with the
+// first S0 arguments, the second element is initialized with the next S1 arguments, and so
+// on. The list of Types (T0, ..., TM) is flattened, so M is equal to the sum of the Sizes.
+//
+// An InitializerList is created using ftl::init::list, and is consumed by constructors of
+// containers. The function call operator is overloaded such that arguments are accumulated
+// in a tuple with each successive call. For instance, the following calls initialize three
+// strings using different constructors, i.e. string literal, default, and count/character:
+//
+// ... = ftl::init::list<std::string>("abc")()(3u, '?');
+//
+// The following syntax is a shorthand for key-value pairs, where the first argument is the
+// key, and the rest construct the value. The types of the key and value are deduced if the
+// first pair contains exactly two arguments:
+//
+// ... = ftl::init::map<int, std::string>(-1, "abc")(-2)(-3, 3u, '?');
+//
+// ... = ftl::init::map(0, 'a')(1, 'b')(2, 'c');
+//
+// WARNING: The InitializerList returned by an ftl::init::list expression must be consumed
+// immediately, since temporary arguments are destroyed after the full expression. Storing
+// an InitializerList results in dangling references.
+//
+template <typename T, typename Sizes = std::index_sequence<>, typename... Types>
+struct InitializerList;
+
+template <typename T, std::size_t... Sizes, typename... Types>
+struct InitializerList<T, std::index_sequence<Sizes...>, Types...> {
+ // Creates a superset InitializerList by appending the number of arguments to Sizes, and
+ // expanding Types with forwarding references for each argument.
+ template <typename... Args>
+ [[nodiscard]] constexpr auto operator()(Args&&... args) && -> InitializerList<
+ T, std::index_sequence<Sizes..., sizeof...(Args)>, Types..., Args&&...> {
+ return {std::tuple_cat(std::move(tuple), std::forward_as_tuple(std::forward<Args>(args)...))};
+ }
+
+ // The temporary InitializerList returned by operator() is bound to an rvalue reference in
+ // container constructors, which extends the lifetime of any temporary arguments that this
+ // tuple refers to until the completion of the full expression containing the construction.
+ std::tuple<Types...> tuple;
+};
+
+template <typename K, typename V>
+struct KeyValue {};
+
+// Shorthand for key-value pairs that assigns the first argument to the key, and the rest to the
+// value. The specialization is on KeyValue rather than std::pair, so that ftl::init::list works
+// with the latter.
+template <typename K, typename V, std::size_t... Sizes, typename... Types>
+struct InitializerList<KeyValue<K, V>, std::index_sequence<Sizes...>, Types...> {
+ // Accumulate the three arguments to std::pair's piecewise constructor.
+ template <typename... Args>
+ [[nodiscard]] constexpr auto operator()(K&& k, Args&&... args) && -> InitializerList<
+ KeyValue<K, V>, std::index_sequence<Sizes..., 3>, Types..., std::piecewise_construct_t,
+ std::tuple<K&&>, std::tuple<Args&&...>> {
+ return {std::tuple_cat(
+ std::move(tuple),
+ std::forward_as_tuple(std::piecewise_construct, std::forward_as_tuple(std::forward<K>(k)),
+ std::forward_as_tuple(std::forward<Args>(args)...)))};
+ }
+
+ std::tuple<Types...> tuple;
+};
+
+namespace init {
+
+template <typename T, typename... Args>
+[[nodiscard]] constexpr auto list(Args&&... args) {
+ return InitializerList<T>{}(std::forward<Args>(args)...);
+}
+
+template <typename K, typename V, typename... Args>
+[[nodiscard]] constexpr auto map(Args&&... args) {
+ return list<KeyValue<K, V>>(std::forward<Args>(args)...);
+}
+
+template <typename K, typename V>
+[[nodiscard]] constexpr auto map(K&& k, V&& v) {
+ return list<KeyValue<K, V>>(std::forward<K>(k), std::forward<V>(v));
+}
+
+} // namespace init
+} // namespace android::ftl
diff --git a/include/ftl/small_map.h b/include/ftl/small_map.h
new file mode 100644
index 0000000..84c15eb
--- /dev/null
+++ b/include/ftl/small_map.h
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <ftl/initializer_list.h>
+#include <ftl/small_vector.h>
+
+#include <functional>
+#include <optional>
+#include <type_traits>
+#include <utility>
+
+namespace android::ftl {
+
+// Associative container with unique, unordered keys. Unlike std::unordered_map, key-value pairs are
+// stored in contiguous storage for cache efficiency. The map is allocated statically until its size
+// exceeds N, at which point mappings are relocated to dynamic memory.
+//
+// SmallMap<K, V, 0> unconditionally allocates on the heap.
+//
+// Example usage:
+//
+// ftl::SmallMap<int, std::string, 3> map;
+// assert(map.empty());
+// assert(!map.dynamic());
+//
+// map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
+// assert(map.size() == 3u);
+// assert(!map.dynamic());
+//
+// assert(map.contains(123));
+// assert(map.find(42, [](const std::string& s) { return s.size(); }) == 3u);
+//
+// const auto opt = map.find(-1);
+// assert(opt);
+//
+// std::string& ref = *opt;
+// assert(ref.empty());
+// ref = "xyz";
+//
+// assert(map == SmallMap(ftl::init::map(-1, "xyz")(42, "???")(123, "abc")));
+//
+template <typename K, typename V, std::size_t N>
+class SmallMap final {
+ using Map = SmallVector<std::pair<const K, V>, N>;
+
+ public:
+ using key_type = K;
+ using mapped_type = V;
+
+ using value_type = typename Map::value_type;
+ using size_type = typename Map::size_type;
+ using difference_type = typename Map::difference_type;
+
+ using reference = typename Map::reference;
+ using iterator = typename Map::iterator;
+
+ using const_reference = typename Map::const_reference;
+ using const_iterator = typename Map::const_iterator;
+
+ // Creates an empty map.
+ SmallMap() = default;
+
+ // Constructs at most N key-value pairs in place by forwarding per-pair constructor arguments.
+ // The template arguments K, V, and N are inferred using the deduction guide defined below.
+ // The syntax for listing pairs is as follows:
+ //
+ // ftl::SmallMap map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
+ //
+ // static_assert(std::is_same_v<decltype(map), ftl::SmallMap<int, std::string, 3>>);
+ // assert(map.size() == 3u);
+ // assert(map.contains(-1) && map.find(-1)->get().empty());
+ // assert(map.contains(42) && map.find(42)->get() == "???");
+ // assert(map.contains(123) && map.find(123)->get() == "abc");
+ //
+ // The types of the key and value are deduced if the first pair contains exactly two arguments:
+ //
+ // ftl::SmallMap map = ftl::init::map(0, 'a')(1, 'b')(2, 'c');
+ // static_assert(std::is_same_v<decltype(map), ftl::SmallMap<int, char, 3>>);
+ //
+ template <typename U, std::size_t... Sizes, typename... Types>
+ SmallMap(InitializerList<U, std::index_sequence<Sizes...>, Types...>&& list)
+ : map_(std::move(list)) {
+ // TODO: Enforce unique keys.
+ }
+
+ size_type max_size() const { return map_.max_size(); }
+ size_type size() const { return map_.size(); }
+ bool empty() const { return map_.empty(); }
+
+ // Returns whether the map is backed by static or dynamic storage.
+ bool dynamic() const { return map_.dynamic(); }
+
+ iterator begin() { return map_.begin(); }
+ const_iterator begin() const { return cbegin(); }
+ const_iterator cbegin() const { return map_.cbegin(); }
+
+ iterator end() { return map_.end(); }
+ const_iterator end() const { return cend(); }
+ const_iterator cend() const { return map_.cend(); }
+
+ // Returns whether a mapping exists for the given key.
+ bool contains(const key_type& key) const {
+ return find(key, [](const mapped_type&) {});
+ }
+
+ // Returns a reference to the value for the given key, or std::nullopt if the key was not found.
+ //
+ // ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
+ //
+ // const auto opt = map.find('c');
+ // assert(opt == 'C');
+ //
+ // char d = 'd';
+ // const auto ref = map.find('d').value_or(std::ref(d));
+ // ref.get() = 'D';
+ // assert(d == 'D');
+ //
+ auto find(const key_type& key) const -> std::optional<std::reference_wrapper<const mapped_type>> {
+ return find(key, [](const mapped_type& v) { return std::cref(v); });
+ }
+
+ auto find(const key_type& key) -> std::optional<std::reference_wrapper<mapped_type>> {
+ return find(key, [](mapped_type& v) { return std::ref(v); });
+ }
+
+ // Returns the result R of a unary operation F on (a constant or mutable reference to) the value
+ // for the given key, or std::nullopt if the key was not found. If F has a return type of void,
+ // then the Boolean result indicates whether the key was found.
+ //
+ // ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
+ //
+ // assert(map.find('c', [](char c) { return std::toupper(c); }) == 'Z');
+ // assert(map.find('c', [](char& c) { c = std::toupper(c); }));
+ //
+ template <typename F, typename R = std::invoke_result_t<F, const mapped_type&>>
+ auto find(const key_type& key, F f) const
+ -> std::conditional_t<std::is_void_v<R>, bool, std::optional<R>> {
+ for (auto& [k, v] : *this) {
+ if (k == key) {
+ if constexpr (std::is_void_v<R>) {
+ f(v);
+ return true;
+ } else {
+ return f(v);
+ }
+ }
+ }
+
+ return {};
+ }
+
+ template <typename F>
+ auto find(const key_type& key, F f) {
+ return std::as_const(*this).find(
+ key, [&f](const mapped_type& v) { return f(const_cast<mapped_type&>(v)); });
+ }
+
+ private:
+ Map map_;
+};
+
+// Deduction guide for in-place constructor.
+template <typename K, typename V, std::size_t... Sizes, typename... Types>
+SmallMap(InitializerList<KeyValue<K, V>, std::index_sequence<Sizes...>, Types...>&&)
+ -> SmallMap<K, V, sizeof...(Sizes)>;
+
+// Returns whether the key-value pairs of two maps are equal.
+template <typename K, typename V, std::size_t N, typename Q, typename W, std::size_t M>
+bool operator==(const SmallMap<K, V, N>& lhs, const SmallMap<Q, W, M>& rhs) {
+ if (lhs.size() != rhs.size()) return false;
+
+ for (const auto& [k, v] : lhs) {
+ const auto& lv = v;
+ if (!rhs.find(k, [&lv](const auto& rv) { return lv == rv; }).value_or(false)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// TODO: Remove in C++20.
+template <typename K, typename V, std::size_t N, typename Q, typename W, std::size_t M>
+inline bool operator!=(const SmallMap<K, V, N>& lhs, const SmallMap<Q, W, M>& rhs) {
+ return !(lhs == rhs);
+}
+
+} // namespace android::ftl
diff --git a/include/ftl/small_vector.h b/include/ftl/small_vector.h
new file mode 100644
index 0000000..cb0ae35
--- /dev/null
+++ b/include/ftl/small_vector.h
@@ -0,0 +1,387 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <ftl/array_traits.h>
+#include <ftl/static_vector.h>
+
+#include <algorithm>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+#include <variant>
+#include <vector>
+
+namespace android::ftl {
+
+template <typename>
+struct is_small_vector;
+
+// ftl::StaticVector that promotes to std::vector when full. SmallVector is a drop-in replacement
+// for std::vector with statically allocated storage for N elements, whose goal is to improve run
+// time by avoiding heap allocation and increasing probability of cache hits. The standard API is
+// augmented by an unstable_erase operation that does not preserve order, and a replace operation
+// that destructively emplaces.
+//
+// SmallVector<T, 0> is a specialization that thinly wraps std::vector.
+//
+// Example usage:
+//
+// ftl::SmallVector<char, 3> vector;
+// assert(vector.empty());
+// assert(!vector.dynamic());
+//
+// vector = {'a', 'b', 'c'};
+// assert(vector.size() == 3u);
+// assert(!vector.dynamic());
+//
+// vector.push_back('d');
+// assert(vector.dynamic());
+//
+// vector.unstable_erase(vector.begin());
+// assert(vector == (ftl::SmallVector{'d', 'b', 'c'}));
+//
+// vector.pop_back();
+// assert(vector.back() == 'b');
+// assert(vector.dynamic());
+//
+// const char array[] = "hi";
+// vector = ftl::SmallVector(array);
+// assert(vector == (ftl::SmallVector{'h', 'i', '\0'}));
+// assert(!vector.dynamic());
+//
+// ftl::SmallVector strings = ftl::init::list<std::string>("abc")("123456", 3u)(3u, '?');
+// assert(strings.size() == 3u);
+// assert(!strings.dynamic());
+//
+// assert(strings[0] == "abc");
+// assert(strings[1] == "123");
+// assert(strings[2] == "???");
+//
+template <typename T, std::size_t N>
+class SmallVector final : ArrayTraits<T>, ArrayComparators<SmallVector> {
+ using Static = StaticVector<T, N>;
+ using Dynamic = SmallVector<T, 0>;
+
+ // TODO: Replace with std::remove_cvref_t in C++20.
+ template <typename U>
+ using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<U>>;
+
+ public:
+ FTL_ARRAY_TRAIT(T, value_type);
+ FTL_ARRAY_TRAIT(T, size_type);
+ FTL_ARRAY_TRAIT(T, difference_type);
+
+ FTL_ARRAY_TRAIT(T, pointer);
+ FTL_ARRAY_TRAIT(T, reference);
+ FTL_ARRAY_TRAIT(T, iterator);
+ FTL_ARRAY_TRAIT(T, reverse_iterator);
+
+ FTL_ARRAY_TRAIT(T, const_pointer);
+ FTL_ARRAY_TRAIT(T, const_reference);
+ FTL_ARRAY_TRAIT(T, const_iterator);
+ FTL_ARRAY_TRAIT(T, const_reverse_iterator);
+
+ // Creates an empty vector.
+ SmallVector() = default;
+
+ // Constructs at most N elements. See StaticVector for underlying constructors.
+ template <typename Arg, typename... Args,
+ typename = std::enable_if_t<!is_small_vector<remove_cvref_t<Arg>>{}>>
+ SmallVector(Arg&& arg, Args&&... args)
+ : vector_(std::in_place_type<Static>, std::forward<Arg>(arg), std::forward<Args>(args)...) {}
+
+ // Copies at most N elements from a smaller convertible vector.
+ template <typename U, std::size_t M, typename = std::enable_if_t<M <= N>>
+ SmallVector(const SmallVector<U, M>& other)
+ : SmallVector(kIteratorRange, other.begin(), other.end()) {}
+
+ void swap(SmallVector& other) { vector_.swap(other.vector_); }
+
+ // Returns whether the vector is backed by static or dynamic storage.
+ bool dynamic() const { return std::holds_alternative<Dynamic>(vector_); }
+
+ // Avoid std::visit as it generates a dispatch table.
+#define DISPATCH(T, F, ...) \
+ T F() __VA_ARGS__ { \
+ return dynamic() ? std::get<Dynamic>(vector_).F() : std::get<Static>(vector_).F(); \
+ }
+
+ DISPATCH(size_type, max_size, const)
+ DISPATCH(size_type, size, const)
+ DISPATCH(bool, empty, const)
+
+ // noexcept to suppress warning about zero variadic macro arguments.
+ DISPATCH(iterator, begin, noexcept)
+ DISPATCH(const_iterator, begin, const)
+ DISPATCH(const_iterator, cbegin, const)
+
+ DISPATCH(iterator, end, noexcept)
+ DISPATCH(const_iterator, end, const)
+ DISPATCH(const_iterator, cend, const)
+
+ DISPATCH(reverse_iterator, rbegin, noexcept)
+ DISPATCH(const_reverse_iterator, rbegin, const)
+ DISPATCH(const_reverse_iterator, crbegin, const)
+
+ DISPATCH(reverse_iterator, rend, noexcept)
+ DISPATCH(const_reverse_iterator, rend, const)
+ DISPATCH(const_reverse_iterator, crend, const)
+
+ DISPATCH(iterator, last, noexcept)
+ DISPATCH(const_iterator, last, const)
+
+ DISPATCH(reference, front, noexcept)
+ DISPATCH(const_reference, front, const)
+
+ DISPATCH(reference, back, noexcept)
+ DISPATCH(const_reference, back, const)
+
+#undef DISPATCH
+
+ reference operator[](size_type i) {
+ return dynamic() ? std::get<Dynamic>(vector_)[i] : std::get<Static>(vector_)[i];
+ }
+
+ const_reference operator[](size_type i) const { return const_cast<SmallVector&>(*this)[i]; }
+
+ // Replaces an element, and returns a reference to it. The iterator must be dereferenceable, so
+ // replacing at end() is erroneous.
+ //
+ // The element is emplaced via move constructor, so type T does not need to define copy/move
+ // assignment, e.g. its data members may be const.
+ //
+ // The arguments may directly or indirectly refer to the element being replaced.
+ //
+ // Iterators to the replaced element point to its replacement, and others remain valid.
+ //
+ template <typename... Args>
+ reference replace(const_iterator it, Args&&... args) {
+ if (dynamic()) {
+ return std::get<Dynamic>(vector_).replace(it, std::forward<Args>(args)...);
+ } else {
+ return std::get<Static>(vector_).replace(it, std::forward<Args>(args)...);
+ }
+ }
+
+ // Appends an element, and returns a reference to it.
+ //
+ // If the vector reaches its static or dynamic capacity, then all iterators are invalidated.
+ // Otherwise, only the end() iterator is invalidated.
+ //
+ template <typename... Args>
+ reference emplace_back(Args&&... args) {
+ constexpr auto kInsertStatic = &Static::template emplace_back<Args...>;
+ constexpr auto kInsertDynamic = &Dynamic::template emplace_back<Args...>;
+ return *insert<kInsertStatic, kInsertDynamic>(std::forward<Args>(args)...);
+ }
+
+ // Appends an element.
+ //
+ // If the vector reaches its static or dynamic capacity, then all iterators are invalidated.
+ // Otherwise, only the end() iterator is invalidated.
+ //
+ void push_back(const value_type& v) {
+ constexpr auto kInsertStatic =
+ static_cast<bool (Static::*)(const value_type&)>(&Static::push_back);
+ constexpr auto kInsertDynamic =
+ static_cast<bool (Dynamic::*)(const value_type&)>(&Dynamic::push_back);
+ insert<kInsertStatic, kInsertDynamic>(v);
+ }
+
+ void push_back(value_type&& v) {
+ constexpr auto kInsertStatic = static_cast<bool (Static::*)(value_type &&)>(&Static::push_back);
+ constexpr auto kInsertDynamic =
+ static_cast<bool (Dynamic::*)(value_type &&)>(&Dynamic::push_back);
+ insert<kInsertStatic, kInsertDynamic>(std::move(v));
+ }
+
+ // Removes the last element. The vector must not be empty, or the call is erroneous.
+ //
+ // The last() and end() iterators are invalidated.
+ //
+ void pop_back() {
+ if (dynamic()) {
+ std::get<Dynamic>(vector_).pop_back();
+ } else {
+ std::get<Static>(vector_).pop_back();
+ }
+ }
+
+ // Erases an element, but does not preserve order. Rather than shifting subsequent elements,
+ // this moves the last element to the slot of the erased element.
+ //
+ // The last() and end() iterators, as well as those to the erased element, are invalidated.
+ //
+ void unstable_erase(iterator it) {
+ if (dynamic()) {
+ std::get<Dynamic>(vector_).unstable_erase(it);
+ } else {
+ std::get<Static>(vector_).unstable_erase(it);
+ }
+ }
+
+ private:
+ template <auto InsertStatic, auto InsertDynamic, typename... Args>
+ auto insert(Args&&... args) {
+ if (Dynamic* const vector = std::get_if<Dynamic>(&vector_)) {
+ return (vector->*InsertDynamic)(std::forward<Args>(args)...);
+ }
+
+ auto& vector = std::get<Static>(vector_);
+ if (vector.full()) {
+ return (promote(vector).*InsertDynamic)(std::forward<Args>(args)...);
+ } else {
+ return (vector.*InsertStatic)(std::forward<Args>(args)...);
+ }
+ }
+
+ Dynamic& promote(Static& static_vector) {
+ assert(static_vector.full());
+
+ // Allocate double capacity to reduce probability of reallocation.
+ Dynamic vector;
+ vector.reserve(Static::max_size() * 2);
+ std::move(static_vector.begin(), static_vector.end(), std::back_inserter(vector));
+
+ return vector_.template emplace<Dynamic>(std::move(vector));
+ }
+
+ std::variant<Static, Dynamic> vector_;
+};
+
+// Partial specialization without static storage.
+template <typename T>
+class SmallVector<T, 0> final : ArrayTraits<T>,
+ ArrayIterators<SmallVector<T, 0>, T>,
+ std::vector<T> {
+ using ArrayTraits<T>::construct_at;
+
+ using Iter = ArrayIterators<SmallVector, T>;
+ using Impl = std::vector<T>;
+
+ friend Iter;
+
+ public:
+ FTL_ARRAY_TRAIT(T, value_type);
+ FTL_ARRAY_TRAIT(T, size_type);
+ FTL_ARRAY_TRAIT(T, difference_type);
+
+ FTL_ARRAY_TRAIT(T, pointer);
+ FTL_ARRAY_TRAIT(T, reference);
+ FTL_ARRAY_TRAIT(T, iterator);
+ FTL_ARRAY_TRAIT(T, reverse_iterator);
+
+ FTL_ARRAY_TRAIT(T, const_pointer);
+ FTL_ARRAY_TRAIT(T, const_reference);
+ FTL_ARRAY_TRAIT(T, const_iterator);
+ FTL_ARRAY_TRAIT(T, const_reverse_iterator);
+
+ using Impl::Impl;
+
+ using Impl::empty;
+ using Impl::max_size;
+ using Impl::size;
+
+ using Impl::reserve;
+
+ // std::vector iterators are not necessarily raw pointers.
+ iterator begin() { return Impl::data(); }
+ iterator end() { return Impl::data() + size(); }
+
+ using Iter::begin;
+ using Iter::end;
+
+ using Iter::cbegin;
+ using Iter::cend;
+
+ using Iter::rbegin;
+ using Iter::rend;
+
+ using Iter::crbegin;
+ using Iter::crend;
+
+ using Iter::last;
+
+ using Iter::back;
+ using Iter::front;
+
+ using Iter::operator[];
+
+ template <typename... Args>
+ reference replace(const_iterator it, Args&&... args) {
+ value_type element{std::forward<Args>(args)...};
+ std::destroy_at(it);
+ // This is only safe because exceptions are disabled.
+ return *construct_at(it, std::move(element));
+ }
+
+ template <typename... Args>
+ iterator emplace_back(Args&&... args) {
+ return &Impl::emplace_back(std::forward<Args>(args)...);
+ }
+
+ bool push_back(const value_type& v) {
+ Impl::push_back(v);
+ return true;
+ }
+
+ bool push_back(value_type&& v) {
+ Impl::push_back(std::move(v));
+ return true;
+ }
+
+ using Impl::pop_back;
+
+ void unstable_erase(iterator it) {
+ if (it != last()) std::iter_swap(it, last());
+ pop_back();
+ }
+
+ void swap(SmallVector& other) { Impl::swap(other); }
+};
+
+template <typename>
+struct is_small_vector : std::false_type {};
+
+template <typename T, std::size_t N>
+struct is_small_vector<SmallVector<T, N>> : std::true_type {};
+
+// Deduction guide for array constructor.
+template <typename T, std::size_t N>
+SmallVector(T (&)[N]) -> SmallVector<std::remove_cv_t<T>, N>;
+
+// Deduction guide for variadic constructor.
+template <typename T, typename... Us, typename V = std::decay_t<T>,
+ typename = std::enable_if_t<(std::is_constructible_v<V, Us> && ...)>>
+SmallVector(T&&, Us&&...) -> SmallVector<V, 1 + sizeof...(Us)>;
+
+// Deduction guide for in-place constructor.
+template <typename T, std::size_t... Sizes, typename... Types>
+SmallVector(InitializerList<T, std::index_sequence<Sizes...>, Types...>&&)
+ -> SmallVector<T, sizeof...(Sizes)>;
+
+// Deduction guide for StaticVector conversion.
+template <typename T, std::size_t N>
+SmallVector(StaticVector<T, N>&&) -> SmallVector<T, N>;
+
+template <typename T, std::size_t N>
+inline void swap(SmallVector<T, N>& lhs, SmallVector<T, N>& rhs) {
+ lhs.swap(rhs);
+}
+
+} // namespace android::ftl
diff --git a/include/ftl/static_vector.h b/include/ftl/static_vector.h
new file mode 100644
index 0000000..96a1ae8
--- /dev/null
+++ b/include/ftl/static_vector.h
@@ -0,0 +1,394 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <ftl/array_traits.h>
+#include <ftl/initializer_list.h>
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+namespace android::ftl {
+
+constexpr struct IteratorRangeTag {
+} kIteratorRange;
+
+// Fixed-capacity, statically allocated counterpart of std::vector. Like std::array, StaticVector
+// allocates contiguous storage for N elements of type T at compile time, but stores at most (rather
+// than exactly) N elements. Unlike std::array, its default constructor does not require T to have a
+// default constructor, since elements are constructed in place as the vector grows. Operations that
+// insert an element (emplace_back, push_back, etc.) fail when the vector is full. The API otherwise
+// adheres to standard containers, except the unstable_erase operation that does not preserve order,
+// and the replace operation that destructively emplaces.
+//
+// StaticVector<T, 1> is analogous to an iterable std::optional.
+// StaticVector<T, 0> is an error.
+//
+// Example usage:
+//
+// ftl::StaticVector<char, 3> vector;
+// assert(vector.empty());
+//
+// vector = {'a', 'b'};
+// assert(vector.size() == 2u);
+//
+// vector.push_back('c');
+// assert(vector.full());
+//
+// assert(!vector.push_back('d'));
+// assert(vector.size() == 3u);
+//
+// vector.unstable_erase(vector.begin());
+// assert(vector == (ftl::StaticVector{'c', 'b'}));
+//
+// vector.pop_back();
+// assert(vector.back() == 'c');
+//
+// const char array[] = "hi";
+// vector = ftl::StaticVector(array);
+// assert(vector == (ftl::StaticVector{'h', 'i', '\0'}));
+//
+// ftl::StaticVector strings = ftl::init::list<std::string>("abc")("123456", 3u)(3u, '?');
+// assert(strings.size() == 3u);
+// assert(strings[0] == "abc");
+// assert(strings[1] == "123");
+// assert(strings[2] == "???");
+//
+template <typename T, std::size_t N>
+class StaticVector final : ArrayTraits<T>,
+ ArrayIterators<StaticVector<T, N>, T>,
+ ArrayComparators<StaticVector> {
+ static_assert(N > 0);
+
+ using ArrayTraits<T>::construct_at;
+
+ using Iter = ArrayIterators<StaticVector, T>;
+ friend Iter;
+
+ // There is ambiguity when constructing from two iterator-like elements like pointers:
+ // they could be an iterator range, or arguments for in-place construction. Assume the
+ // latter unless they are input iterators and cannot be used to construct elements. If
+ // the former is intended, the caller can pass an IteratorRangeTag to disambiguate.
+ template <typename I, typename Traits = std::iterator_traits<I>>
+ using is_input_iterator =
+ std::conjunction<std::is_base_of<std::input_iterator_tag, typename Traits::iterator_category>,
+ std::negation<std::is_constructible<T, I>>>;
+
+ public:
+ FTL_ARRAY_TRAIT(T, value_type);
+ FTL_ARRAY_TRAIT(T, size_type);
+ FTL_ARRAY_TRAIT(T, difference_type);
+
+ FTL_ARRAY_TRAIT(T, pointer);
+ FTL_ARRAY_TRAIT(T, reference);
+ FTL_ARRAY_TRAIT(T, iterator);
+ FTL_ARRAY_TRAIT(T, reverse_iterator);
+
+ FTL_ARRAY_TRAIT(T, const_pointer);
+ FTL_ARRAY_TRAIT(T, const_reference);
+ FTL_ARRAY_TRAIT(T, const_iterator);
+ FTL_ARRAY_TRAIT(T, const_reverse_iterator);
+
+ // Creates an empty vector.
+ StaticVector() = default;
+
+ // Copies and moves a vector, respectively.
+ StaticVector(const StaticVector& other)
+ : StaticVector(kIteratorRange, other.begin(), other.end()) {}
+
+ StaticVector(StaticVector&& other) { swap<true>(other); }
+
+ // Copies at most N elements from a smaller convertible vector.
+ template <typename U, std::size_t M, typename = std::enable_if_t<M <= N>>
+ StaticVector(const StaticVector<U, M>& other)
+ : StaticVector(kIteratorRange, other.begin(), other.end()) {}
+
+ // Copies at most N elements from an array.
+ template <typename U, std::size_t M>
+ explicit StaticVector(U (&array)[M])
+ : StaticVector(kIteratorRange, std::begin(array), std::end(array)) {}
+
+ // Copies at most N elements from the range [first, last).
+ //
+ // IteratorRangeTag disambiguates with initialization from two iterator-like elements.
+ //
+ template <typename Iterator, typename = std::enable_if_t<is_input_iterator<Iterator>{}>>
+ StaticVector(Iterator first, Iterator last) : StaticVector(kIteratorRange, first, last) {
+ using V = typename std::iterator_traits<Iterator>::value_type;
+ static_assert(std::is_constructible_v<value_type, V>, "Incompatible iterator range");
+ }
+
+ template <typename Iterator>
+ StaticVector(IteratorRangeTag, Iterator first, Iterator last)
+ : size_(std::min(max_size(), static_cast<size_type>(std::distance(first, last)))) {
+ std::uninitialized_copy(first, first + size_, begin());
+ }
+
+ // Constructs at most N elements. The template arguments T and N are inferred using the
+ // deduction guide defined below. Note that T is determined from the first element, and
+ // subsequent elements must have convertible types:
+ //
+ // ftl::StaticVector vector = {1, 2, 3};
+ // static_assert(std::is_same_v<decltype(vector), ftl::StaticVector<int, 3>>);
+ //
+ // const auto copy = "quince"s;
+ // auto move = "tart"s;
+ // ftl::StaticVector vector = {copy, std::move(move)};
+ //
+ // static_assert(std::is_same_v<decltype(vector), ftl::StaticVector<std::string, 2>>);
+ //
+ template <typename E, typename... Es,
+ typename = std::enable_if_t<std::is_constructible_v<value_type, E>>>
+ StaticVector(E&& element, Es&&... elements)
+ : StaticVector(std::index_sequence<0>{}, std::forward<E>(element),
+ std::forward<Es>(elements)...) {
+ static_assert(sizeof...(elements) < N, "Too many elements");
+ }
+
+ // Constructs at most N elements in place by forwarding per-element constructor arguments. The
+ // template arguments T and N are inferred using the deduction guide defined below. The syntax
+ // for listing arguments is as follows:
+ //
+ // ftl::StaticVector vector = ftl::init::list<std::string>("abc")()(3u, '?');
+ //
+ // static_assert(std::is_same_v<decltype(vector), ftl::StaticVector<std::string, 3>>);
+ // assert(vector.full());
+ // assert(vector[0] == "abc");
+ // assert(vector[1].empty());
+ // assert(vector[2] == "???");
+ //
+ template <typename U, std::size_t Size, std::size_t... Sizes, typename... Types>
+ StaticVector(InitializerList<U, std::index_sequence<Size, Sizes...>, Types...>&& list)
+ : StaticVector(std::index_sequence<0, 0, Size>{}, std::make_index_sequence<Size>{},
+ std::index_sequence<Sizes...>{}, list.tuple) {}
+
+ ~StaticVector() { std::destroy(begin(), end()); }
+
+ StaticVector& operator=(const StaticVector& other) {
+ StaticVector copy(other);
+ swap(copy);
+ return *this;
+ }
+
+ StaticVector& operator=(StaticVector&& other) {
+ std::destroy(begin(), end());
+ size_ = 0;
+ swap<true>(other);
+ return *this;
+ }
+
+ // IsEmpty enables a fast path when the vector is known to be empty at compile time.
+ template <bool IsEmpty = false>
+ void swap(StaticVector&);
+
+ static constexpr size_type max_size() { return N; }
+ size_type size() const { return size_; }
+
+ bool empty() const { return size() == 0; }
+ bool full() const { return size() == max_size(); }
+
+ iterator begin() { return std::launder(reinterpret_cast<pointer>(data_)); }
+ iterator end() { return begin() + size(); }
+
+ using Iter::begin;
+ using Iter::end;
+
+ using Iter::cbegin;
+ using Iter::cend;
+
+ using Iter::rbegin;
+ using Iter::rend;
+
+ using Iter::crbegin;
+ using Iter::crend;
+
+ using Iter::last;
+
+ using Iter::back;
+ using Iter::front;
+
+ using Iter::operator[];
+
+ // Replaces an element, and returns a reference to it. The iterator must be dereferenceable, so
+ // replacing at end() is erroneous.
+ //
+ // The element is emplaced via move constructor, so type T does not need to define copy/move
+ // assignment, e.g. its data members may be const.
+ //
+ // The arguments may directly or indirectly refer to the element being replaced.
+ //
+ // Iterators to the replaced element point to its replacement, and others remain valid.
+ //
+ template <typename... Args>
+ reference replace(const_iterator it, Args&&... args) {
+ value_type element{std::forward<Args>(args)...};
+ std::destroy_at(it);
+ // This is only safe because exceptions are disabled.
+ return *construct_at(it, std::move(element));
+ }
+
+ // Appends an element, and returns an iterator to it. If the vector is full, the element is not
+ // inserted, and the end() iterator is returned.
+ //
+ // On success, the end() iterator is invalidated.
+ //
+ template <typename... Args>
+ iterator emplace_back(Args&&... args) {
+ if (full()) return end();
+ const iterator it = construct_at(end(), std::forward<Args>(args)...);
+ ++size_;
+ return it;
+ }
+
+ // Appends an element unless the vector is full, and returns whether the element was inserted.
+ //
+ // On success, the end() iterator is invalidated.
+ //
+ bool push_back(const value_type& v) {
+ // Two statements for sequence point.
+ const iterator it = emplace_back(v);
+ return it != end();
+ }
+
+ bool push_back(value_type&& v) {
+ // Two statements for sequence point.
+ const iterator it = emplace_back(std::move(v));
+ return it != end();
+ }
+
+ // Removes the last element. The vector must not be empty, or the call is erroneous.
+ //
+ // The last() and end() iterators are invalidated.
+ //
+ void pop_back() { unstable_erase(last()); }
+
+ // Erases an element, but does not preserve order. Rather than shifting subsequent elements,
+ // this moves the last element to the slot of the erased element.
+ //
+ // The last() and end() iterators, as well as those to the erased element, are invalidated.
+ //
+ void unstable_erase(const_iterator it) {
+ std::destroy_at(it);
+ if (it != last()) {
+ // Move last element and destroy its source for destructor side effects. This is only
+ // safe because exceptions are disabled.
+ construct_at(it, std::move(back()));
+ std::destroy_at(last());
+ }
+ --size_;
+ }
+
+ private:
+ // Recursion for variadic constructor.
+ template <std::size_t I, typename E, typename... Es>
+ StaticVector(std::index_sequence<I>, E&& element, Es&&... elements)
+ : StaticVector(std::index_sequence<I + 1>{}, std::forward<Es>(elements)...) {
+ construct_at(begin() + I, std::forward<E>(element));
+ }
+
+ // Base case for variadic constructor.
+ template <std::size_t I>
+ explicit StaticVector(std::index_sequence<I>) : size_(I) {}
+
+ // Recursion for in-place constructor.
+ //
+ // Construct element I by extracting its arguments from the InitializerList tuple. ArgIndex
+ // is the position of its first argument in Args, and ArgCount is the number of arguments.
+ // The Indices sequence corresponds to [0, ArgCount).
+ //
+ // The Sizes sequence lists the argument counts for elements after I, so Size is the ArgCount
+ // for the next element. The recursion stops when Sizes is empty for the last element.
+ //
+ template <std::size_t I, std::size_t ArgIndex, std::size_t ArgCount, std::size_t... Indices,
+ std::size_t Size, std::size_t... Sizes, typename... Args>
+ StaticVector(std::index_sequence<I, ArgIndex, ArgCount>, std::index_sequence<Indices...>,
+ std::index_sequence<Size, Sizes...>, std::tuple<Args...>& tuple)
+ : StaticVector(std::index_sequence<I + 1, ArgIndex + ArgCount, Size>{},
+ std::make_index_sequence<Size>{}, std::index_sequence<Sizes...>{}, tuple) {
+ construct_at(begin() + I, std::move(std::get<ArgIndex + Indices>(tuple))...);
+ }
+
+ // Base case for in-place constructor.
+ template <std::size_t I, std::size_t ArgIndex, std::size_t ArgCount, std::size_t... Indices,
+ typename... Args>
+ StaticVector(std::index_sequence<I, ArgIndex, ArgCount>, std::index_sequence<Indices...>,
+ std::index_sequence<>, std::tuple<Args...>& tuple)
+ : size_(I + 1) {
+ construct_at(begin() + I, std::move(std::get<ArgIndex + Indices>(tuple))...);
+ }
+
+ size_type size_ = 0;
+ std::aligned_storage_t<sizeof(value_type), alignof(value_type)> data_[N];
+};
+
+// Deduction guide for array constructor.
+template <typename T, std::size_t N>
+StaticVector(T (&)[N]) -> StaticVector<std::remove_cv_t<T>, N>;
+
+// Deduction guide for variadic constructor.
+template <typename T, typename... Us, typename V = std::decay_t<T>,
+ typename = std::enable_if_t<(std::is_constructible_v<V, Us> && ...)>>
+StaticVector(T&&, Us&&...) -> StaticVector<V, 1 + sizeof...(Us)>;
+
+// Deduction guide for in-place constructor.
+template <typename T, std::size_t... Sizes, typename... Types>
+StaticVector(InitializerList<T, std::index_sequence<Sizes...>, Types...>&&)
+ -> StaticVector<T, sizeof...(Sizes)>;
+
+template <typename T, std::size_t N>
+template <bool IsEmpty>
+void StaticVector<T, N>::swap(StaticVector& other) {
+ auto [to, from] = std::make_pair(this, &other);
+ if (from == this) return;
+
+ // Assume this vector has fewer elements, so the excess of the other vector will be moved to it.
+ auto [min, max] = std::make_pair(size(), other.size());
+
+ // No elements to swap if moving into an empty vector.
+ if constexpr (IsEmpty) {
+ assert(min == 0);
+ } else {
+ if (min > max) {
+ std::swap(from, to);
+ std::swap(min, max);
+ }
+
+ // Swap elements [0, min).
+ std::swap_ranges(begin(), begin() + min, other.begin());
+
+ // No elements to move if sizes are equal.
+ if (min == max) return;
+ }
+
+ // Move elements [min, max) and destroy their source for destructor side effects.
+ const auto [first, last] = std::make_pair(from->begin() + min, from->begin() + max);
+ std::uninitialized_move(first, last, to->begin() + min);
+ std::destroy(first, last);
+
+ std::swap(size_, other.size_);
+}
+
+template <typename T, std::size_t N>
+inline void swap(StaticVector<T, N>& lhs, StaticVector<T, N>& rhs) {
+ lhs.swap(rhs);
+}
+
+} // namespace android::ftl
diff --git a/include/gfx/.clang-format b/include/gfx/.clang-format
deleted file mode 100644
index 86763a0..0000000
--- a/include/gfx/.clang-format
+++ /dev/null
@@ -1,11 +0,0 @@
-BasedOnStyle: Google
-
-AccessModifierOffset: -2
-AllowShortFunctionsOnASingleLine: Inline
-BinPackParameters: false
-ColumnLimit: 100
-CommentPragmas: NOLINT:.*
-ConstructorInitializerAllOnOneLineOrOnePerLine: true
-ConstructorInitializerIndentWidth: 2
-ContinuationIndentWidth: 8
-IndentWidth: 4
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
index 2427a07..5e40ca7 100644
--- a/include/input/DisplayViewport.h
+++ b/include/input/DisplayViewport.h
@@ -17,11 +17,12 @@
#ifndef _LIBINPUT_DISPLAY_VIEWPORT_H
#define _LIBINPUT_DISPLAY_VIEWPORT_H
-#include <cinttypes>
-#include <optional>
-
#include <android-base/stringprintf.h>
#include <input/Input.h>
+#include <input/NamedEnum.h>
+
+#include <cinttypes>
+#include <optional>
using android::base::StringPrintf;
@@ -39,24 +40,11 @@
* Keep in sync with values in InputManagerService.java.
*/
enum class ViewportType : int32_t {
- VIEWPORT_INTERNAL = 1,
- VIEWPORT_EXTERNAL = 2,
- VIEWPORT_VIRTUAL = 3,
+ INTERNAL = 1,
+ EXTERNAL = 2,
+ VIRTUAL = 3,
};
-static const char* viewportTypeToString(ViewportType type) {
- switch(type) {
- case ViewportType::VIEWPORT_INTERNAL:
- return "INTERNAL";
- case ViewportType::VIEWPORT_EXTERNAL:
- return "EXTERNAL";
- case ViewportType::VIEWPORT_VIRTUAL:
- return "VIRTUAL";
- default:
- return "UNKNOWN";
- }
-}
-
/*
* Describes how coordinates are mapped on a physical display.
* See com.android.server.display.DisplayViewport.
@@ -97,7 +85,7 @@
isActive(false),
uniqueId(),
physicalPort(std::nullopt),
- type(ViewportType::VIEWPORT_INTERNAL) {}
+ type(ViewportType::INTERNAL) {}
bool operator==(const DisplayViewport& other) const {
return displayId == other.displayId && orientation == other.orientation &&
@@ -131,10 +119,10 @@
physicalBottom = height;
deviceWidth = width;
deviceHeight = height;
- isActive = false;
+ isActive = true;
uniqueId.clear();
physicalPort = std::nullopt;
- type = ViewportType::VIEWPORT_INTERNAL;
+ type = ViewportType::INTERNAL;
}
std::string toString() const {
@@ -143,7 +131,7 @@
"physicalFrame=[%d, %d, %d, %d], "
"deviceSize=[%d, %d], "
"isActive=[%d]",
- viewportTypeToString(type), displayId, uniqueId.c_str(),
+ NamedEnum::string(type).c_str(), displayId, uniqueId.c_str(),
physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str()
: "<none>",
orientation, logicalLeft, logicalTop, logicalRight, logicalBottom,
diff --git a/include/input/Flags.h b/include/input/Flags.h
new file mode 100644
index 0000000..b12a9ed
--- /dev/null
+++ b/include/input/Flags.h
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2020 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 <android-base/stringprintf.h>
+
+#include <array>
+#include <cstdint>
+#include <optional>
+#include <string>
+#include <type_traits>
+
+#include "NamedEnum.h"
+#include "utils/BitSet.h"
+
+#ifndef __UI_INPUT_FLAGS_H
+#define __UI_INPUT_FLAGS_H
+
+namespace android {
+
+namespace details {
+
+template <typename F>
+inline constexpr auto flag_count = sizeof(F) * __CHAR_BIT__;
+
+template <typename F, typename T, T... I>
+constexpr auto generate_flag_values(std::integer_sequence<T, I...> seq) {
+ constexpr size_t count = seq.size();
+
+ std::array<F, count> values{};
+ for (size_t i = 0, v = 0; v < count; ++i) {
+ values[v++] = static_cast<F>(T{1} << i);
+ }
+
+ return values;
+}
+
+template <typename F>
+inline constexpr auto flag_values = generate_flag_values<F>(
+ std::make_integer_sequence<std::underlying_type_t<F>, flag_count<F>>{});
+
+template <typename F, std::size_t... I>
+constexpr auto generate_flag_names(std::index_sequence<I...>) noexcept {
+ return std::array<std::optional<std::string_view>, sizeof...(I)>{
+ {enum_value_name<F, flag_values<F>[I]>()...}};
+}
+
+template <typename F>
+inline constexpr auto flag_names =
+ generate_flag_names<F>(std::make_index_sequence<flag_count<F>>{});
+
+// A trait for determining whether a type is specifically an enum class or not.
+template <typename T, bool = std::is_enum_v<T>>
+struct is_enum_class : std::false_type {};
+
+// By definition, an enum class is an enum that is not implicitly convertible to its underlying
+// type.
+template <typename T>
+struct is_enum_class<T, true>
+ : std::bool_constant<!std::is_convertible_v<T, std::underlying_type_t<T>>> {};
+
+template <typename T>
+inline constexpr bool is_enum_class_v = is_enum_class<T>::value;
+} // namespace details
+
+template <auto V>
+constexpr auto flag_name() {
+ using F = decltype(V);
+ return details::enum_value_name<F, V>();
+}
+
+template <typename F>
+constexpr std::optional<std::string_view> flag_name(F flag) {
+ using U = std::underlying_type_t<F>;
+ auto idx = static_cast<size_t>(__builtin_ctzl(static_cast<U>(flag)));
+ return details::flag_names<F>[idx];
+}
+
+/* A class for handling flags defined by an enum or enum class in a type-safe way. */
+template <typename F>
+class Flags {
+ // F must be an enum or its underlying type is undefined. Theoretically we could specialize this
+ // further to avoid this restriction but in general we want to encourage the use of enums
+ // anyways.
+ static_assert(std::is_enum_v<F>, "Flags type must be an enum");
+ using U = typename std::underlying_type_t<F>;
+
+public:
+ constexpr Flags(F f) : mFlags(static_cast<U>(f)) {}
+ constexpr Flags() : mFlags(0) {}
+ constexpr Flags(const Flags<F>& f) : mFlags(f.mFlags) {}
+
+ // Provide a non-explicit construct for non-enum classes since they easily convert to their
+ // underlying types (e.g. when used with bitwise operators). For enum classes, however, we
+ // should force them to be explicitly constructed from their underlying types to make full use
+ // of the type checker.
+ template <typename T = U>
+ constexpr Flags(T t, typename std::enable_if_t<!details::is_enum_class_v<F>, T>* = nullptr)
+ : mFlags(t) {}
+ template <typename T = U>
+ explicit constexpr Flags(T t,
+ typename std::enable_if_t<details::is_enum_class_v<F>, T>* = nullptr)
+ : mFlags(t) {}
+
+ class Iterator {
+ // The type can't be larger than 64-bits otherwise it won't fit in BitSet64.
+ static_assert(sizeof(U) <= sizeof(uint64_t));
+
+ public:
+ Iterator(Flags<F> flags) : mRemainingFlags(flags.mFlags) { (*this)++; }
+ Iterator() : mRemainingFlags(0), mCurrFlag(static_cast<F>(0)) {}
+
+ // Pre-fix ++
+ Iterator& operator++() {
+ if (mRemainingFlags.isEmpty()) {
+ mCurrFlag = static_cast<F>(0);
+ } else {
+ uint64_t bit = mRemainingFlags.clearLastMarkedBit(); // counts from left
+ const U flag = 1 << (64 - bit - 1);
+ mCurrFlag = static_cast<F>(flag);
+ }
+ return *this;
+ }
+
+ // Post-fix ++
+ Iterator operator++(int) {
+ Iterator iter = *this;
+ ++*this;
+ return iter;
+ }
+
+ bool operator==(Iterator other) const {
+ return mCurrFlag == other.mCurrFlag && mRemainingFlags == other.mRemainingFlags;
+ }
+
+ bool operator!=(Iterator other) const { return !(*this == other); }
+
+ F operator*() { return mCurrFlag; }
+
+ // iterator traits
+
+ // In the future we could make this a bidirectional const iterator instead of a forward
+ // iterator but it doesn't seem worth the added complexity at this point. This could not,
+ // however, be made a non-const iterator as assigning one flag to another is a non-sensical
+ // operation.
+ using iterator_category = std::input_iterator_tag;
+ using value_type = F;
+ // Per the C++ spec, because input iterators are not assignable the iterator's reference
+ // type does not actually need to be a reference. In fact, making it a reference would imply
+ // that modifying it would change the underlying Flags object, which is obviously wrong for
+ // the same reason this can't be a non-const iterator.
+ using reference = F;
+ using difference_type = void;
+ using pointer = void;
+
+ private:
+ BitSet64 mRemainingFlags;
+ F mCurrFlag;
+ };
+
+ /*
+ * Tests whether the given flag is set.
+ */
+ bool test(F flag) const {
+ U f = static_cast<U>(flag);
+ return (f & mFlags) == f;
+ }
+
+ /* Tests whether any of the given flags are set */
+ bool any(Flags<F> f) { return (mFlags & f.mFlags) != 0; }
+
+ /* Tests whether all of the given flags are set */
+ bool all(Flags<F> f) { return (mFlags & f.mFlags) == f.mFlags; }
+
+ Flags<F> operator|(Flags<F> rhs) const { return static_cast<F>(mFlags | rhs.mFlags); }
+ Flags<F>& operator|=(Flags<F> rhs) {
+ mFlags = mFlags | rhs.mFlags;
+ return *this;
+ }
+
+ Flags<F> operator&(Flags<F> rhs) const { return static_cast<F>(mFlags & rhs.mFlags); }
+ Flags<F>& operator&=(Flags<F> rhs) {
+ mFlags = mFlags & rhs.mFlags;
+ return *this;
+ }
+
+ Flags<F> operator^(Flags<F> rhs) const { return static_cast<F>(mFlags ^ rhs.mFlags); }
+ Flags<F>& operator^=(Flags<F> rhs) {
+ mFlags = mFlags ^ rhs.mFlags;
+ return *this;
+ }
+
+ Flags<F> operator~() { return static_cast<F>(~mFlags); }
+
+ bool operator==(Flags<F> rhs) const { return mFlags == rhs.mFlags; }
+ bool operator!=(Flags<F> rhs) const { return !operator==(rhs); }
+
+ Flags<F>& operator=(const Flags<F>& rhs) {
+ mFlags = rhs.mFlags;
+ return *this;
+ }
+
+ Iterator begin() const { return Iterator(*this); }
+
+ Iterator end() const { return Iterator(); }
+
+ /*
+ * Returns the stored set of flags.
+ *
+ * Note that this returns the underlying type rather than the base enum class. This is because
+ * the value is no longer necessarily a strict member of the enum since the returned value could
+ * be multiple enum variants OR'd together.
+ */
+ U get() const { return mFlags; }
+
+ std::string string() const {
+ std::string result;
+ bool first = true;
+ U unstringified = 0;
+ for (const F f : *this) {
+ std::optional<std::string_view> flagString = flag_name(f);
+ if (flagString) {
+ appendFlag(result, flagString.value(), first);
+ } else {
+ unstringified |= static_cast<U>(f);
+ }
+ }
+
+ if (unstringified != 0) {
+ appendFlag(result, base::StringPrintf("0x%08x", unstringified), first);
+ }
+
+ if (first) {
+ result += "0x0";
+ }
+
+ return result;
+ }
+
+private:
+ U mFlags;
+
+ static void appendFlag(std::string& str, const std::string_view& flag, bool& first) {
+ if (first) {
+ first = false;
+ } else {
+ str += " | ";
+ }
+ str += flag;
+ }
+};
+
+// This namespace provides operator overloads for enum classes to make it easier to work with them
+// as flags. In order to use these, add them via a `using namespace` declaration.
+namespace flag_operators {
+
+template <typename F, typename = std::enable_if_t<details::is_enum_class_v<F>>>
+inline Flags<F> operator~(F f) {
+ using U = typename std::underlying_type_t<F>;
+ return static_cast<F>(~static_cast<U>(f));
+}
+template <typename F, typename = std::enable_if_t<details::is_enum_class_v<F>>>
+Flags<F> operator|(F lhs, F rhs) {
+ using U = typename std::underlying_type_t<F>;
+ return static_cast<F>(static_cast<U>(lhs) | static_cast<U>(rhs));
+}
+
+} // namespace flag_operators
+} // namespace android
+
+#endif // __UI_INPUT_FLAGS_H
diff --git a/include/input/IInputFlinger.h b/include/input/IInputFlinger.h
deleted file mode 100644
index d23e3b7..0000000
--- a/include/input/IInputFlinger.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-#ifndef _LIBINPUT_IINPUT_FLINGER_H
-#define _LIBINPUT_IINPUT_FLINGER_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/IInterface.h>
-
-#include <input/InputWindow.h>
-#include <input/ISetInputWindowsListener.h>
-
-namespace android {
-
-/*
- * This class defines the Binder IPC interface for accessing various
- * InputFlinger features.
- */
-class IInputFlinger : public IInterface {
-public:
- DECLARE_META_INTERFACE(InputFlinger)
-
- virtual void setInputWindows(const std::vector<InputWindowInfo>& inputHandles,
- const sp<ISetInputWindowsListener>& setInputWindowsListener) = 0;
- virtual void registerInputChannel(const sp<InputChannel>& channel) = 0;
- virtual void unregisterInputChannel(const sp<InputChannel>& channel) = 0;
-};
-
-
-/**
- * Binder implementation.
- */
-class BnInputFlinger : public BnInterface<IInputFlinger> {
-public:
- enum {
- SET_INPUT_WINDOWS_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
- REGISTER_INPUT_CHANNEL_TRANSACTION,
- UNREGISTER_INPUT_CHANNEL_TRANSACTION
- };
-
- virtual status_t onTransact(uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags = 0);
-};
-
-} // namespace android
-
-#endif // _LIBINPUT_IINPUT_FLINGER_H
diff --git a/include/input/ISetInputWindowsListener.h b/include/input/ISetInputWindowsListener.h
deleted file mode 100644
index 15d31b2..0000000
--- a/include/input/ISetInputWindowsListener.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-
-#pragma once
-
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-
-namespace android {
-
-class ISetInputWindowsListener : public IInterface {
-public:
- DECLARE_META_INTERFACE(SetInputWindowsListener)
- virtual void onSetInputWindowsFinished() = 0;
-};
-
-class BnSetInputWindowsListener: public BnInterface<ISetInputWindowsListener> {
-public:
- enum SetInputWindowsTag : uint32_t {
- ON_SET_INPUT_WINDOWS_FINISHED
- };
-
- virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags = 0) override;
-};
-
-}; // namespace android
diff --git a/include/input/Input.h b/include/input/Input.h
index af9c41d..bb5ca0e 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -26,6 +26,7 @@
#include <android/input.h>
#include <math.h>
#include <stdint.h>
+#include <ui/Transform.h>
#include <utils/BitSet.h>
#include <utils/KeyedVector.h>
#include <utils/RefBase.h>
@@ -140,14 +141,6 @@
#define MAX_CONTROLLER_LEDS 4
/*
- * SystemUiVisibility constants from View.
- */
-enum {
- ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE = 0,
- ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN = 0x00000001,
-};
-
-/*
* Maximum number of pointers supported per motion event.
* Smallest number of pointers is 1.
* (We want at least 10 but some touch controllers obstensibly configured for 10 pointers
@@ -184,7 +177,7 @@
namespace android {
-#ifdef __ANDROID__
+#ifdef __linux__
class Parcel;
#endif
@@ -273,6 +266,20 @@
const char* motionClassificationToString(MotionClassification classification);
/**
+ * Portion of FrameMetrics timeline of interest to input code.
+ */
+enum GraphicsTimeline : size_t {
+ /** Time when the app sent the buffer to SurfaceFlinger. */
+ GPU_COMPLETED_TIME = 0,
+
+ /** Time when the frame was presented on the display */
+ PRESENT_TIME = 1,
+
+ /** Total size of the 'GraphicsTimeline' array. Must always be last. */
+ SIZE = 2
+};
+
+/**
* Generator of unique numbers used to identify input events.
*
* Layout of ID:
@@ -287,9 +294,9 @@
public:
// Used to divide integer space to ensure no conflict among these sources./
enum class Source : int32_t {
- INPUT_READER = 0x0 << SOURCE_SHIFT,
- INPUT_DISPATCHER = 0x1 << SOURCE_SHIFT,
- OTHER = 0x3 << SOURCE_SHIFT, // E.g. app injected events
+ INPUT_READER = static_cast<int32_t>(0x0u << SOURCE_SHIFT),
+ INPUT_DISPATCHER = static_cast<int32_t>(0x1u << SOURCE_SHIFT),
+ OTHER = static_cast<int32_t>(0x3u << SOURCE_SHIFT), // E.g. app injected events
};
IdGenerator(Source source);
@@ -301,7 +308,7 @@
private:
const Source mSource;
- static constexpr int32_t SOURCE_MASK = 0x3 << SOURCE_SHIFT;
+ static constexpr int32_t SOURCE_MASK = static_cast<int32_t>(0x3u << SOURCE_SHIFT);
};
/**
@@ -311,11 +318,6 @@
*/
constexpr float AMOTION_EVENT_INVALID_CURSOR_POSITION = std::numeric_limits<float>::quiet_NaN();
-/**
- * Invalid value of HMAC - SHA256. Any events with this HMAC value will be marked as not verified.
- */
-constexpr std::array<uint8_t, 32> INVALID_HMAC = {0};
-
/*
* Pointer coordinate data.
*/
@@ -348,6 +350,8 @@
void scale(float globalScale, float windowXScale, float windowYScale);
void applyOffset(float xOffset, float yOffset);
+ void transform(const ui::Transform& transform);
+
inline float getX() const {
return getAxisValue(AMOTION_EVENT_AXIS_X);
}
@@ -356,7 +360,7 @@
return getAxisValue(AMOTION_EVENT_AXIS_Y);
}
-#ifdef __ANDROID__
+#ifdef __linux__
status_t readFromParcel(Parcel* parcel);
status_t writeToParcel(Parcel* parcel) const;
#endif
@@ -524,13 +528,11 @@
inline void setActionButton(int32_t button) { mActionButton = button; }
- inline float getXScale() const { return mXScale; }
+ inline float getXOffset() const { return mTransform.tx(); }
- inline float getYScale() const { return mYScale; }
+ inline float getYOffset() const { return mTransform.ty(); }
- inline float getXOffset() const { return mXOffset; }
-
- inline float getYOffset() const { return mYOffset; }
+ inline ui::Transform getTransform() const { return mTransform; }
inline float getXPrecision() const { return mXPrecision; }
@@ -582,10 +584,18 @@
float getAxisValue(int32_t axis, size_t pointerIndex) const;
+ /**
+ * Get the X coordinate of the latest sample in this MotionEvent for pointer 'pointerIndex'.
+ * Identical to calling getHistoricalX(pointerIndex, getHistorySize()).
+ */
inline float getX(size_t pointerIndex) const {
return getAxisValue(AMOTION_EVENT_AXIS_X, pointerIndex);
}
+ /**
+ * Get the Y coordinate of the latest sample in this MotionEvent for pointer 'pointerIndex'.
+ * Identical to calling getHistoricalX(pointerIndex, getHistorySize()).
+ */
inline float getY(size_t pointerIndex) const {
return getAxisValue(AMOTION_EVENT_AXIS_Y, pointerIndex);
}
@@ -692,8 +702,8 @@
void initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton,
int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState,
- MotionClassification classification, float xScale, float yScale, float xOffset,
- float yOffset, float xPrecision, float yPrecision, float rawXCursorPosition,
+ MotionClassification classification, const ui::Transform& transform,
+ float xPrecision, float yPrecision, float rawXCursorPosition,
float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime,
size_t pointerCount, const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords);
@@ -710,9 +720,9 @@
// Apply 3x3 perspective matrix transformation.
// Matrix is in row-major form and compatible with SkMatrix.
- void transform(const float matrix[9]);
+ void transform(const std::array<float, 9>& matrix);
-#ifdef __ANDROID__
+#ifdef __linux__
status_t readFromParcel(Parcel* parcel);
status_t writeToParcel(Parcel* parcel) const;
#endif
@@ -726,7 +736,7 @@
inline const PointerProperties* getPointerProperties() const {
return mPointerProperties.array();
}
- inline const nsecs_t* getSampleEventTimes() const { return mSampleEventTimes.array(); }
+ inline const nsecs_t* getSampleEventTimes() const { return mSampleEventTimes.data(); }
inline const PointerCoords* getSamplePointerCoords() const {
return mSamplePointerCoords.array();
}
@@ -734,7 +744,7 @@
static const char* getLabel(int32_t axis);
static int32_t getAxisFromLabel(const char* label);
- static const char* actionToString(int32_t action);
+ static std::string actionToString(int32_t action);
protected:
int32_t mAction;
@@ -744,17 +754,14 @@
int32_t mMetaState;
int32_t mButtonState;
MotionClassification mClassification;
- float mXScale;
- float mYScale;
- float mXOffset;
- float mYOffset;
+ ui::Transform mTransform;
float mXPrecision;
float mYPrecision;
float mRawXCursorPosition;
float mRawYCursorPosition;
nsecs_t mDownTime;
Vector<PointerProperties> mPointerProperties;
- Vector<nsecs_t> mSampleEventTimes;
+ std::vector<nsecs_t> mSampleEventTimes;
Vector<PointerCoords> mSamplePointerCoords;
};
@@ -780,6 +787,49 @@
bool mInTouchMode;
};
+/*
+ * Capture events.
+ */
+class CaptureEvent : public InputEvent {
+public:
+ virtual ~CaptureEvent() {}
+
+ virtual int32_t getType() const override { return AINPUT_EVENT_TYPE_CAPTURE; }
+
+ inline bool getPointerCaptureEnabled() const { return mPointerCaptureEnabled; }
+
+ void initialize(int32_t id, bool pointerCaptureEnabled);
+
+ void initialize(const CaptureEvent& from);
+
+protected:
+ bool mPointerCaptureEnabled;
+};
+
+/*
+ * Drag events.
+ */
+class DragEvent : public InputEvent {
+public:
+ virtual ~DragEvent() {}
+
+ virtual int32_t getType() const override { return AINPUT_EVENT_TYPE_DRAG; }
+
+ inline bool isExiting() const { return mIsExiting; }
+
+ inline float getX() const { return mX; }
+
+ inline float getY() const { return mY; }
+
+ void initialize(int32_t id, float x, float y, bool isExiting);
+
+ void initialize(const DragEvent& from);
+
+protected:
+ bool mIsExiting;
+ float mX, mY;
+};
+
/**
* Base class for verified events.
* Do not create a VerifiedInputEvent explicitly.
@@ -842,6 +892,8 @@
virtual KeyEvent* createKeyEvent() = 0;
virtual MotionEvent* createMotionEvent() = 0;
virtual FocusEvent* createFocusEvent() = 0;
+ virtual CaptureEvent* createCaptureEvent() = 0;
+ virtual DragEvent* createDragEvent() = 0;
};
/*
@@ -856,11 +908,15 @@
virtual KeyEvent* createKeyEvent() override { return &mKeyEvent; }
virtual MotionEvent* createMotionEvent() override { return &mMotionEvent; }
virtual FocusEvent* createFocusEvent() override { return &mFocusEvent; }
+ virtual CaptureEvent* createCaptureEvent() override { return &mCaptureEvent; }
+ virtual DragEvent* createDragEvent() override { return &mDragEvent; }
private:
KeyEvent mKeyEvent;
MotionEvent mMotionEvent;
FocusEvent mFocusEvent;
+ CaptureEvent mCaptureEvent;
+ DragEvent mDragEvent;
};
/*
@@ -874,6 +930,8 @@
virtual KeyEvent* createKeyEvent() override;
virtual MotionEvent* createMotionEvent() override;
virtual FocusEvent* createFocusEvent() override;
+ virtual CaptureEvent* createCaptureEvent() override;
+ virtual DragEvent* createDragEvent() override;
void recycle(InputEvent* event);
@@ -883,6 +941,8 @@
std::queue<std::unique_ptr<KeyEvent>> mKeyEventPool;
std::queue<std::unique_ptr<MotionEvent>> mMotionEventPool;
std::queue<std::unique_ptr<FocusEvent>> mFocusEventPool;
+ std::queue<std::unique_ptr<CaptureEvent>> mCaptureEventPool;
+ std::queue<std::unique_ptr<DragEvent>> mDragEventPool;
};
} // namespace android
diff --git a/include/input/InputApplication.h b/include/input/InputApplication.h
index 86de394..8e4fe79 100644
--- a/include/input/InputApplication.h
+++ b/include/input/InputApplication.h
@@ -19,35 +19,24 @@
#include <string>
+#include <android/InputApplicationInfo.h>
+
#include <binder/IBinder.h>
#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
#include <input/Input.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>
namespace android {
-
-/*
- * Describes the properties of an application that can receive input.
- */
-struct InputApplicationInfo {
- sp<IBinder> token;
- std::string name;
- nsecs_t dispatchingTimeout;
-
- status_t write(Parcel& output) const;
- static InputApplicationInfo read(const Parcel& from);
-};
-
-
/*
* Handle for an application that can receive input.
*
* Used by the native input dispatcher as a handle for the window manager objects
* that describe an application.
*/
-class InputApplicationHandle : public RefBase {
+class InputApplicationHandle {
public:
inline const InputApplicationInfo* getInfo() const {
return &mInfo;
@@ -57,19 +46,22 @@
return !mInfo.name.empty() ? mInfo.name : "<invalid>";
}
- inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const {
- return mInfo.token ? mInfo.dispatchingTimeout : defaultValue;
- }
-
inline std::chrono::nanoseconds getDispatchingTimeout(
std::chrono::nanoseconds defaultValue) const {
- return mInfo.token ? std::chrono::nanoseconds(mInfo.dispatchingTimeout) : defaultValue;
+ return mInfo.token ? std::chrono::milliseconds(mInfo.dispatchingTimeoutMillis)
+ : defaultValue;
}
inline sp<IBinder> getApplicationToken() const {
return mInfo.token;
}
+ bool operator==(const InputApplicationHandle& other) const {
+ return getName() == other.getName() && getApplicationToken() == other.getApplicationToken();
+ }
+
+ bool operator!=(const InputApplicationHandle& other) const { return !(*this == other); }
+
/**
* Requests that the state of this object be updated to reflect
* the most current available information about the application.
@@ -80,9 +72,10 @@
* Returns true on success, or false if the handle is no longer valid.
*/
virtual bool updateInfo() = 0;
+
protected:
- InputApplicationHandle();
- virtual ~InputApplicationHandle();
+ InputApplicationHandle() = default;
+ virtual ~InputApplicationHandle() = default;
InputApplicationInfo mInfo;
};
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 20a17e3..712adfa 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -17,8 +17,10 @@
#ifndef _LIBINPUT_INPUT_DEVICE_H
#define _LIBINPUT_INPUT_DEVICE_H
+#include <android/sensor.h>
#include <input/Input.h>
#include <input/KeyCharacterMap.h>
+#include <unordered_map>
#include <vector>
namespace android {
@@ -63,6 +65,126 @@
std::string getCanonicalName() const;
};
+/* Types of input device sensors. Keep sync with core/java/android/hardware/Sensor.java */
+enum class InputDeviceSensorType : int32_t {
+ ACCELEROMETER = ASENSOR_TYPE_ACCELEROMETER,
+ MAGNETIC_FIELD = ASENSOR_TYPE_MAGNETIC_FIELD,
+ ORIENTATION = 3,
+ GYROSCOPE = ASENSOR_TYPE_GYROSCOPE,
+ LIGHT = ASENSOR_TYPE_LIGHT,
+ PRESSURE = ASENSOR_TYPE_PRESSURE,
+ TEMPERATURE = 7,
+ PROXIMITY = ASENSOR_TYPE_PROXIMITY,
+ GRAVITY = ASENSOR_TYPE_GRAVITY,
+ LINEAR_ACCELERATION = ASENSOR_TYPE_LINEAR_ACCELERATION,
+ ROTATION_VECTOR = ASENSOR_TYPE_ROTATION_VECTOR,
+ RELATIVE_HUMIDITY = ASENSOR_TYPE_RELATIVE_HUMIDITY,
+ AMBIENT_TEMPERATURE = ASENSOR_TYPE_AMBIENT_TEMPERATURE,
+ MAGNETIC_FIELD_UNCALIBRATED = ASENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED,
+ GAME_ROTATION_VECTOR = ASENSOR_TYPE_GAME_ROTATION_VECTOR,
+ GYROSCOPE_UNCALIBRATED = ASENSOR_TYPE_GYROSCOPE_UNCALIBRATED,
+ SIGNIFICANT_MOTION = ASENSOR_TYPE_SIGNIFICANT_MOTION,
+};
+
+enum class InputDeviceSensorAccuracy : int32_t {
+ ACCURACY_NONE = 0,
+ ACCURACY_LOW = 1,
+ ACCURACY_MEDIUM = 2,
+ ACCURACY_HIGH = 3,
+};
+
+enum class InputDeviceSensorReportingMode : int32_t {
+ CONTINUOUS = 0,
+ ON_CHANGE = 1,
+ ONE_SHOT = 2,
+ SPECIAL_TRIGGER = 3,
+};
+
+enum class InputDeviceLightType : int32_t {
+ SINGLE = 0,
+ PLAYER_ID = 1,
+ RGB = 2,
+ MULTI_COLOR = 3,
+};
+
+struct InputDeviceSensorInfo {
+ explicit InputDeviceSensorInfo(std::string name, std::string vendor, int32_t version,
+ InputDeviceSensorType type, InputDeviceSensorAccuracy accuracy,
+ float maxRange, float resolution, float power, int32_t minDelay,
+ int32_t fifoReservedEventCount, int32_t fifoMaxEventCount,
+ std::string stringType, int32_t maxDelay, int32_t flags,
+ int32_t id)
+ : name(name),
+ vendor(vendor),
+ version(version),
+ type(type),
+ accuracy(accuracy),
+ maxRange(maxRange),
+ resolution(resolution),
+ power(power),
+ minDelay(minDelay),
+ fifoReservedEventCount(fifoReservedEventCount),
+ fifoMaxEventCount(fifoMaxEventCount),
+ stringType(stringType),
+ maxDelay(maxDelay),
+ flags(flags),
+ id(id) {}
+ // Name string of the sensor.
+ std::string name;
+ // Vendor string of this sensor.
+ std::string vendor;
+ // Version of the sensor's module.
+ int32_t version;
+ // Generic type of this sensor.
+ InputDeviceSensorType type;
+ // The current accuracy of sensor event.
+ InputDeviceSensorAccuracy accuracy;
+ // Maximum range of the sensor in the sensor's unit.
+ float maxRange;
+ // Resolution of the sensor in the sensor's unit.
+ float resolution;
+ // The power in mA used by this sensor while in use.
+ float power;
+ // The minimum delay allowed between two events in microsecond or zero if this sensor only
+ // returns a value when the data it's measuring changes.
+ int32_t minDelay;
+ // Number of events reserved for this sensor in the batch mode FIFO.
+ int32_t fifoReservedEventCount;
+ // Maximum number of events of this sensor that could be batched.
+ int32_t fifoMaxEventCount;
+ // The type of this sensor as a string.
+ std::string stringType;
+ // The delay between two sensor events corresponding to the lowest frequency that this sensor
+ // supports.
+ int32_t maxDelay;
+ // Sensor flags
+ int32_t flags;
+ // Sensor id, same as the input device ID it belongs to.
+ int32_t id;
+};
+
+struct InputDeviceLightInfo {
+ explicit InputDeviceLightInfo(std::string name, int32_t id, InputDeviceLightType type,
+ int32_t ordinal)
+ : name(name), id(id), type(type), ordinal(ordinal) {}
+ // Name string of the light.
+ std::string name;
+ // Light id
+ int32_t id;
+ // Type of the light.
+ InputDeviceLightType type;
+ // Ordinal of the light
+ int32_t ordinal;
+};
+
+struct InputDeviceBatteryInfo {
+ explicit InputDeviceBatteryInfo(std::string name, int32_t id) : name(name), id(id) {}
+ // Name string of the battery.
+ std::string name;
+ // Battery id
+ int32_t id;
+};
+
/*
* Describes the characteristics and capabilities of an input device.
*/
@@ -104,28 +226,45 @@
void addMotionRange(int32_t axis, uint32_t source,
float min, float max, float flat, float fuzz, float resolution);
void addMotionRange(const MotionRange& range);
+ void addSensorInfo(const InputDeviceSensorInfo& info);
+ void addBatteryInfo(const InputDeviceBatteryInfo& info);
+ void addLightInfo(const InputDeviceLightInfo& info);
inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; }
inline int32_t getKeyboardType() const { return mKeyboardType; }
- inline void setKeyCharacterMap(const sp<KeyCharacterMap>& value) {
+ inline void setKeyCharacterMap(const std::shared_ptr<KeyCharacterMap> value) {
mKeyCharacterMap = value;
}
- inline sp<KeyCharacterMap> getKeyCharacterMap() const {
+ inline const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap() const {
return mKeyCharacterMap;
}
inline void setVibrator(bool hasVibrator) { mHasVibrator = hasVibrator; }
inline bool hasVibrator() const { return mHasVibrator; }
+ inline void setHasBattery(bool hasBattery) { mHasBattery = hasBattery; }
+ inline bool hasBattery() const { return mHasBattery; }
+
inline void setButtonUnderPad(bool hasButton) { mHasButtonUnderPad = hasButton; }
inline bool hasButtonUnderPad() const { return mHasButtonUnderPad; }
+ inline void setHasSensor(bool hasSensor) { mHasSensor = hasSensor; }
+ inline bool hasSensor() const { return mHasSensor; }
+
inline const std::vector<MotionRange>& getMotionRanges() const {
return mMotionRanges;
}
+ const InputDeviceSensorInfo* getSensorInfo(InputDeviceSensorType type);
+
+ const std::vector<InputDeviceSensorType> getSensorTypes();
+
+ const std::vector<int32_t> getLightIds();
+
+ const InputDeviceLightInfo* getLightInfo(int32_t id);
+
private:
int32_t mId;
int32_t mGeneration;
@@ -136,18 +275,25 @@
bool mHasMic;
uint32_t mSources;
int32_t mKeyboardType;
- sp<KeyCharacterMap> mKeyCharacterMap;
+ std::shared_ptr<KeyCharacterMap> mKeyCharacterMap;
bool mHasVibrator;
+ bool mHasBattery;
bool mHasButtonUnderPad;
+ bool mHasSensor;
std::vector<MotionRange> mMotionRanges;
+ std::unordered_map<InputDeviceSensorType, InputDeviceSensorInfo> mSensors;
+ /* Map from light ID to light info */
+ std::unordered_map<int32_t, InputDeviceLightInfo> mLights;
+ /* Map from battery ID to battery info */
+ std::unordered_map<int32_t, InputDeviceBatteryInfo> mBatteries;
};
/* Types of input device configuration files. */
-enum InputDeviceConfigurationFileType {
- INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION = 0, /* .idc file */
- INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT = 1, /* .kl file */
- INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP = 2, /* .kcm file */
+enum class InputDeviceConfigurationFileType : int32_t {
+ CONFIGURATION = 0, /* .idc file */
+ KEY_LAYOUT = 1, /* .kl file */
+ KEY_CHARACTER_MAP = 2, /* .kcm file */
};
/*
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h
index b327d76..2a742f9 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -19,11 +19,7 @@
#include <input/Input.h>
#include <android/keycodes.h>
-
-#define DEFINE_KEYCODE(key) { #key, AKEYCODE_##key }
-#define DEFINE_AXIS(axis) { #axis, AMOTION_EVENT_AXIS_##axis }
-#define DEFINE_LED(led) { #led, ALED_##led }
-#define DEFINE_FLAG(flag) { #flag, POLICY_FLAG_##flag }
+#include <unordered_map>
namespace android {
@@ -35,430 +31,41 @@
int value;
};
+// NOTE: If you want a new key code, axis code, led code or flag code in keylayout file,
+// then you must add it to InputEventLabels.cpp.
-static const InputEventLabel KEYCODES[] = {
- // NOTE: If you add a new keycode here you must also add it to several other files.
- // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
- DEFINE_KEYCODE(UNKNOWN),
- DEFINE_KEYCODE(SOFT_LEFT),
- DEFINE_KEYCODE(SOFT_RIGHT),
- DEFINE_KEYCODE(HOME),
- DEFINE_KEYCODE(BACK),
- DEFINE_KEYCODE(CALL),
- DEFINE_KEYCODE(ENDCALL),
- DEFINE_KEYCODE(0),
- DEFINE_KEYCODE(1),
- DEFINE_KEYCODE(2),
- DEFINE_KEYCODE(3),
- DEFINE_KEYCODE(4),
- DEFINE_KEYCODE(5),
- DEFINE_KEYCODE(6),
- DEFINE_KEYCODE(7),
- DEFINE_KEYCODE(8),
- DEFINE_KEYCODE(9),
- DEFINE_KEYCODE(STAR),
- DEFINE_KEYCODE(POUND),
- DEFINE_KEYCODE(DPAD_UP),
- DEFINE_KEYCODE(DPAD_DOWN),
- DEFINE_KEYCODE(DPAD_LEFT),
- DEFINE_KEYCODE(DPAD_RIGHT),
- DEFINE_KEYCODE(DPAD_CENTER),
- DEFINE_KEYCODE(VOLUME_UP),
- DEFINE_KEYCODE(VOLUME_DOWN),
- DEFINE_KEYCODE(POWER),
- DEFINE_KEYCODE(CAMERA),
- DEFINE_KEYCODE(CLEAR),
- DEFINE_KEYCODE(A),
- DEFINE_KEYCODE(B),
- DEFINE_KEYCODE(C),
- DEFINE_KEYCODE(D),
- DEFINE_KEYCODE(E),
- DEFINE_KEYCODE(F),
- DEFINE_KEYCODE(G),
- DEFINE_KEYCODE(H),
- DEFINE_KEYCODE(I),
- DEFINE_KEYCODE(J),
- DEFINE_KEYCODE(K),
- DEFINE_KEYCODE(L),
- DEFINE_KEYCODE(M),
- DEFINE_KEYCODE(N),
- DEFINE_KEYCODE(O),
- DEFINE_KEYCODE(P),
- DEFINE_KEYCODE(Q),
- DEFINE_KEYCODE(R),
- DEFINE_KEYCODE(S),
- DEFINE_KEYCODE(T),
- DEFINE_KEYCODE(U),
- DEFINE_KEYCODE(V),
- DEFINE_KEYCODE(W),
- DEFINE_KEYCODE(X),
- DEFINE_KEYCODE(Y),
- DEFINE_KEYCODE(Z),
- DEFINE_KEYCODE(COMMA),
- DEFINE_KEYCODE(PERIOD),
- DEFINE_KEYCODE(ALT_LEFT),
- DEFINE_KEYCODE(ALT_RIGHT),
- DEFINE_KEYCODE(SHIFT_LEFT),
- DEFINE_KEYCODE(SHIFT_RIGHT),
- DEFINE_KEYCODE(TAB),
- DEFINE_KEYCODE(SPACE),
- DEFINE_KEYCODE(SYM),
- DEFINE_KEYCODE(EXPLORER),
- DEFINE_KEYCODE(ENVELOPE),
- DEFINE_KEYCODE(ENTER),
- DEFINE_KEYCODE(DEL),
- DEFINE_KEYCODE(GRAVE),
- DEFINE_KEYCODE(MINUS),
- DEFINE_KEYCODE(EQUALS),
- DEFINE_KEYCODE(LEFT_BRACKET),
- DEFINE_KEYCODE(RIGHT_BRACKET),
- DEFINE_KEYCODE(BACKSLASH),
- DEFINE_KEYCODE(SEMICOLON),
- DEFINE_KEYCODE(APOSTROPHE),
- DEFINE_KEYCODE(SLASH),
- DEFINE_KEYCODE(AT),
- DEFINE_KEYCODE(NUM),
- DEFINE_KEYCODE(HEADSETHOOK),
- DEFINE_KEYCODE(FOCUS), // *Camera* focus
- DEFINE_KEYCODE(PLUS),
- DEFINE_KEYCODE(MENU),
- DEFINE_KEYCODE(NOTIFICATION),
- DEFINE_KEYCODE(SEARCH),
- DEFINE_KEYCODE(MEDIA_PLAY_PAUSE),
- DEFINE_KEYCODE(MEDIA_STOP),
- DEFINE_KEYCODE(MEDIA_NEXT),
- DEFINE_KEYCODE(MEDIA_PREVIOUS),
- DEFINE_KEYCODE(MEDIA_REWIND),
- DEFINE_KEYCODE(MEDIA_FAST_FORWARD),
- DEFINE_KEYCODE(MUTE),
- DEFINE_KEYCODE(PAGE_UP),
- DEFINE_KEYCODE(PAGE_DOWN),
- DEFINE_KEYCODE(PICTSYMBOLS),
- DEFINE_KEYCODE(SWITCH_CHARSET),
- DEFINE_KEYCODE(BUTTON_A),
- DEFINE_KEYCODE(BUTTON_B),
- DEFINE_KEYCODE(BUTTON_C),
- DEFINE_KEYCODE(BUTTON_X),
- DEFINE_KEYCODE(BUTTON_Y),
- DEFINE_KEYCODE(BUTTON_Z),
- DEFINE_KEYCODE(BUTTON_L1),
- DEFINE_KEYCODE(BUTTON_R1),
- DEFINE_KEYCODE(BUTTON_L2),
- DEFINE_KEYCODE(BUTTON_R2),
- DEFINE_KEYCODE(BUTTON_THUMBL),
- DEFINE_KEYCODE(BUTTON_THUMBR),
- DEFINE_KEYCODE(BUTTON_START),
- DEFINE_KEYCODE(BUTTON_SELECT),
- DEFINE_KEYCODE(BUTTON_MODE),
- DEFINE_KEYCODE(ESCAPE),
- DEFINE_KEYCODE(FORWARD_DEL),
- DEFINE_KEYCODE(CTRL_LEFT),
- DEFINE_KEYCODE(CTRL_RIGHT),
- DEFINE_KEYCODE(CAPS_LOCK),
- DEFINE_KEYCODE(SCROLL_LOCK),
- DEFINE_KEYCODE(META_LEFT),
- DEFINE_KEYCODE(META_RIGHT),
- DEFINE_KEYCODE(FUNCTION),
- DEFINE_KEYCODE(SYSRQ),
- DEFINE_KEYCODE(BREAK),
- DEFINE_KEYCODE(MOVE_HOME),
- DEFINE_KEYCODE(MOVE_END),
- DEFINE_KEYCODE(INSERT),
- DEFINE_KEYCODE(FORWARD),
- DEFINE_KEYCODE(MEDIA_PLAY),
- DEFINE_KEYCODE(MEDIA_PAUSE),
- DEFINE_KEYCODE(MEDIA_CLOSE),
- DEFINE_KEYCODE(MEDIA_EJECT),
- DEFINE_KEYCODE(MEDIA_RECORD),
- DEFINE_KEYCODE(F1),
- DEFINE_KEYCODE(F2),
- DEFINE_KEYCODE(F3),
- DEFINE_KEYCODE(F4),
- DEFINE_KEYCODE(F5),
- DEFINE_KEYCODE(F6),
- DEFINE_KEYCODE(F7),
- DEFINE_KEYCODE(F8),
- DEFINE_KEYCODE(F9),
- DEFINE_KEYCODE(F10),
- DEFINE_KEYCODE(F11),
- DEFINE_KEYCODE(F12),
- DEFINE_KEYCODE(NUM_LOCK),
- DEFINE_KEYCODE(NUMPAD_0),
- DEFINE_KEYCODE(NUMPAD_1),
- DEFINE_KEYCODE(NUMPAD_2),
- DEFINE_KEYCODE(NUMPAD_3),
- DEFINE_KEYCODE(NUMPAD_4),
- DEFINE_KEYCODE(NUMPAD_5),
- DEFINE_KEYCODE(NUMPAD_6),
- DEFINE_KEYCODE(NUMPAD_7),
- DEFINE_KEYCODE(NUMPAD_8),
- DEFINE_KEYCODE(NUMPAD_9),
- DEFINE_KEYCODE(NUMPAD_DIVIDE),
- DEFINE_KEYCODE(NUMPAD_MULTIPLY),
- DEFINE_KEYCODE(NUMPAD_SUBTRACT),
- DEFINE_KEYCODE(NUMPAD_ADD),
- DEFINE_KEYCODE(NUMPAD_DOT),
- DEFINE_KEYCODE(NUMPAD_COMMA),
- DEFINE_KEYCODE(NUMPAD_ENTER),
- DEFINE_KEYCODE(NUMPAD_EQUALS),
- DEFINE_KEYCODE(NUMPAD_LEFT_PAREN),
- DEFINE_KEYCODE(NUMPAD_RIGHT_PAREN),
- DEFINE_KEYCODE(VOLUME_MUTE),
- DEFINE_KEYCODE(INFO),
- DEFINE_KEYCODE(CHANNEL_UP),
- DEFINE_KEYCODE(CHANNEL_DOWN),
- DEFINE_KEYCODE(ZOOM_IN),
- DEFINE_KEYCODE(ZOOM_OUT),
- DEFINE_KEYCODE(TV),
- DEFINE_KEYCODE(WINDOW),
- DEFINE_KEYCODE(GUIDE),
- DEFINE_KEYCODE(DVR),
- DEFINE_KEYCODE(BOOKMARK),
- DEFINE_KEYCODE(CAPTIONS),
- DEFINE_KEYCODE(SETTINGS),
- DEFINE_KEYCODE(TV_POWER),
- DEFINE_KEYCODE(TV_INPUT),
- DEFINE_KEYCODE(STB_POWER),
- DEFINE_KEYCODE(STB_INPUT),
- DEFINE_KEYCODE(AVR_POWER),
- DEFINE_KEYCODE(AVR_INPUT),
- DEFINE_KEYCODE(PROG_RED),
- DEFINE_KEYCODE(PROG_GREEN),
- DEFINE_KEYCODE(PROG_YELLOW),
- DEFINE_KEYCODE(PROG_BLUE),
- DEFINE_KEYCODE(APP_SWITCH),
- DEFINE_KEYCODE(BUTTON_1),
- DEFINE_KEYCODE(BUTTON_2),
- DEFINE_KEYCODE(BUTTON_3),
- DEFINE_KEYCODE(BUTTON_4),
- DEFINE_KEYCODE(BUTTON_5),
- DEFINE_KEYCODE(BUTTON_6),
- DEFINE_KEYCODE(BUTTON_7),
- DEFINE_KEYCODE(BUTTON_8),
- DEFINE_KEYCODE(BUTTON_9),
- DEFINE_KEYCODE(BUTTON_10),
- DEFINE_KEYCODE(BUTTON_11),
- DEFINE_KEYCODE(BUTTON_12),
- DEFINE_KEYCODE(BUTTON_13),
- DEFINE_KEYCODE(BUTTON_14),
- DEFINE_KEYCODE(BUTTON_15),
- DEFINE_KEYCODE(BUTTON_16),
- DEFINE_KEYCODE(LANGUAGE_SWITCH),
- DEFINE_KEYCODE(MANNER_MODE),
- DEFINE_KEYCODE(3D_MODE),
- DEFINE_KEYCODE(CONTACTS),
- DEFINE_KEYCODE(CALENDAR),
- DEFINE_KEYCODE(MUSIC),
- DEFINE_KEYCODE(CALCULATOR),
- DEFINE_KEYCODE(ZENKAKU_HANKAKU),
- DEFINE_KEYCODE(EISU),
- DEFINE_KEYCODE(MUHENKAN),
- DEFINE_KEYCODE(HENKAN),
- DEFINE_KEYCODE(KATAKANA_HIRAGANA),
- DEFINE_KEYCODE(YEN),
- DEFINE_KEYCODE(RO),
- DEFINE_KEYCODE(KANA),
- DEFINE_KEYCODE(ASSIST),
- DEFINE_KEYCODE(BRIGHTNESS_DOWN),
- DEFINE_KEYCODE(BRIGHTNESS_UP),
- DEFINE_KEYCODE(MEDIA_AUDIO_TRACK),
- DEFINE_KEYCODE(SLEEP),
- DEFINE_KEYCODE(WAKEUP),
- DEFINE_KEYCODE(PAIRING),
- DEFINE_KEYCODE(MEDIA_TOP_MENU),
- DEFINE_KEYCODE(11),
- DEFINE_KEYCODE(12),
- DEFINE_KEYCODE(LAST_CHANNEL),
- DEFINE_KEYCODE(TV_DATA_SERVICE),
- DEFINE_KEYCODE(VOICE_ASSIST),
- DEFINE_KEYCODE(TV_RADIO_SERVICE),
- DEFINE_KEYCODE(TV_TELETEXT),
- DEFINE_KEYCODE(TV_NUMBER_ENTRY),
- DEFINE_KEYCODE(TV_TERRESTRIAL_ANALOG),
- DEFINE_KEYCODE(TV_TERRESTRIAL_DIGITAL),
- DEFINE_KEYCODE(TV_SATELLITE),
- DEFINE_KEYCODE(TV_SATELLITE_BS),
- DEFINE_KEYCODE(TV_SATELLITE_CS),
- DEFINE_KEYCODE(TV_SATELLITE_SERVICE),
- DEFINE_KEYCODE(TV_NETWORK),
- DEFINE_KEYCODE(TV_ANTENNA_CABLE),
- DEFINE_KEYCODE(TV_INPUT_HDMI_1),
- DEFINE_KEYCODE(TV_INPUT_HDMI_2),
- DEFINE_KEYCODE(TV_INPUT_HDMI_3),
- DEFINE_KEYCODE(TV_INPUT_HDMI_4),
- DEFINE_KEYCODE(TV_INPUT_COMPOSITE_1),
- DEFINE_KEYCODE(TV_INPUT_COMPOSITE_2),
- DEFINE_KEYCODE(TV_INPUT_COMPONENT_1),
- DEFINE_KEYCODE(TV_INPUT_COMPONENT_2),
- DEFINE_KEYCODE(TV_INPUT_VGA_1),
- DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION),
- DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION_MIX_UP),
- DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION_MIX_DOWN),
- DEFINE_KEYCODE(TV_ZOOM_MODE),
- DEFINE_KEYCODE(TV_CONTENTS_MENU),
- DEFINE_KEYCODE(TV_MEDIA_CONTEXT_MENU),
- DEFINE_KEYCODE(TV_TIMER_PROGRAMMING),
- DEFINE_KEYCODE(HELP),
- DEFINE_KEYCODE(NAVIGATE_PREVIOUS),
- DEFINE_KEYCODE(NAVIGATE_NEXT),
- DEFINE_KEYCODE(NAVIGATE_IN),
- DEFINE_KEYCODE(NAVIGATE_OUT),
- DEFINE_KEYCODE(STEM_PRIMARY),
- DEFINE_KEYCODE(STEM_1),
- DEFINE_KEYCODE(STEM_2),
- DEFINE_KEYCODE(STEM_3),
- DEFINE_KEYCODE(DPAD_UP_LEFT),
- DEFINE_KEYCODE(DPAD_DOWN_LEFT),
- DEFINE_KEYCODE(DPAD_UP_RIGHT),
- DEFINE_KEYCODE(DPAD_DOWN_RIGHT),
- DEFINE_KEYCODE(MEDIA_SKIP_FORWARD),
- DEFINE_KEYCODE(MEDIA_SKIP_BACKWARD),
- DEFINE_KEYCODE(MEDIA_STEP_FORWARD),
- DEFINE_KEYCODE(MEDIA_STEP_BACKWARD),
- DEFINE_KEYCODE(SOFT_SLEEP),
- DEFINE_KEYCODE(CUT),
- DEFINE_KEYCODE(COPY),
- DEFINE_KEYCODE(PASTE),
- DEFINE_KEYCODE(SYSTEM_NAVIGATION_UP),
- DEFINE_KEYCODE(SYSTEM_NAVIGATION_DOWN),
- DEFINE_KEYCODE(SYSTEM_NAVIGATION_LEFT),
- DEFINE_KEYCODE(SYSTEM_NAVIGATION_RIGHT),
- DEFINE_KEYCODE(ALL_APPS),
- DEFINE_KEYCODE(REFRESH),
- DEFINE_KEYCODE(THUMBS_UP),
- DEFINE_KEYCODE(THUMBS_DOWN),
- DEFINE_KEYCODE(PROFILE_SWITCH),
+class InputEventLookup {
+public:
+ static int lookupValueByLabel(const std::unordered_map<std::string, int>& map,
+ const char* literal);
- { nullptr, 0 }
+ static const char* lookupLabelByValue(const std::vector<InputEventLabel>& vec, int value);
+
+ static int32_t getKeyCodeByLabel(const char* label);
+
+ static const char* getLabelByKeyCode(int32_t keyCode);
+
+ static uint32_t getKeyFlagByLabel(const char* label);
+
+ static int32_t getAxisByLabel(const char* label);
+
+ static const char* getAxisLabel(int32_t axisId);
+
+ static int32_t getLedByLabel(const char* label);
+
+private:
+ static const std::unordered_map<std::string, int> KEYCODES;
+
+ static const std::vector<InputEventLabel> KEY_NAMES;
+
+ static const std::unordered_map<std::string, int> AXES;
+
+ static const std::vector<InputEventLabel> AXES_NAMES;
+
+ static const std::unordered_map<std::string, int> LEDS;
+
+ static const std::unordered_map<std::string, int> FLAGS;
};
-static const InputEventLabel AXES[] = {
- DEFINE_AXIS(X),
- DEFINE_AXIS(Y),
- DEFINE_AXIS(PRESSURE),
- DEFINE_AXIS(SIZE),
- DEFINE_AXIS(TOUCH_MAJOR),
- DEFINE_AXIS(TOUCH_MINOR),
- DEFINE_AXIS(TOOL_MAJOR),
- DEFINE_AXIS(TOOL_MINOR),
- DEFINE_AXIS(ORIENTATION),
- DEFINE_AXIS(VSCROLL),
- DEFINE_AXIS(HSCROLL),
- DEFINE_AXIS(Z),
- DEFINE_AXIS(RX),
- DEFINE_AXIS(RY),
- DEFINE_AXIS(RZ),
- DEFINE_AXIS(HAT_X),
- DEFINE_AXIS(HAT_Y),
- DEFINE_AXIS(LTRIGGER),
- DEFINE_AXIS(RTRIGGER),
- DEFINE_AXIS(THROTTLE),
- DEFINE_AXIS(RUDDER),
- DEFINE_AXIS(WHEEL),
- DEFINE_AXIS(GAS),
- DEFINE_AXIS(BRAKE),
- DEFINE_AXIS(DISTANCE),
- DEFINE_AXIS(TILT),
- DEFINE_AXIS(SCROLL),
- DEFINE_AXIS(RELATIVE_X),
- DEFINE_AXIS(RELATIVE_Y),
- DEFINE_AXIS(GENERIC_1),
- DEFINE_AXIS(GENERIC_2),
- DEFINE_AXIS(GENERIC_3),
- DEFINE_AXIS(GENERIC_4),
- DEFINE_AXIS(GENERIC_5),
- DEFINE_AXIS(GENERIC_6),
- DEFINE_AXIS(GENERIC_7),
- DEFINE_AXIS(GENERIC_8),
- DEFINE_AXIS(GENERIC_9),
- DEFINE_AXIS(GENERIC_10),
- DEFINE_AXIS(GENERIC_11),
- DEFINE_AXIS(GENERIC_12),
- DEFINE_AXIS(GENERIC_13),
- DEFINE_AXIS(GENERIC_14),
- DEFINE_AXIS(GENERIC_15),
- DEFINE_AXIS(GENERIC_16),
-
- // NOTE: If you add a new axis here you must also add it to several other files.
- // Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list.
- { nullptr, 0 }
-};
-
-static const InputEventLabel LEDS[] = {
- DEFINE_LED(NUM_LOCK),
- DEFINE_LED(CAPS_LOCK),
- DEFINE_LED(SCROLL_LOCK),
- DEFINE_LED(COMPOSE),
- DEFINE_LED(KANA),
- DEFINE_LED(SLEEP),
- DEFINE_LED(SUSPEND),
- DEFINE_LED(MUTE),
- DEFINE_LED(MISC),
- DEFINE_LED(MAIL),
- DEFINE_LED(CHARGING),
- DEFINE_LED(CONTROLLER_1),
- DEFINE_LED(CONTROLLER_2),
- DEFINE_LED(CONTROLLER_3),
- DEFINE_LED(CONTROLLER_4),
-
- // NOTE: If you add new LEDs here, you must also add them to Input.h
- { nullptr, 0 }
-};
-
-static const InputEventLabel FLAGS[] = {DEFINE_FLAG(VIRTUAL),
- DEFINE_FLAG(FUNCTION),
- DEFINE_FLAG(GESTURE),
- DEFINE_FLAG(WAKE),
-
- {nullptr, 0}};
-
-static int lookupValueByLabel(const char* literal, const InputEventLabel *list) {
- while (list->literal) {
- if (strcmp(literal, list->literal) == 0) {
- return list->value;
- }
- list++;
- }
- return list->value;
-}
-
-static const char* lookupLabelByValue(int value, const InputEventLabel* list) {
- while (list->literal) {
- if (list->value == value) {
- return list->literal;
- }
- list++;
- }
- return nullptr;
-}
-
-static inline int32_t getKeyCodeByLabel(const char* label) {
- return int32_t(lookupValueByLabel(label, KEYCODES));
-}
-
-static inline const char* getLabelByKeyCode(int32_t keyCode) {
- if (keyCode >= 0 && keyCode < static_cast<int32_t>(size(KEYCODES))) {
- return KEYCODES[keyCode].literal;
- }
- return nullptr;
-}
-
-static inline uint32_t getKeyFlagByLabel(const char* label) {
- return uint32_t(lookupValueByLabel(label, FLAGS));
-}
-
-static inline int32_t getAxisByLabel(const char* label) {
- return int32_t(lookupValueByLabel(label, AXES));
-}
-
-static inline const char* getAxisLabel(int32_t axisId) {
- return lookupLabelByValue(axisId, AXES);
-}
-
-static inline int32_t getLedByLabel(const char* label) {
- return int32_t(lookupValueByLabel(label, LEDS));
-}
-
-
} // namespace android
#endif // _LIBINPUT_INPUT_EVENT_LABELS_H
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 7ca9031..898d1a9 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -30,18 +30,22 @@
*/
#include <string>
+#include <unordered_map>
#include <android-base/chrono_utils.h>
+#include <android-base/result.h>
+#include <android-base/unique_fd.h>
#include <binder/IBinder.h>
+#include <binder/Parcelable.h>
#include <input/Input.h>
+#include <sys/stat.h>
+#include <ui/Transform.h>
#include <utils/BitSet.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>
-#include <utils/Vector.h>
-#include <android-base/unique_fd.h>
namespace android {
class Parcel;
@@ -65,24 +69,29 @@
MOTION,
FINISHED,
FOCUS,
+ CAPTURE,
+ DRAG,
+ TIMELINE,
};
struct Header {
Type type; // 4 bytes
- // We don't need this field in order to align the body below but we
- // leave it here because InputMessage::size() and other functions
- // compute the size of this structure as sizeof(Header) + sizeof(Body).
- uint32_t padding;
+ uint32_t seq;
} header;
- // Body *must* be 8 byte aligned.
// For keys and motions, rely on the fact that std::array takes up exactly as much space
// as the underlying data. This is not guaranteed by C++, but it simplifies the conversions.
static_assert(sizeof(std::array<uint8_t, 32>) == 32);
+
+ // For bool values, rely on the fact that they take up exactly one byte. This is not guaranteed
+ // by C++ and is implementation-dependent, but it simplifies the conversions.
+ static_assert(sizeof(bool) == 1);
+
+ // Body *must* be 8 byte aligned.
union Body {
struct Key {
- uint32_t seq;
int32_t eventId;
+ uint32_t empty1;
nsecs_t eventTime __attribute__((aligned(8)));
int32_t deviceId;
int32_t source;
@@ -101,8 +110,8 @@
} key;
struct Motion {
- uint32_t seq;
int32_t eventId;
+ uint32_t empty1;
nsecs_t eventTime __attribute__((aligned(8)));
int32_t deviceId;
int32_t source;
@@ -117,10 +126,12 @@
uint8_t empty2[3]; // 3 bytes to fill gap created by classification
int32_t edgeFlags;
nsecs_t downTime __attribute__((aligned(8)));
- float xScale;
- float yScale;
- float xOffset;
- float yOffset;
+ float dsdx;
+ float dtdx;
+ float dtdy;
+ float dsdy;
+ float tx;
+ float ty;
float xPrecision;
float yPrecision;
float xCursorPosition;
@@ -151,22 +162,48 @@
} motion;
struct Finished {
- uint32_t seq;
- uint32_t handled; // actually a bool, but we must maintain 8-byte alignment
+ bool handled;
+ uint8_t empty[7];
+ nsecs_t consumeTime; // The time when the event was consumed by the receiving end
inline size_t size() const { return sizeof(Finished); }
} finished;
struct Focus {
- uint32_t seq;
int32_t eventId;
- uint32_t empty1;
- // The following two fields take up 4 bytes total
- uint16_t hasFocus; // actually a bool
- uint16_t inTouchMode; // actually a bool, but we must maintain 8-byte alignment
+ // The following 3 fields take up 4 bytes total
+ bool hasFocus;
+ bool inTouchMode;
+ uint8_t empty[2];
inline size_t size() const { return sizeof(Focus); }
} focus;
+
+ struct Capture {
+ int32_t eventId;
+ bool pointerCaptureEnabled;
+ uint8_t empty[3];
+
+ inline size_t size() const { return sizeof(Capture); }
+ } capture;
+
+ struct Drag {
+ int32_t eventId;
+ float x;
+ float y;
+ bool isExiting;
+ uint8_t empty[3];
+
+ inline size_t size() const { return sizeof(Drag); }
+ } drag;
+
+ struct Timeline {
+ int32_t eventId;
+ uint32_t empty;
+ std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
+
+ inline size_t size() const { return sizeof(Timeline); }
+ } timeline;
} __attribute__((aligned(8))) body;
bool isValid(size_t actualSize) const;
@@ -182,14 +219,15 @@
*
* The input channel is closed when all references to it are released.
*/
-class InputChannel : public RefBase {
-protected:
- virtual ~InputChannel();
-
+class InputChannel : public Parcelable {
public:
- static sp<InputChannel> create(const std::string& name, android::base::unique_fd fd,
- sp<IBinder> token);
-
+ static std::unique_ptr<InputChannel> create(const std::string& name,
+ android::base::unique_fd fd, sp<IBinder> token);
+ InputChannel() = default;
+ InputChannel(const InputChannel& other)
+ : mName(other.mName), mFd(::dup(other.mFd)), mToken(other.mToken){};
+ InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token);
+ virtual ~InputChannel();
/**
* Create a pair of input channels.
* The two returned input channels are equivalent, and are labeled as "server" and "client"
@@ -198,10 +236,12 @@
* Return OK on success.
*/
static status_t openInputChannelPair(const std::string& name,
- sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel);
+ std::unique_ptr<InputChannel>& outServerChannel,
+ std::unique_ptr<InputChannel>& outClientChannel);
inline std::string getName() const { return mName; }
- inline int getFd() const { return mFd.get(); }
+ inline const android::base::unique_fd& getFd() const { return mFd; }
+ inline sp<IBinder> getToken() const { return mToken; }
/* Send a message to the other endpoint.
*
@@ -229,10 +269,12 @@
status_t receiveMessage(InputMessage* msg);
/* Return a new object that has a duplicate of this channel's fd. */
- sp<InputChannel> dup() const;
+ std::unique_ptr<InputChannel> dup() const;
- status_t write(Parcel& out) const;
- static sp<InputChannel> read(const Parcel& from);
+ void copyTo(InputChannel& outChannel) const;
+
+ status_t readFromParcel(const android::Parcel* parcel) override;
+ status_t writeToParcel(android::Parcel* parcel) const override;
/**
* The connection token is used to identify the input connection, i.e.
@@ -248,8 +290,22 @@
*/
sp<IBinder> getConnectionToken() const;
+ bool operator==(const InputChannel& inputChannel) const {
+ struct stat lhs, rhs;
+ if (fstat(mFd.get(), &lhs) != 0) {
+ return false;
+ }
+ if (fstat(inputChannel.getFd(), &rhs) != 0) {
+ return false;
+ }
+ // If file descriptors are pointing to same inode they are duplicated fds.
+ return inputChannel.getName() == getName() && inputChannel.getConnectionToken() == mToken &&
+ lhs.st_ino == rhs.st_ino;
+ }
+
private:
- InputChannel(const std::string& name, android::base::unique_fd fd, sp<IBinder> token);
+ base::unique_fd dupFd() const;
+
std::string mName;
android::base::unique_fd mFd;
@@ -262,13 +318,13 @@
class InputPublisher {
public:
/* Creates a publisher associated with an input channel. */
- explicit InputPublisher(const sp<InputChannel>& channel);
+ explicit InputPublisher(const std::shared_ptr<InputChannel>& channel);
/* Destroys the publisher and releases its input channel. */
~InputPublisher();
/* Gets the underlying input channel. */
- inline sp<InputChannel> getChannel() { return mChannel; }
+ inline std::shared_ptr<InputChannel> getChannel() { return mChannel; }
/* Publishes a key event to the input channel.
*
@@ -295,11 +351,10 @@
int32_t displayId, std::array<uint8_t, 32> hmac, int32_t action,
int32_t actionButton, int32_t flags, int32_t edgeFlags,
int32_t metaState, int32_t buttonState,
- MotionClassification classification, float xScale, float yScale,
- float xOffset, float yOffset, float xPrecision, float yPrecision,
- float xCursorPosition, float yCursorPosition, nsecs_t downTime,
- nsecs_t eventTime, uint32_t pointerCount,
- const PointerProperties* pointerProperties,
+ MotionClassification classification, const ui::Transform& transform,
+ float xPrecision, float yPrecision, float xCursorPosition,
+ float yCursorPosition, nsecs_t downTime, nsecs_t eventTime,
+ uint32_t pointerCount, const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords);
/* Publishes a focus event to the input channel.
@@ -311,22 +366,60 @@
*/
status_t publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus, bool inTouchMode);
- /* Receives the finished signal from the consumer in reply to the original dispatch signal.
- * If a signal was received, returns the message sequence number,
- * and whether the consumer handled the message.
- *
- * The returned sequence number is never 0 unless the operation failed.
+ /* Publishes a capture event to the input channel.
*
* Returns OK on success.
- * Returns WOULD_BLOCK if there is no signal present.
+ * Returns WOULD_BLOCK if the channel is full.
* Returns DEAD_OBJECT if the channel's peer has been closed.
* Other errors probably indicate that the channel is broken.
*/
- status_t receiveFinishedSignal(uint32_t* outSeq, bool* outHandled);
+ status_t publishCaptureEvent(uint32_t seq, int32_t eventId, bool pointerCaptureEnabled);
+
+ /* Publishes a drag event to the input channel.
+ *
+ * Returns OK on success.
+ * Returns WOULD_BLOCK if the channel is full.
+ * Returns DEAD_OBJECT if the channel's peer has been closed.
+ * Other errors probably indicate that the channel is broken.
+ */
+ status_t publishDragEvent(uint32_t seq, int32_t eventId, float x, float y, bool isExiting);
+
+ struct Finished {
+ uint32_t seq;
+ bool handled;
+ nsecs_t consumeTime;
+ };
+
+ struct Timeline {
+ int32_t inputEventId;
+ std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
+ };
+
+ typedef std::variant<Finished, Timeline> ConsumerResponse;
+ /* Receive a signal from the consumer in reply to the original dispatch signal.
+ * If a signal was received, returns a Finished or a Timeline object.
+ * The InputConsumer should return a Finished object for every InputMessage that it is sent
+ * to confirm that it has been processed and that the InputConsumer is responsive.
+ * If several InputMessages are sent to InputConsumer, it's possible to receive Finished
+ * events out of order for those messages.
+ *
+ * The Timeline object is returned whenever the receiving end has processed a graphical frame
+ * and is returning the timeline of the frame. Not all input events will cause a Timeline
+ * object to be returned, and there is not guarantee about when it will arrive.
+ *
+ * If an object of Finished is returned, the returned sequence number is never 0 unless the
+ * operation failed.
+ *
+ * Returned error codes:
+ * OK on success.
+ * WOULD_BLOCK if there is no signal present.
+ * DEAD_OBJECT if the channel's peer has been closed.
+ * Other errors probably indicate that the channel is broken.
+ */
+ android::base::Result<ConsumerResponse> receiveConsumerResponse();
private:
-
- sp<InputChannel> mChannel;
+ std::shared_ptr<InputChannel> mChannel;
};
/*
@@ -335,13 +428,13 @@
class InputConsumer {
public:
/* Creates a consumer associated with an input channel. */
- explicit InputConsumer(const sp<InputChannel>& channel);
+ explicit InputConsumer(const std::shared_ptr<InputChannel>& channel);
/* Destroys the consumer and releases its input channel. */
~InputConsumer();
/* Gets the underlying input channel. */
- inline sp<InputChannel> getChannel() { return mChannel; }
+ inline std::shared_ptr<InputChannel> getChannel() { return mChannel; }
/* Consumes an input event from the input channel and copies its contents into
* an InputEvent object created using the specified factory.
@@ -379,6 +472,9 @@
*/
status_t sendFinishedSignal(uint32_t seq, bool handled);
+ status_t sendTimeline(int32_t inputEventId,
+ std::array<nsecs_t, GraphicsTimeline::SIZE> timeline);
+
/* Returns true if there is a deferred event waiting.
*
* Should be called after calling consume() to determine whether the consumer
@@ -411,12 +507,13 @@
*/
int32_t getPendingBatchSource() const;
+ std::string dump() const;
+
private:
// True if touch resampling is enabled.
const bool mResampleTouch;
- // The input channel.
- sp<InputChannel> mChannel;
+ std::shared_ptr<InputChannel> mChannel;
// The current input message.
InputMessage mMsg;
@@ -427,9 +524,9 @@
// Batched motion events per device and source.
struct Batch {
- Vector<InputMessage> samples;
+ std::vector<InputMessage> samples;
};
- Vector<Batch> mBatches;
+ std::vector<Batch> mBatches;
// Touch state per device and source, only for sources of class pointer.
struct History {
@@ -516,7 +613,7 @@
return false;
}
};
- Vector<TouchState> mTouchStates;
+ std::vector<TouchState> mTouchStates;
// Chain of batched sequence numbers. When multiple input messages are combined into
// a batch, we append a record here that associates the last sequence number in the
@@ -526,7 +623,14 @@
uint32_t seq; // sequence number of batched input message
uint32_t chain; // sequence number of previous batched input message
};
- Vector<SeqChain> mSeqChains;
+ std::vector<SeqChain> mSeqChains;
+
+ // The time at which each event with the sequence number 'seq' was consumed.
+ // This data is provided in 'finishInputEvent' so that the receiving end can measure the latency
+ // This collection is populated when the event is received, and the entries are erased when the
+ // events are finished. It should not grow infinitely because if an event is not ack'd, ANR
+ // will be raised for that connection, and no further events will be posted to that channel.
+ std::unordered_map<uint32_t /*seq*/, nsecs_t /*consumeTime*/> mConsumeTimes;
status_t consumeBatch(InputEventFactoryInterface* factory,
nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent);
@@ -540,12 +644,16 @@
ssize_t findBatch(int32_t deviceId, int32_t source) const;
ssize_t findTouchState(int32_t deviceId, int32_t source) const;
+ nsecs_t getConsumeTime(uint32_t seq) const;
+ void popConsumeTime(uint32_t seq);
status_t sendUnchainedFinishedSignal(uint32_t seq, bool handled);
static void rewriteMessage(TouchState& state, InputMessage& msg);
static void initializeKeyEvent(KeyEvent* event, const InputMessage* msg);
static void initializeMotionEvent(MotionEvent* event, const InputMessage* msg);
static void initializeFocusEvent(FocusEvent* event, const InputMessage* msg);
+ static void initializeCaptureEvent(CaptureEvent* event, const InputMessage* msg);
+ static void initializeDragEvent(DragEvent* event, const InputMessage* msg);
static void addSample(MotionEvent* event, const InputMessage* msg);
static bool canAddSample(const Batch& batch, const InputMessage* msg);
static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time);
diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h
index 2dac5b6..36097d6 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -17,105 +17,117 @@
#ifndef _UI_INPUT_WINDOW_H
#define _UI_INPUT_WINDOW_H
+#include <android/os/TouchOcclusionMode.h>
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <input/Flags.h>
#include <input/Input.h>
#include <input/InputTransport.h>
#include <ui/Rect.h>
#include <ui/Region.h>
+#include <ui/Transform.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>
#include "InputApplication.h"
+using android::os::TouchOcclusionMode;
+
namespace android {
-class Parcel;
/*
* Describes the properties of a window that can receive input.
*/
-struct InputWindowInfo {
+struct InputWindowInfo : public Parcelable {
InputWindowInfo() = default;
- InputWindowInfo(const Parcel& from);
// Window flags from WindowManager.LayoutParams
- enum {
- FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001,
- FLAG_DIM_BEHIND = 0x00000002,
- FLAG_BLUR_BEHIND = 0x00000004,
- FLAG_NOT_FOCUSABLE = 0x00000008,
- FLAG_NOT_TOUCHABLE = 0x00000010,
- FLAG_NOT_TOUCH_MODAL = 0x00000020,
- FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040,
- FLAG_KEEP_SCREEN_ON = 0x00000080,
- FLAG_LAYOUT_IN_SCREEN = 0x00000100,
- FLAG_LAYOUT_NO_LIMITS = 0x00000200,
- FLAG_FULLSCREEN = 0x00000400,
- FLAG_FORCE_NOT_FULLSCREEN = 0x00000800,
- FLAG_DITHER = 0x00001000,
- FLAG_SECURE = 0x00002000,
- FLAG_SCALED = 0x00004000,
- FLAG_IGNORE_CHEEK_PRESSES = 0x00008000,
- FLAG_LAYOUT_INSET_DECOR = 0x00010000,
- FLAG_ALT_FOCUSABLE_IM = 0x00020000,
- FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000,
- FLAG_SHOW_WHEN_LOCKED = 0x00080000,
- FLAG_SHOW_WALLPAPER = 0x00100000,
- FLAG_TURN_SCREEN_ON = 0x00200000,
- FLAG_DISMISS_KEYGUARD = 0x00400000,
- FLAG_SPLIT_TOUCH = 0x00800000,
- FLAG_SLIPPERY = 0x20000000,
- };
+ enum class Flag : uint32_t {
+ ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001,
+ DIM_BEHIND = 0x00000002,
+ BLUR_BEHIND = 0x00000004,
+ NOT_FOCUSABLE = 0x00000008,
+ NOT_TOUCHABLE = 0x00000010,
+ NOT_TOUCH_MODAL = 0x00000020,
+ TOUCHABLE_WHEN_WAKING = 0x00000040,
+ KEEP_SCREEN_ON = 0x00000080,
+ LAYOUT_IN_SCREEN = 0x00000100,
+ LAYOUT_NO_LIMITS = 0x00000200,
+ FULLSCREEN = 0x00000400,
+ FORCE_NOT_FULLSCREEN = 0x00000800,
+ DITHER = 0x00001000,
+ SECURE = 0x00002000,
+ SCALED = 0x00004000,
+ IGNORE_CHEEK_PRESSES = 0x00008000,
+ LAYOUT_INSET_DECOR = 0x00010000,
+ ALT_FOCUSABLE_IM = 0x00020000,
+ WATCH_OUTSIDE_TOUCH = 0x00040000,
+ SHOW_WHEN_LOCKED = 0x00080000,
+ SHOW_WALLPAPER = 0x00100000,
+ TURN_SCREEN_ON = 0x00200000,
+ DISMISS_KEYGUARD = 0x00400000,
+ SPLIT_TOUCH = 0x00800000,
+ HARDWARE_ACCELERATED = 0x01000000,
+ LAYOUT_IN_OVERSCAN = 0x02000000,
+ TRANSLUCENT_STATUS = 0x04000000,
+ TRANSLUCENT_NAVIGATION = 0x08000000,
+ LOCAL_FOCUS_MODE = 0x10000000,
+ SLIPPERY = 0x20000000,
+ LAYOUT_ATTACHED_IN_DECOR = 0x40000000,
+ DRAWS_SYSTEM_BAR_BACKGROUNDS = 0x80000000,
+ }; // Window types from WindowManager.LayoutParams
- // Window types from WindowManager.LayoutParams
- enum {
+ enum class Type : int32_t {
+ UNKNOWN = 0,
FIRST_APPLICATION_WINDOW = 1,
- TYPE_BASE_APPLICATION = 1,
- TYPE_APPLICATION = 2,
- TYPE_APPLICATION_STARTING = 3,
+ BASE_APPLICATION = 1,
+ APPLICATION = 2,
+ APPLICATION_STARTING = 3,
LAST_APPLICATION_WINDOW = 99,
FIRST_SUB_WINDOW = 1000,
- TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW,
- TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1,
- TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2,
- TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3,
- TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4,
+ APPLICATION_PANEL = FIRST_SUB_WINDOW,
+ APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1,
+ APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2,
+ APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3,
+ APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4,
LAST_SUB_WINDOW = 1999,
FIRST_SYSTEM_WINDOW = 2000,
- TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW,
- TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW + 1,
- TYPE_PHONE = FIRST_SYSTEM_WINDOW + 2,
- TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW + 3,
- TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW + 4,
- TYPE_TOAST = FIRST_SYSTEM_WINDOW + 5,
- TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 6,
- TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW + 7,
- TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW + 8,
- TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW + 9,
- TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW + 10,
- TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW + 11,
- TYPE_INPUT_METHOD_DIALOG = FIRST_SYSTEM_WINDOW + 12,
- TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW + 13,
- TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW + 14,
- TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 15,
- TYPE_DRAG = FIRST_SYSTEM_WINDOW + 16,
- TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW + 17,
- TYPE_POINTER = FIRST_SYSTEM_WINDOW + 18,
- TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW + 19,
- TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW + 20,
- TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW + 21,
- TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW + 22,
- TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW + 24,
- TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 27,
- TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW + 32,
- TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW + 34,
- TYPE_NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW + 40,
- TYPE_TRUSTED_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 42,
+ STATUS_BAR = FIRST_SYSTEM_WINDOW,
+ SEARCH_BAR = FIRST_SYSTEM_WINDOW + 1,
+ PHONE = FIRST_SYSTEM_WINDOW + 2,
+ SYSTEM_ALERT = FIRST_SYSTEM_WINDOW + 3,
+ KEYGUARD = FIRST_SYSTEM_WINDOW + 4,
+ TOAST = FIRST_SYSTEM_WINDOW + 5,
+ SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 6,
+ PRIORITY_PHONE = FIRST_SYSTEM_WINDOW + 7,
+ SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW + 8,
+ KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW + 9,
+ SYSTEM_ERROR = FIRST_SYSTEM_WINDOW + 10,
+ INPUT_METHOD = FIRST_SYSTEM_WINDOW + 11,
+ INPUT_METHOD_DIALOG = FIRST_SYSTEM_WINDOW + 12,
+ WALLPAPER = FIRST_SYSTEM_WINDOW + 13,
+ STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW + 14,
+ SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 15,
+ DRAG = FIRST_SYSTEM_WINDOW + 16,
+ STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW + 17,
+ POINTER = FIRST_SYSTEM_WINDOW + 18,
+ NAVIGATION_BAR = FIRST_SYSTEM_WINDOW + 19,
+ VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW + 20,
+ BOOT_PROGRESS = FIRST_SYSTEM_WINDOW + 21,
+ INPUT_CONSUMER = FIRST_SYSTEM_WINDOW + 22,
+ NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW + 24,
+ MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 27,
+ ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW + 32,
+ DOCK_DIVIDER = FIRST_SYSTEM_WINDOW + 34,
+ ACCESSIBILITY_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 39,
+ NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW + 40,
LAST_SYSTEM_WINDOW = 2999,
};
- enum {
- INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES = 0x00000001,
- INPUT_FEATURE_NO_INPUT_CHANNEL = 0x00000002,
- INPUT_FEATURE_DISABLE_USER_ACTIVITY = 0x00000004,
+ enum class Feature {
+ DISABLE_TOUCH_PAD_GESTURES = 0x00000001,
+ NO_INPUT_CHANNEL = 0x00000002,
+ DISABLE_USER_ACTIVITY = 0x00000004,
};
/* These values are filled in by the WM and passed through SurfaceFlinger
@@ -127,9 +139,9 @@
// This uniquely identifies the input window.
int32_t id = -1;
std::string name;
- int32_t layoutParamsFlags = 0;
- int32_t layoutParamsType = 0;
- nsecs_t dispatchingTimeout = -1;
+ Flags<Flag> flags;
+ Type type = Type::UNKNOWN;
+ std::chrono::nanoseconds dispatchingTimeout = std::chrono::seconds(5);
/* These values are filled in by SurfaceFlinger. */
int32_t frameLeft = -1;
@@ -149,9 +161,12 @@
// in scaling of the TOUCH_MAJOR/TOUCH_MINOR axis.
float globalScaleFactor = 1.0f;
- // Scaling factors applied to individual windows.
- float windowXScale = 1.0f;
- float windowYScale = 1.0f;
+ // The opacity of this window, from 0.0 to 1.0 (inclusive).
+ // An alpha of 1.0 means fully opaque and 0.0 means fully transparent.
+ float alpha;
+
+ // Transform applied to individual windows.
+ ui::Transform transform;
/*
* This is filled in by the WM relative to the frame and then translated
@@ -159,13 +174,20 @@
*/
Region touchableRegion;
bool visible = false;
- bool canReceiveKeys = false;
- bool hasFocus = false;
+ bool focusable = false;
bool hasWallpaper = false;
bool paused = false;
+ /* This flag is set when the window is of a trusted type that is allowed to silently
+ * overlay other windows for the purpose of implementing the secure views feature.
+ * Trusted overlays, such as IME windows, can partly obscure other windows without causing
+ * motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED.
+ */
+ bool trustedOverlay = false;
+ TouchOcclusionMode touchOcclusionMode = TouchOcclusionMode::BLOCK_UNTRUSTED;
int32_t ownerPid = -1;
int32_t ownerUid = -1;
- int32_t inputFeatures = 0;
+ std::string packageName;
+ Flags<Feature> inputFeatures;
int32_t displayId = ADISPLAY_ID_NONE;
int32_t portalToDisplayId = ADISPLAY_ID_NONE;
InputApplicationInfo applicationInfo;
@@ -175,23 +197,19 @@
void addTouchableRegion(const Rect& region);
bool touchableRegionContainsPoint(int32_t x, int32_t y) const;
- bool frameContainsPoint(int32_t x, int32_t y) const;
- /* Returns true if the window is of a trusted type that is allowed to silently
- * overlay other windows for the purpose of implementing the secure views feature.
- * Trusted overlays, such as IME windows, can partly obscure other windows without causing
- * motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED.
- */
- bool isTrustedOverlay() const;
+ bool frameContainsPoint(int32_t x, int32_t y) const;
bool supportsSplitTouch() const;
bool overlaps(const InputWindowInfo* other) const;
- status_t write(Parcel& output) const;
- static InputWindowInfo read(const Parcel& from);
-};
+ bool operator==(const InputWindowInfo& inputChannel) const;
+ status_t writeToParcel(android::Parcel* parcel) const override;
+
+ status_t readFromParcel(const android::Parcel* parcel) override;
+};
/*
* Handle for a window that can receive input.
@@ -201,26 +219,19 @@
*/
class InputWindowHandle : public RefBase {
public:
+ explicit InputWindowHandle();
+ InputWindowHandle(const InputWindowHandle& other);
+ InputWindowHandle(const InputWindowInfo& other);
- inline const InputWindowInfo* getInfo() const {
- return &mInfo;
- }
+ inline const InputWindowInfo* getInfo() const { return &mInfo; }
sp<IBinder> getToken() const;
int32_t getId() const { return mInfo.id; }
- sp<IBinder> getApplicationToken() {
- return mInfo.applicationInfo.token;
- }
+ sp<IBinder> getApplicationToken() { return mInfo.applicationInfo.token; }
- inline std::string getName() const {
- return !mInfo.name.empty() ? mInfo.name : "<invalid>";
- }
-
- inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const {
- return mInfo.token ? mInfo.dispatchingTimeout : defaultValue;
- }
+ inline std::string getName() const { return !mInfo.name.empty() ? mInfo.name : "<invalid>"; }
inline std::chrono::nanoseconds getDispatchingTimeout(
std::chrono::nanoseconds defaultValue) const {
@@ -230,13 +241,14 @@
/**
* Requests that the state of this object be updated to reflect
* the most current available information about the application.
+ * As this class is created as RefBase object, no pure virtual function is allowed.
*
* This method should only be called from within the input dispatcher's
* critical section.
*
* Returns true on success, or false if the handle is no longer valid.
*/
- virtual bool updateInfo() = 0;
+ virtual bool updateInfo() { return false; }
/**
* Updates from another input window handle.
@@ -249,13 +261,15 @@
*/
void releaseChannel();
+ // Not override since this class is not derrived from Parcelable.
+ status_t readFromParcel(const android::Parcel* parcel);
+ status_t writeToParcel(android::Parcel* parcel) const;
+
protected:
- explicit InputWindowHandle();
virtual ~InputWindowHandle();
InputWindowInfo mInfo;
};
-
} // namespace android
#endif // _UI_INPUT_WINDOW_H
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index a1a32a6..451ca3c 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -19,16 +19,16 @@
#include <stdint.h>
-#ifdef __ANDROID__
+#ifdef __linux__
#include <binder/IBinder.h>
#endif
+#include <android-base/result.h>
#include <input/Input.h>
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
#include <utils/Tokenizer.h>
#include <utils/Unicode.h>
-#include <utils/RefBase.h>
// Maximum number of keys supported by KeyCharacterMaps
#define MAX_KEYS 8192
@@ -42,29 +42,29 @@
*
* This object is immutable after it has been loaded.
*/
-class KeyCharacterMap : public RefBase {
+class KeyCharacterMap {
public:
- enum KeyboardType {
- KEYBOARD_TYPE_UNKNOWN = 0,
- KEYBOARD_TYPE_NUMERIC = 1,
- KEYBOARD_TYPE_PREDICTIVE = 2,
- KEYBOARD_TYPE_ALPHA = 3,
- KEYBOARD_TYPE_FULL = 4,
+ enum class KeyboardType : int32_t {
+ UNKNOWN = 0,
+ NUMERIC = 1,
+ PREDICTIVE = 2,
+ ALPHA = 3,
+ FULL = 4,
/**
* Deprecated. Set 'keyboard.specialFunction' to '1' in the device's IDC file instead.
*/
- KEYBOARD_TYPE_SPECIAL_FUNCTION = 5,
- KEYBOARD_TYPE_OVERLAY = 6,
+ SPECIAL_FUNCTION = 5,
+ OVERLAY = 6,
};
- enum Format {
+ enum class Format {
// Base keyboard layout, may contain device-specific options, such as "type" declaration.
- FORMAT_BASE = 0,
+ BASE = 0,
// Overlay keyboard layout, more restrictive, may be published by applications,
// cannot override device-specific options.
- FORMAT_OVERLAY = 1,
+ OVERLAY = 1,
// Either base or overlay layout ok.
- FORMAT_ANY = 2,
+ ANY = 2,
};
// Substitute key code and meta state for fallback action.
@@ -74,21 +74,21 @@
};
/* Loads a key character map from a file. */
- static status_t load(const std::string& filename, Format format, sp<KeyCharacterMap>* outMap);
+ static base::Result<std::shared_ptr<KeyCharacterMap>> load(const std::string& filename,
+ Format format);
/* Loads a key character map from its string contents. */
- static status_t loadContents(const std::string& filename,
- const char* contents, Format format, sp<KeyCharacterMap>* outMap);
+ static base::Result<std::shared_ptr<KeyCharacterMap>> loadContents(const std::string& filename,
+ const char* contents,
+ Format format);
- /* Combines a base key character map and an overlay. */
- static sp<KeyCharacterMap> combine(const sp<KeyCharacterMap>& base,
- const sp<KeyCharacterMap>& overlay);
+ const std::string getLoadFileName() const;
- /* Returns an empty key character map. */
- static sp<KeyCharacterMap> empty();
+ /* Combines this key character map with an overlay. */
+ void combine(const KeyCharacterMap& overlay);
/* Gets the keyboard type. */
- int32_t getKeyboardType() const;
+ KeyboardType getKeyboardType() const;
/* Gets the primary character for this key as in the label physically printed on it.
* Returns 0 if none (eg. for non-printing keys). */
@@ -134,15 +134,18 @@
void tryRemapKey(int32_t scanCode, int32_t metaState,
int32_t* outKeyCode, int32_t* outMetaState) const;
-#ifdef __ANDROID__
+#ifdef __linux__
/* Reads a key map from a parcel. */
- static sp<KeyCharacterMap> readFromParcel(Parcel* parcel);
+ static std::shared_ptr<KeyCharacterMap> readFromParcel(Parcel* parcel);
/* Writes a key map to a parcel. */
void writeToParcel(Parcel* parcel) const;
#endif
-protected:
+ bool operator==(const KeyCharacterMap& other) const;
+
+ KeyCharacterMap(const KeyCharacterMap& other);
+
virtual ~KeyCharacterMap();
private:
@@ -224,16 +227,14 @@
status_t parseCharacterLiteral(char16_t* outCharacter);
};
- static sp<KeyCharacterMap> sEmpty;
-
KeyedVector<int32_t, Key*> mKeys;
- int mType;
+ KeyboardType mType;
+ std::string mLoadFileName;
KeyedVector<int32_t, int32_t> mKeysByScanCode;
KeyedVector<int32_t, int32_t> mKeysByUsageCode;
KeyCharacterMap();
- KeyCharacterMap(const KeyCharacterMap& other);
bool getKey(int32_t keyCode, const Key** outKey) const;
bool getKeyBehavior(int32_t keyCode, int32_t metaState,
@@ -242,7 +243,7 @@
bool findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const;
- static status_t load(Tokenizer* tokenizer, Format format, sp<KeyCharacterMap>* outMap);
+ static base::Result<std::shared_ptr<KeyCharacterMap>> load(Tokenizer* tokenizer, Format format);
static void addKey(Vector<KeyEvent>& outEvents,
int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time);
diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h
index 26f3501..b2bd535 100644
--- a/include/input/KeyLayoutMap.h
+++ b/include/input/KeyLayoutMap.h
@@ -17,11 +17,14 @@
#ifndef _LIBINPUT_KEY_LAYOUT_MAP_H
#define _LIBINPUT_KEY_LAYOUT_MAP_H
+#include <android-base/result.h>
#include <stdint.h>
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
-#include <utils/Tokenizer.h>
#include <utils/RefBase.h>
+#include <utils/Tokenizer.h>
+
+#include <input/InputDevice.h>
namespace android {
@@ -60,9 +63,12 @@
*
* This object is immutable after it has been loaded.
*/
-class KeyLayoutMap : public RefBase {
+class KeyLayoutMap {
public:
- static status_t load(const std::string& filename, sp<KeyLayoutMap>* outMap);
+ static base::Result<std::shared_ptr<KeyLayoutMap>> load(const std::string& filename);
+ static base::Result<std::shared_ptr<KeyLayoutMap>> load(Tokenizer* tokenizer);
+ static base::Result<std::shared_ptr<KeyLayoutMap>> loadContents(const std::string& filename,
+ const char* contents);
status_t mapKey(int32_t scanCode, int32_t usageCode,
int32_t* outKeyCode, uint32_t* outFlags) const;
@@ -71,8 +77,10 @@
status_t findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCode) const;
status_t mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const;
+ const std::string getLoadFileName() const;
+ // Return pair of sensor type and sensor data index, for the input device abs code
+ base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(int32_t absCode);
-protected:
virtual ~KeyLayoutMap();
private:
@@ -85,12 +93,18 @@
int32_t ledCode;
};
+ struct Sensor {
+ InputDeviceSensorType sensorType;
+ int32_t sensorDataIndex;
+ };
KeyedVector<int32_t, Key> mKeysByScanCode;
KeyedVector<int32_t, Key> mKeysByUsageCode;
KeyedVector<int32_t, AxisInfo> mAxes;
KeyedVector<int32_t, Led> mLedsByScanCode;
KeyedVector<int32_t, Led> mLedsByUsageCode;
+ std::unordered_map<int32_t, Sensor> mSensorsByAbsCode;
+ std::string mLoadFileName;
KeyLayoutMap();
@@ -109,6 +123,7 @@
status_t parseKey();
status_t parseAxis();
status_t parseLed();
+ status_t parseSensor();
};
};
diff --git a/include/input/Keyboard.h b/include/input/Keyboard.h
index 0a00241..08ad8c6 100644
--- a/include/input/Keyboard.h
+++ b/include/input/Keyboard.h
@@ -34,10 +34,10 @@
class KeyMap {
public:
std::string keyLayoutFile;
- sp<KeyLayoutMap> keyLayoutMap;
+ std::shared_ptr<KeyLayoutMap> keyLayoutMap;
std::string keyCharacterMapFile;
- sp<KeyCharacterMap> keyCharacterMap;
+ std::shared_ptr<KeyCharacterMap> keyCharacterMap;
KeyMap();
~KeyMap();
diff --git a/include/input/NamedEnum.h b/include/input/NamedEnum.h
new file mode 100644
index 0000000..6562348
--- /dev/null
+++ b/include/input/NamedEnum.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2020 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 <android-base/stringprintf.h>
+
+#include <array>
+#include <cstdint>
+#include <optional>
+#include <string>
+
+#ifndef __UI_INPUT_NAMEDENUM_H
+#define __UI_INPUT_NAMEDENUM_H
+
+namespace android {
+
+namespace details {
+template <typename E, E V>
+constexpr std::optional<std::string_view> enum_value_name() {
+ // Should look something like (but all on one line):
+ // std::optional<std::string_view>
+ // android::details::enum_value_name()
+ // [E = android::test::TestEnums, V = android::test::TestEnums::ONE]
+ std::string_view view = __PRETTY_FUNCTION__;
+ size_t templateStart = view.rfind("[");
+ size_t templateEnd = view.rfind("]");
+ if (templateStart == std::string::npos || templateEnd == std::string::npos) {
+ return std::nullopt;
+ }
+
+ // Extract the template parameters without the enclosing braces.
+ // Example (cont'd): E = android::test::TestEnums, V = android::test::TestEnums::ONE
+ view = view.substr(templateStart + 1, templateEnd - templateStart - 1);
+ size_t valStart = view.rfind("V = ");
+ if (valStart == std::string::npos) {
+ return std::nullopt;
+ }
+
+ // Example (cont'd): V = android::test::TestEnums::ONE
+ view = view.substr(valStart);
+ size_t nameStart = view.rfind("::");
+ if (nameStart == std::string::npos) {
+ return std::nullopt;
+ }
+
+ // Chop off the initial "::"
+ nameStart += 2;
+ return view.substr(nameStart);
+}
+
+template <typename E, typename T, T... I>
+constexpr auto generate_enum_values(std::integer_sequence<T, I...> seq) {
+ constexpr size_t count = seq.size();
+
+ std::array<E, count> values{};
+ for (size_t i = 0, v = 0; v < count; ++i) {
+ values[v++] = static_cast<E>(T{0} + i);
+ }
+
+ return values;
+}
+
+template <typename E, std::size_t N>
+inline constexpr auto enum_values =
+ generate_enum_values<E>(std::make_integer_sequence<std::underlying_type_t<E>, N>{});
+
+template <typename E, std::size_t N, std::size_t... I>
+constexpr auto generate_enum_names(std::index_sequence<I...>) noexcept {
+ return std::array<std::optional<std::string_view>, sizeof...(I)>{
+ {enum_value_name<E, enum_values<E, N>[I]>()...}};
+}
+
+template <typename E, std::size_t N>
+inline constexpr auto enum_names = generate_enum_names<E, N>(std::make_index_sequence<N>{});
+
+} // namespace details
+
+class NamedEnum {
+public:
+ // By default allowed enum value range is 0 ~ 7.
+ template <typename E>
+ static constexpr size_t max = 8;
+
+ template <auto V>
+ static constexpr auto enum_name() {
+ using E = decltype(V);
+ return details::enum_value_name<E, V>();
+ }
+
+ template <typename E>
+ static constexpr std::optional<std::string_view> enum_name(E val) {
+ auto idx = static_cast<size_t>(val);
+ return idx < max<E> ? details::enum_names<E, max<E>>[idx] : std::nullopt;
+ }
+
+ // Helper function for parsing enum value to string.
+ // Example : enum class TestEnums { ZERO = 0x0 };
+ // NamedEnum::string(TestEnums::ZERO) returns string of "ZERO".
+ // Note the default maximum enum is 8, if the enum ID to be parsed if greater than 8 like 16,
+ // it should be declared to specialized the maximum enum by below:
+ // template <> constexpr size_t NamedEnum::max<TestEnums> = 16;
+ // If the enum class definition is sparse and contains enum values starting from a large value,
+ // Do not specialize it to a large number to avoid performance issues.
+ // The recommended maximum enum number to specialize is 64.
+ template <typename E>
+ static const std::string string(E val, const char* fallbackFormat = "%02d") {
+ std::string result;
+ std::optional<std::string_view> enumString = enum_name(val);
+ result += enumString ? enumString.value() : base::StringPrintf(fallbackFormat, val);
+ return result;
+ }
+};
+
+} // namespace android
+
+#endif // __UI_INPUT_NAMEDENUM_H
\ No newline at end of file
diff --git a/include/input/PropertyMap.h b/include/input/PropertyMap.h
index 3d04331..451918b 100644
--- a/include/input/PropertyMap.h
+++ b/include/input/PropertyMap.h
@@ -17,6 +17,7 @@
#ifndef _UTILS_PROPERTY_MAP_H
#define _UTILS_PROPERTY_MAP_H
+#include <android-base/result.h>
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
#include <utils/String8.h>
@@ -78,7 +79,7 @@
inline const KeyedVector<String8, String8>& getProperties() const { return mProperties; }
/* Loads a property map from a file. */
- static status_t load(const String8& filename, PropertyMap** outMap);
+ static android::base::Result<std::unique_ptr<PropertyMap>> load(const char* filename);
private:
class Parser {
diff --git a/include/input/TouchVideoFrame.h b/include/input/TouchVideoFrame.h
index 4fa2f86..eda628e 100644
--- a/include/input/TouchVideoFrame.h
+++ b/include/input/TouchVideoFrame.h
@@ -57,7 +57,7 @@
/**
* Rotate the video frame.
- * The rotation value is an enum from ui/DisplayInfo.h
+ * The rotation value is an enum from ui/Rotation.h
*/
void rotate(int32_t orientation);
diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h
index 727865a..886f1f7 100644
--- a/include/input/VelocityTracker.h
+++ b/include/input/VelocityTracker.h
@@ -18,8 +18,8 @@
#define _LIBINPUT_VELOCITY_TRACKER_H
#include <input/Input.h>
-#include <utils/Timers.h>
#include <utils/BitSet.h>
+#include <utils/Timers.h>
namespace android {
@@ -30,6 +30,22 @@
*/
class VelocityTracker {
public:
+ enum class Strategy : int32_t {
+ DEFAULT = -1,
+ MIN = 0,
+ IMPULSE = 0,
+ LSQ1 = 1,
+ LSQ2 = 2,
+ LSQ3 = 3,
+ WLSQ2_DELTA = 4,
+ WLSQ2_CENTRAL = 5,
+ WLSQ2_RECENT = 6,
+ INT1 = 7,
+ INT2 = 8,
+ LEGACY = 9,
+ MAX = LEGACY,
+ };
+
struct Position {
float x, y;
};
@@ -62,8 +78,8 @@
};
// Creates a velocity tracker using the specified strategy.
- // If strategy is NULL, uses the default strategy for the platform.
- VelocityTracker(const char* strategy = nullptr);
+ // If strategy is not provided, uses the default strategy for the platform.
+ VelocityTracker(const Strategy strategy = Strategy::DEFAULT);
~VelocityTracker();
@@ -80,7 +96,7 @@
// are included in the movement.
// The positions array contains position information for each pointer in order by
// increasing id. Its size should be equal to the number of one bits in idBits.
- void addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions);
+ void addMovement(nsecs_t eventTime, BitSet32 idBits, const std::vector<Position>& positions);
// Adds movement information for all pointers in a MotionEvent, including historical samples.
void addMovement(const MotionEvent* event);
@@ -102,16 +118,21 @@
inline BitSet32 getCurrentPointerIdBits() const { return mCurrentPointerIdBits; }
private:
- static const char* DEFAULT_STRATEGY;
+ // The default velocity tracker strategy.
+ // Although other strategies are available for testing and comparison purposes,
+ // this is the strategy that applications will actually use. Be very careful
+ // when adjusting the default strategy because it can dramatically affect
+ // (often in a bad way) the user experience.
+ static const Strategy DEFAULT_STRATEGY = Strategy::LSQ2;
nsecs_t mLastEventTime;
BitSet32 mCurrentPointerIdBits;
int32_t mActivePointerId;
- VelocityTrackerStrategy* mStrategy;
+ std::unique_ptr<VelocityTrackerStrategy> mStrategy;
- bool configureStrategy(const char* strategy);
+ bool configureStrategy(const Strategy strategy);
- static VelocityTrackerStrategy* createStrategy(const char* strategy);
+ static std::unique_ptr<VelocityTrackerStrategy> createStrategy(const Strategy strategy);
};
@@ -128,7 +149,7 @@
virtual void clear() = 0;
virtual void clearPointers(BitSet32 idBits) = 0;
virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
- const VelocityTracker::Position* positions) = 0;
+ const std::vector<VelocityTracker::Position>& positions) = 0;
virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const = 0;
};
@@ -159,8 +180,8 @@
virtual void clear();
virtual void clearPointers(BitSet32 idBits);
- virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
- const VelocityTracker::Position* positions);
+ void addMovement(nsecs_t eventTime, BitSet32 idBits,
+ const std::vector<VelocityTracker::Position>& positions) override;
virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
private:
@@ -202,8 +223,8 @@
virtual void clear();
virtual void clearPointers(BitSet32 idBits);
- virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
- const VelocityTracker::Position* positions);
+ void addMovement(nsecs_t eventTime, BitSet32 idBits,
+ const std::vector<VelocityTracker::Position>& positions) override;
virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
private:
@@ -236,8 +257,8 @@
virtual void clear();
virtual void clearPointers(BitSet32 idBits);
- virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
- const VelocityTracker::Position* positions);
+ void addMovement(nsecs_t eventTime, BitSet32 idBits,
+ const std::vector<VelocityTracker::Position>& positions) override;
virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
private:
@@ -271,8 +292,8 @@
virtual void clear();
virtual void clearPointers(BitSet32 idBits);
- virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
- const VelocityTracker::Position* positions);
+ void addMovement(nsecs_t eventTime, BitSet32 idBits,
+ const std::vector<VelocityTracker::Position>& positions) override;
virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
private:
diff --git a/include/powermanager/IPowerManager.h b/include/powermanager/IPowerManager.h
deleted file mode 100644
index 964e318..0000000
--- a/include/powermanager/IPowerManager.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-#ifndef ANDROID_IPOWERMANAGER_H
-#define ANDROID_IPOWERMANAGER_H
-
-#include <utils/Errors.h>
-#include <binder/IInterface.h>
-#include <hardware/power.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-class IPowerManager : public IInterface
-{
-public:
- // These transaction IDs must be kept in sync with the method order from
- // IPowerManager.aidl.
- enum {
- ACQUIRE_WAKE_LOCK = IBinder::FIRST_CALL_TRANSACTION,
- ACQUIRE_WAKE_LOCK_UID = IBinder::FIRST_CALL_TRANSACTION + 1,
- RELEASE_WAKE_LOCK = IBinder::FIRST_CALL_TRANSACTION + 2,
- UPDATE_WAKE_LOCK_UIDS = IBinder::FIRST_CALL_TRANSACTION + 3,
- POWER_HINT = IBinder::FIRST_CALL_TRANSACTION + 4,
- UPDATE_WAKE_LOCK_SOURCE = IBinder::FIRST_CALL_TRANSACTION + 5,
- IS_WAKE_LOCK_LEVEL_SUPPORTED = IBinder::FIRST_CALL_TRANSACTION + 6,
- USER_ACTIVITY = IBinder::FIRST_CALL_TRANSACTION + 7,
- WAKE_UP = IBinder::FIRST_CALL_TRANSACTION + 8,
- GO_TO_SLEEP = IBinder::FIRST_CALL_TRANSACTION + 9,
- NAP = IBinder::FIRST_CALL_TRANSACTION + 10,
- IS_INTERACTIVE = IBinder::FIRST_CALL_TRANSACTION + 11,
- IS_POWER_SAVE_MODE = IBinder::FIRST_CALL_TRANSACTION + 12,
- GET_POWER_SAVE_STATE = IBinder::FIRST_CALL_TRANSACTION + 13,
- SET_POWER_SAVE_MODE_ENABLED = IBinder::FIRST_CALL_TRANSACTION + 14,
- REBOOT = IBinder::FIRST_CALL_TRANSACTION + 21,
- REBOOT_SAFE_MODE = IBinder::FIRST_CALL_TRANSACTION + 22,
- SHUTDOWN = IBinder::FIRST_CALL_TRANSACTION + 23,
- CRASH = IBinder::FIRST_CALL_TRANSACTION + 24,
- };
-
- DECLARE_META_INTERFACE(PowerManager)
-
- // The parcels created by these methods must be kept in sync with the
- // corresponding methods from IPowerManager.aidl.
- // FIXME remove the bool isOneWay parameters as they are not oneway in the .aidl
- virtual status_t acquireWakeLock(int flags, const sp<IBinder>& lock, const String16& tag,
- const String16& packageName, bool isOneWay = false) = 0;
- virtual status_t acquireWakeLockWithUid(int flags, const sp<IBinder>& lock, const String16& tag,
- const String16& packageName, int uid, bool isOneWay = false) = 0;
- virtual status_t releaseWakeLock(const sp<IBinder>& lock, int flags, bool isOneWay = false) = 0;
- virtual status_t updateWakeLockUids(const sp<IBinder>& lock, int len, const int *uids,
- bool isOneWay = false) = 0;
- virtual status_t powerHint(int hintId, int data) = 0;
- virtual status_t goToSleep(int64_t event_time_ms, int reason, int flags) = 0;
- virtual status_t reboot(bool confirm, const String16& reason, bool wait) = 0;
- virtual status_t shutdown(bool confirm, const String16& reason, bool wait) = 0;
- virtual status_t crash(const String16& message) = 0;
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_IPOWERMANAGER_H
diff --git a/include/powermanager/PowerHalController.h b/include/powermanager/PowerHalController.h
new file mode 100644
index 0000000..71a36d0
--- /dev/null
+++ b/include/powermanager/PowerHalController.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef ANDROID_POWERHALCONTROLLER_H
+#define ANDROID_POWERHALCONTROLLER_H
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/IPowerHintSession.h>
+#include <android/hardware/power/Mode.h>
+#include <powermanager/PowerHalWrapper.h>
+
+namespace android {
+
+namespace power {
+
+// -------------------------------------------------------------------------------------------------
+
+// Connects to underlying Power HAL handles.
+class HalConnector {
+public:
+ HalConnector() = default;
+ virtual ~HalConnector() = default;
+
+ virtual std::unique_ptr<HalWrapper> connect();
+ virtual void reset();
+};
+
+// -------------------------------------------------------------------------------------------------
+
+// Controller for Power HAL handle.
+// This relies on HalConnector to connect to the underlying Power HAL
+// service and reconnects to it after each failed api call. This also ensures
+// connecting to the service is thread-safe.
+class PowerHalController : public HalWrapper {
+public:
+ PowerHalController() : PowerHalController(std::make_unique<HalConnector>()) {}
+ explicit PowerHalController(std::unique_ptr<HalConnector> connector)
+ : mHalConnector(std::move(connector)) {}
+ virtual ~PowerHalController() = default;
+
+ void init();
+
+ virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+ virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
+ virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+ int64_t durationNanos) override;
+ virtual HalResult<int64_t> getHintSessionPreferredRate() override;
+
+private:
+ std::mutex mConnectedHalMutex;
+ std::unique_ptr<HalConnector> mHalConnector;
+
+ // Shared pointers to keep global pointer and allow local copies to be used in
+ // different threads
+ std::shared_ptr<HalWrapper> mConnectedHal GUARDED_BY(mConnectedHalMutex) = nullptr;
+ const std::shared_ptr<HalWrapper> mDefaultHal = std::make_shared<EmptyHalWrapper>();
+
+ std::shared_ptr<HalWrapper> initHal();
+ template <typename T>
+ HalResult<T> processHalResult(HalResult<T> result, const char* functionName);
+};
+
+// -------------------------------------------------------------------------------------------------
+
+}; // namespace power
+
+}; // namespace android
+
+#endif // ANDROID_POWERHALCONTROLLER_H
diff --git a/include/powermanager/PowerHalLoader.h b/include/powermanager/PowerHalLoader.h
new file mode 100644
index 0000000..ed6f6f3
--- /dev/null
+++ b/include/powermanager/PowerHalLoader.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef ANDROID_POWERHALLOADER_H
+#define ANDROID_POWERHALLOADER_H
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/IPower.h>
+
+namespace android {
+
+namespace power {
+
+// Loads available Power HAL services.
+class PowerHalLoader {
+public:
+ static void unloadAll();
+ static sp<hardware::power::IPower> loadAidl();
+ static sp<hardware::power::V1_0::IPower> loadHidlV1_0();
+ static sp<hardware::power::V1_1::IPower> loadHidlV1_1();
+
+private:
+ static std::mutex gHalMutex;
+ static sp<hardware::power::IPower> gHalAidl GUARDED_BY(gHalMutex);
+ static sp<hardware::power::V1_0::IPower> gHalHidlV1_0 GUARDED_BY(gHalMutex);
+ static sp<hardware::power::V1_1::IPower> gHalHidlV1_1 GUARDED_BY(gHalMutex);
+
+ static sp<hardware::power::V1_0::IPower> loadHidlV1_0Locked()
+ EXCLUSIVE_LOCKS_REQUIRED(gHalMutex);
+
+ PowerHalLoader() = delete;
+ ~PowerHalLoader() = delete;
+};
+
+}; // namespace power
+
+} // namespace android
+
+#endif // ANDROID_POWERHALLOADER_H
diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h
new file mode 100644
index 0000000..2c6eacb
--- /dev/null
+++ b/include/powermanager/PowerHalWrapper.h
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef ANDROID_POWERHALWRAPPER_H
+#define ANDROID_POWERHALWRAPPER_H
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/IPowerHintSession.h>
+#include <android/hardware/power/Mode.h>
+
+namespace android {
+
+namespace power {
+
+// State of Power HAL support for individual apis.
+enum class HalSupport {
+ UNKNOWN = 0,
+ ON = 1,
+ OFF = 2,
+};
+
+// Result of a call to the Power HAL wrapper, holding data if successful.
+template <typename T>
+class HalResult {
+public:
+ static HalResult<T> ok(T value) { return HalResult(value); }
+ static HalResult<T> failed(std::string msg) {
+ return HalResult(std::move(msg), /* unsupported= */ false);
+ }
+ static HalResult<T> unsupported() { return HalResult("", /* unsupported= */ true); }
+
+ static HalResult<T> fromStatus(binder::Status status, T data) {
+ if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+ return HalResult<T>::unsupported();
+ }
+ if (status.isOk()) {
+ return HalResult<T>::ok(data);
+ }
+ return HalResult<T>::failed(std::string(status.toString8().c_str()));
+ }
+ static HalResult<T> fromStatus(hardware::power::V1_0::Status status, T data);
+
+ template <typename R>
+ static HalResult<T> fromReturn(hardware::Return<R>& ret, T data);
+
+ template <typename R>
+ static HalResult<T> fromReturn(hardware::Return<R>& ret, hardware::power::V1_0::Status status,
+ T data);
+
+ // This will throw std::bad_optional_access if this result is not ok.
+ const T& value() const { return mValue.value(); }
+ bool isOk() const { return !mUnsupported && mValue.has_value(); }
+ bool isFailed() const { return !mUnsupported && !mValue.has_value(); }
+ bool isUnsupported() const { return mUnsupported; }
+ const char* errorMessage() const { return mErrorMessage.c_str(); }
+
+private:
+ std::optional<T> mValue;
+ std::string mErrorMessage;
+ bool mUnsupported;
+
+ explicit HalResult(T value)
+ : mValue(std::make_optional(value)), mErrorMessage(), mUnsupported(false) {}
+ explicit HalResult(std::string errorMessage, bool unsupported)
+ : mValue(), mErrorMessage(std::move(errorMessage)), mUnsupported(unsupported) {}
+};
+
+// Empty result of a call to the Power HAL wrapper.
+template <>
+class HalResult<void> {
+public:
+ static HalResult<void> ok() { return HalResult(); }
+ static HalResult<void> failed(std::string msg) { return HalResult(std::move(msg)); }
+ static HalResult<void> unsupported() { return HalResult(/* unsupported= */ true); }
+
+ static HalResult<void> fromStatus(status_t status);
+ static HalResult<void> fromStatus(binder::Status status);
+ static HalResult<void> fromStatus(hardware::power::V1_0::Status status);
+
+ template <typename R>
+ static HalResult<void> fromReturn(hardware::Return<R>& ret);
+
+ bool isOk() const { return !mUnsupported && !mFailed; }
+ bool isFailed() const { return !mUnsupported && mFailed; }
+ bool isUnsupported() const { return mUnsupported; }
+ const char* errorMessage() const { return mErrorMessage.c_str(); }
+
+private:
+ std::string mErrorMessage;
+ bool mFailed;
+ bool mUnsupported;
+
+ explicit HalResult(bool unsupported = false)
+ : mErrorMessage(), mFailed(false), mUnsupported(unsupported) {}
+ explicit HalResult(std::string errorMessage)
+ : mErrorMessage(std::move(errorMessage)), mFailed(true), mUnsupported(false) {}
+};
+
+// Wrapper for Power HAL handlers.
+class HalWrapper {
+public:
+ virtual ~HalWrapper() = default;
+
+ virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) = 0;
+ virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) = 0;
+ virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+ int64_t durationNanos) = 0;
+ virtual HalResult<int64_t> getHintSessionPreferredRate() = 0;
+};
+
+// Empty Power HAL wrapper that ignores all api calls.
+class EmptyHalWrapper : public HalWrapper {
+public:
+ EmptyHalWrapper() = default;
+ ~EmptyHalWrapper() = default;
+
+ virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+ virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
+ virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+ int64_t durationNanos) override;
+ virtual HalResult<int64_t> getHintSessionPreferredRate() override;
+};
+
+// Wrapper for the HIDL Power HAL v1.0.
+class HidlHalWrapperV1_0 : public HalWrapper {
+public:
+ explicit HidlHalWrapperV1_0(sp<hardware::power::V1_0::IPower> Hal)
+ : mHandleV1_0(std::move(Hal)) {}
+ virtual ~HidlHalWrapperV1_0() = default;
+
+ virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+ virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
+ virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+ int64_t durationNanos) override;
+ virtual HalResult<int64_t> getHintSessionPreferredRate() override;
+
+protected:
+ virtual HalResult<void> sendPowerHint(hardware::power::V1_0::PowerHint hintId, uint32_t data);
+
+private:
+ sp<hardware::power::V1_0::IPower> mHandleV1_0;
+ HalResult<void> setInteractive(bool enabled);
+ HalResult<void> setFeature(hardware::power::V1_0::Feature feature, bool enabled);
+};
+
+// Wrapper for the HIDL Power HAL v1.1.
+class HidlHalWrapperV1_1 : public HidlHalWrapperV1_0 {
+public:
+ HidlHalWrapperV1_1(sp<hardware::power::V1_0::IPower> handleV1_0,
+ sp<hardware::power::V1_1::IPower> handleV1_1)
+ : HidlHalWrapperV1_0(std::move(handleV1_0)), mHandleV1_1(std::move(handleV1_1)) {}
+ virtual ~HidlHalWrapperV1_1() = default;
+
+protected:
+ virtual HalResult<void> sendPowerHint(hardware::power::V1_0::PowerHint hintId,
+ uint32_t data) override;
+
+private:
+ sp<hardware::power::V1_1::IPower> mHandleV1_1;
+};
+
+// Wrapper for the AIDL Power HAL.
+class AidlHalWrapper : public HalWrapper {
+public:
+ explicit AidlHalWrapper(sp<hardware::power::IPower> handle) : mHandle(std::move(handle)) {}
+ virtual ~AidlHalWrapper() = default;
+
+ virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+ virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
+ virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+ int64_t durationNanos) override;
+ virtual HalResult<int64_t> getHintSessionPreferredRate() override;
+
+private:
+ // Control access to the boost and mode supported arrays.
+ std::mutex mBoostMutex;
+ std::mutex mModeMutex;
+ sp<hardware::power::IPower> mHandle;
+ // Android framework only sends boost upto DISPLAY_UPDATE_IMMINENT.
+ // Need to increase the array size if more boost supported.
+ std::array<std::atomic<HalSupport>,
+ static_cast<int32_t>(hardware::power::Boost::DISPLAY_UPDATE_IMMINENT) + 1>
+ mBoostSupportedArray GUARDED_BY(mBoostMutex) = {HalSupport::UNKNOWN};
+ // Android framework only sends mode upto DISPLAY_INACTIVE.
+ // Need to increase the array if more mode supported.
+ std::array<std::atomic<HalSupport>,
+ static_cast<int32_t>(hardware::power::Mode::DISPLAY_INACTIVE) + 1>
+ mModeSupportedArray GUARDED_BY(mModeMutex) = {HalSupport::UNKNOWN};
+};
+
+}; // namespace power
+
+}; // namespace android
+
+#endif // ANDROID_POWERHALWRAPPER_H
diff --git a/include/private/surface_control_private.h b/include/private/surface_control_private.h
new file mode 100644
index 0000000..37a476e
--- /dev/null
+++ b/include/private/surface_control_private.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#ifndef ANDROID_PRIVATE_NATIVE_SURFACE_CONTROL_H
+#define ANDROID_PRIVATE_NATIVE_SURFACE_CONTROL_H
+
+#include <stdint.h>
+
+__BEGIN_DECLS
+
+struct ASurfaceControl;
+struct ASurfaceControlStats;
+
+typedef struct ASurfaceControlStats ASurfaceControlStats;
+
+/**
+ * Callback to be notified when surface stats for a specific surface control are available.
+ */
+typedef void (*ASurfaceControl_SurfaceStatsListener)(void* context,
+ ASurfaceControl* control, ASurfaceControlStats* stats);
+
+/**
+ * Registers a callback to be invoked when surface stats from a specific surface are available.
+ *
+ * \param context Optional context provided by the client that is passed into
+ * the callback.
+ *
+ * \param control The surface to retrieve callbacks for.
+ *
+ * \param func The callback to be invoked when surface stats are available.
+ */
+void ASurfaceControl_registerSurfaceStatsListener(ASurfaceControl* control, void* context,
+ ASurfaceControl_SurfaceStatsListener func);
+
+/**
+ * Unregisters a callback to be invoked when surface stats from a specific surface are available.
+ *
+ * \param context The context passed into ASurfaceControl_registerSurfaceStatsListener
+ *
+ * \param func The callback passed into ASurfaceControl_registerSurfaceStatsListener
+ */
+void ASurfaceControl_unregisterSurfaceStatsListener(void* context,
+ ASurfaceControl_SurfaceStatsListener func);
+
+/**
+ * Returns the timestamp of when the buffer was acquired for a specific frame with frame number
+ * obtained from ASurfaceControlStats_getFrameNumber.
+ */
+int64_t ASurfaceControlStats_getAcquireTime(ASurfaceControlStats* stats);
+
+/**
+ * Returns the frame number of the surface stats object passed into the callback.
+ */
+uint64_t ASurfaceControlStats_getFrameNumber(ASurfaceControlStats* stats);
+
+__END_DECLS
+
+#endif //ANDROID_PRIVATE_NATIVE_SURFACE_CONTROL_H
diff --git a/include/ui/DisplayInfo.h b/include/ui/DisplayInfo.h
deleted file mode 120000
index 9a195ea..0000000
--- a/include/ui/DisplayInfo.h
+++ /dev/null
@@ -1 +0,0 @@
-../../libs/ui/include/ui/DisplayInfo.h
\ No newline at end of file
diff --git a/include/ui/Rotation.h b/include/ui/Rotation.h
new file mode 120000
index 0000000..095d2ce
--- /dev/null
+++ b/include/ui/Rotation.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/Rotation.h
\ No newline at end of file
diff --git a/include/ui/StaticDisplayInfo.h b/include/ui/StaticDisplayInfo.h
new file mode 120000
index 0000000..c58aae3
--- /dev/null
+++ b/include/ui/StaticDisplayInfo.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/StaticDisplayInfo.h
\ No newline at end of file
diff --git a/include/ui/Transform.h b/include/ui/Transform.h
new file mode 120000
index 0000000..323f1fd
--- /dev/null
+++ b/include/ui/Transform.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/Transform.h
\ No newline at end of file
diff --git a/libs/attestation/Android.bp b/libs/attestation/Android.bp
new file mode 100644
index 0000000..ea3c341
--- /dev/null
+++ b/libs/attestation/Android.bp
@@ -0,0 +1,40 @@
+// Copyright (C) 2020 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"],
+}
+
+cc_library_static {
+ name: "libattestation",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ srcs: [
+ "HmacKeyManager.cpp"
+ ],
+
+ clang: true,
+
+ shared_libs: [
+ "liblog",
+ "libcrypto",
+ ],
+}
diff --git a/libs/attestation/HmacKeyManager.cpp b/libs/attestation/HmacKeyManager.cpp
new file mode 100644
index 0000000..b15f143
--- /dev/null
+++ b/libs/attestation/HmacKeyManager.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 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 <attestation/HmacKeyManager.h>
+#include <log/log.h>
+#include <openssl/hmac.h>
+#include <openssl/rand.h>
+
+namespace android {
+
+static std::array<uint8_t, 128> getRandomKey() {
+ std::array<uint8_t, 128> key;
+ if (RAND_bytes(key.data(), key.size()) != 1) {
+ LOG_ALWAYS_FATAL("Can't generate HMAC key");
+ }
+ return key;
+}
+
+HmacKeyManager::HmacKeyManager() : mHmacKey(getRandomKey()) {}
+
+std::array<uint8_t, 32> HmacKeyManager::sign(const uint8_t* data, size_t size) const {
+ // SHA256 always generates 32-bytes result
+ std::array<uint8_t, 32> hash;
+ unsigned int hashLen = 0;
+ uint8_t* result =
+ HMAC(EVP_sha256(), mHmacKey.data(), mHmacKey.size(), data, size, hash.data(), &hashLen);
+ if (result == nullptr) {
+ ALOGE("Could not sign the data using HMAC");
+ return INVALID_HMAC;
+ }
+
+ if (hashLen != hash.size()) {
+ ALOGE("HMAC-SHA256 has unexpected length");
+ return INVALID_HMAC;
+ }
+
+ return hash;
+}
+} // namespace android
\ No newline at end of file
diff --git a/libs/attestation/OWNERS b/libs/attestation/OWNERS
new file mode 100644
index 0000000..4dbb0ea
--- /dev/null
+++ b/libs/attestation/OWNERS
@@ -0,0 +1,2 @@
+chaviw@google.com
+svv@google.com
\ No newline at end of file
diff --git a/libs/attestation/TEST_MAPPING b/libs/attestation/TEST_MAPPING
new file mode 100644
index 0000000..43be638
--- /dev/null
+++ b/libs/attestation/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libattestation_tests"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/libs/attestation/tests/Android.bp b/libs/attestation/tests/Android.bp
new file mode 100644
index 0000000..8dac0ce
--- /dev/null
+++ b/libs/attestation/tests/Android.bp
@@ -0,0 +1,37 @@
+// Copyright (C) 2020 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"],
+}
+
+cc_test {
+ name: "libattestation_tests",
+ test_suites: ["device-tests"],
+ srcs: [
+ "HmacKeyManager_test.cpp",
+ ],
+ static_libs: [
+ "libattestation",
+ ],
+ shared_libs: [
+ "liblog",
+ "libcrypto",
+ ],
+}
diff --git a/libs/attestation/tests/HmacKeyManager_test.cpp b/libs/attestation/tests/HmacKeyManager_test.cpp
new file mode 100644
index 0000000..7f7a408
--- /dev/null
+++ b/libs/attestation/tests/HmacKeyManager_test.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 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 <attestation/HmacKeyManager.h>
+#include <gtest/gtest.h>
+
+namespace android {
+
+class HmacKeyManagerTest : public testing::Test {
+protected:
+ HmacKeyManager mHmacKeyManager;
+};
+
+/**
+ * Ensure that separate calls to sign the same data are generating the same key.
+ * We avoid asserting against INVALID_HMAC. Since the key is random, there is a non-zero chance
+ * that a specific key and data combination would produce INVALID_HMAC, which would cause flaky
+ * tests.
+ */
+TEST_F(HmacKeyManagerTest, GeneratedHmac_IsConsistent) {
+ std::array<uint8_t, 10> data = {4, 3, 5, 1, 8, 5, 2, 7, 1, 8};
+
+ std::array<uint8_t, 32> hmac1 = mHmacKeyManager.sign(data.data(), sizeof(data));
+ std::array<uint8_t, 32> hmac2 = mHmacKeyManager.sign(data.data(), sizeof(data));
+ ASSERT_EQ(hmac1, hmac2);
+}
+
+/**
+ * Ensure that changes in the hmac verification data produce a different hmac.
+ */
+TEST_F(HmacKeyManagerTest, GeneratedHmac_ChangesWhenFieldsChange) {
+ std::array<uint8_t, 10> data = {4, 3, 5, 1, 8, 5, 2, 7, 1, 8};
+ std::array<uint8_t, 32> initialHmac = mHmacKeyManager.sign(data.data(), sizeof(data));
+
+ data[2] = 2;
+ ASSERT_NE(initialHmac, mHmacKeyManager.sign(data.data(), sizeof(data)));
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp
index 5e4c98f..e45a656 100644
--- a/libs/binder/ActivityManager.cpp
+++ b/libs/binder/ActivityManager.cpp
@@ -17,6 +17,7 @@
#include <mutex>
#include <unistd.h>
+#include <android/permission_manager.h>
#include <binder/ActivityManager.h>
#include <binder/Binder.h>
#include <binder/IServiceManager.h>
@@ -61,23 +62,27 @@
return service != nullptr ? service->openContentUri(stringUri) : -1;
}
-void ActivityManager::registerUidObserver(const sp<IUidObserver>& observer,
+status_t ActivityManager::registerUidObserver(const sp<IUidObserver>& observer,
const int32_t event,
const int32_t cutpoint,
const String16& callingPackage)
{
sp<IActivityManager> service = getService();
if (service != nullptr) {
- service->registerUidObserver(observer, event, cutpoint, callingPackage);
+ return service->registerUidObserver(observer, event, cutpoint, callingPackage);
}
+ // ActivityManagerService appears dead. Return usual error code for dead service.
+ return DEAD_OBJECT;
}
-void ActivityManager::unregisterUidObserver(const sp<IUidObserver>& observer)
+status_t ActivityManager::unregisterUidObserver(const sp<IUidObserver>& observer)
{
sp<IActivityManager> service = getService();
if (service != nullptr) {
- service->unregisterUidObserver(observer);
+ return service->unregisterUidObserver(observer);
}
+ // ActivityManagerService appears dead. Return usual error code for dead service.
+ return DEAD_OBJECT;
}
bool ActivityManager::isUidActive(const uid_t uid, const String16& callingPackage)
@@ -98,6 +103,18 @@
return PROCESS_STATE_UNKNOWN;
}
+status_t ActivityManager::checkPermission(const String16& permission,
+ const pid_t pid,
+ const uid_t uid,
+ int32_t* outResult) {
+ sp<IActivityManager> service = getService();
+ if (service != nullptr) {
+ return service->checkPermission(permission, pid, uid, outResult);
+ }
+ // ActivityManagerService appears dead. Return usual error code for dead service.
+ return DEAD_OBJECT;
+}
+
status_t ActivityManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient) {
sp<IActivityManager> service = getService();
if (service != nullptr) {
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 3f110c9..a97cf87 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -89,9 +89,6 @@
// or dessert updates. Instead, apex users should use libbinder_ndk.
apex_available: [
"//apex_available:platform",
- // TODO(b/166468760) remove these three
- "com.android.media.swcodec",
- "test_com.android.media.swcodec",
],
srcs: [
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index b81995c..08169f5 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -17,9 +17,11 @@
#include <unistd.h>
#include <fcntl.h>
+#include <android/permission_manager.h>
#include <binder/ActivityManager.h>
#include <binder/IActivityManager.h>
#include <binder/Parcel.h>
+#include <utils/Errors.h>
namespace android {
@@ -57,7 +59,7 @@
return fd;
}
- virtual void registerUidObserver(const sp<IUidObserver>& observer,
+ virtual status_t registerUidObserver(const sp<IUidObserver>& observer,
const int32_t event,
const int32_t cutpoint,
const String16& callingPackage)
@@ -68,15 +70,23 @@
data.writeInt32(event);
data.writeInt32(cutpoint);
data.writeString16(callingPackage);
- remote()->transact(REGISTER_UID_OBSERVER_TRANSACTION, data, &reply);
+ status_t err = remote()->transact(REGISTER_UID_OBSERVER_TRANSACTION, data, &reply);
+ if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
+ return err;
+ }
+ return OK;
}
- virtual void unregisterUidObserver(const sp<IUidObserver>& observer)
+ virtual status_t unregisterUidObserver(const sp<IUidObserver>& observer)
{
Parcel data, reply;
data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
data.writeStrongBinder(IInterface::asBinder(observer));
- remote()->transact(UNREGISTER_UID_OBSERVER_TRANSACTION, data, &reply);
+ status_t err = remote()->transact(UNREGISTER_UID_OBSERVER_TRANSACTION, data, &reply);
+ if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
+ return err;
+ }
+ return OK;
}
virtual bool isUidActive(const uid_t uid, const String16& callingPackage)
@@ -104,6 +114,23 @@
}
return reply.readInt32();
}
+
+ virtual status_t checkPermission(const String16& permission,
+ const pid_t pid,
+ const uid_t uid,
+ int32_t* outResult) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+ data.writeString16(permission);
+ data.writeInt32(pid);
+ data.writeInt32(uid);
+ status_t err = remote()->transact(CHECK_PERMISSION_TRANSACTION, data, &reply);
+ if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
+ return err;
+ }
+ *outResult = reply.readInt32();
+ return NO_ERROR;
+ }
};
// ------------------------------------------------------------------------------------
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 98ca829..a735309 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -589,7 +589,7 @@
}
}
-#if defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
+#if defined(__ANDROID_VNDK__)
constexpr int32_t kHeader = B_PACK_CHARS('V', 'N', 'D', 'R');
#else
constexpr int32_t kHeader = B_PACK_CHARS('S', 'Y', 'S', 'T');
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index a2efb1c..1d3beb4 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -423,12 +423,6 @@
, mThreadPoolSeq(1)
, mCallRestriction(CallRestriction::NONE)
{
-
-// TODO(b/166468760): enforce in build system
-#if defined(__ANDROID_APEX__)
- LOG_ALWAYS_FATAL("Cannot use libbinder in APEX (only system.img libbinder) since it is not stable.");
-#endif
-
if (mDriverFD >= 0) {
// mmap the binder, providing a chunk of virtual address space to receive transactions.
mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
diff --git a/libs/binder/Stability.cpp b/libs/binder/Stability.cpp
index 709cf67..f12ef4e 100644
--- a/libs/binder/Stability.cpp
+++ b/libs/binder/Stability.cpp
@@ -100,18 +100,12 @@
}
Stability::Level Stability::getLocalLevel() {
+#ifdef __ANDROID_APEX__
+#error APEX can't use libbinder (must use libbinder_ndk)
+#endif
+
#ifdef __ANDROID_VNDK__
- #ifdef __ANDROID_APEX__
- // TODO(b/142684679) avoid use_vendor on system APEXes
- #if !defined(__ANDROID_APEX_COM_ANDROID_MEDIA_SWCODEC__) \
- && !defined(__ANDROID_APEX_TEST_COM_ANDROID_MEDIA_SWCODEC__)
- #error VNDK + APEX only defined for com.android.media.swcodec
- #endif
- // TODO(b/142684679) avoid use_vendor on system APEXes
- return Level::SYSTEM;
- #else
- return Level::VENDOR;
- #endif
+ return Level::VENDOR;
#else
// TODO(b/139325195): split up stability levels for system/APEX.
return Level::SYSTEM;
diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
index dc8d74c..c20d9f6 100644
--- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
+++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
@@ -101,4 +101,26 @@
* This does nothing if this observer was not already registered.
*/
void unregisterPackageChangeObserver(in IPackageChangeObserver observer);
+
+ /**
+ * Returns true if the package has the SHA 256 version of the signing certificate.
+ * @see PackageManager#hasSigningCertificate(String, byte[], int), where type
+ * has been set to {@link PackageManager#CERT_INPUT_SHA256}.
+ */
+ boolean hasSha256SigningCertificate(in @utf8InCpp String packageName, in byte[] certificate);
+
+ /**
+ * Returns the debug flag for the given package.
+ * Unknown packages will cause the call to fail.
+ */
+ boolean isPackageDebuggable(in String packageName);
+
+ /**
+ * Check whether the given feature name and version is one of the available
+ * features as returned by {@link PackageManager#getSystemAvailableFeatures()}. Since
+ * features are defined to always be backwards compatible, this returns true
+ * if the available feature version is greater than or equal to the
+ * requested version.
+ */
+ boolean hasSystemFeature(in String featureName, in int version);
}
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index f35e2db..ff90b30 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -239,23 +239,11 @@
"android.hardware.ICameraRecordingProxyListener",
"android.hardware.ICrypto",
"android.hardware.IOMXObserver",
- "android.hardware.ISoundTrigger",
- "android.hardware.ISoundTriggerClient",
- "android.hardware.ISoundTriggerHwService",
"android.hardware.IStreamListener",
"android.hardware.IStreamSource",
- "android.input.IInputFlinger",
- "android.input.ISetInputWindowsListener",
- "android.media.IAudioFlinger",
- "android.media.IAudioFlingerClient",
- "android.media.IAudioPolicyService",
- "android.media.IAudioPolicyServiceClient",
"android.media.IAudioService",
- "android.media.IAudioTrack",
"android.media.IDataSource",
"android.media.IDrmClient",
- "android.media.IEffect",
- "android.media.IEffectClient",
"android.media.IMediaCodecList",
"android.media.IMediaDrmService",
"android.media.IMediaExtractor",
@@ -279,7 +267,6 @@
"android.os.IComplexTypeInterface",
"android.os.IPermissionController",
"android.os.IPingResponder",
- "android.os.IPowerManager",
"android.os.IProcessInfoService",
"android.os.ISchedulingPolicyService",
"android.os.IStringConstants",
diff --git a/libs/binder/include_activitymanager/binder/ActivityManager.h b/libs/binder/include_activitymanager/binder/ActivityManager.h
index b90dc86..b772b80 100644
--- a/libs/binder/include_activitymanager/binder/ActivityManager.h
+++ b/libs/binder/include_activitymanager/binder/ActivityManager.h
@@ -19,12 +19,16 @@
#ifndef __ANDROID_VNDK__
#include <binder/IActivityManager.h>
+#include <android/app/ProcessStateEnum.h>
#include <utils/threads.h>
// ---------------------------------------------------------------------------
namespace android {
+#define DECLARE_PROCESS_STATE(name) \
+ PROCESS_STATE_##name = (int32_t) app::ProcessStateEnum::name
+
class ActivityManager
{
public:
@@ -37,48 +41,53 @@
// Flag for registerUidObserver: report uid has become idle
UID_OBSERVER_IDLE = 1<<2,
// Flag for registerUidObserver: report uid has become active
- UID_OBSERVER_ACTIVE = 1<<3
+ UID_OBSERVER_ACTIVE = 1<<3,
+ // Flag for registerUidObserver: report uid cached state has changed
+ UID_OBSERVER_CACHED = 1<<4,
+ // Flag for registerUidObserver: report uid capability has changed
+ UID_OBSERVER_CAPABILITY = 1<<5,
};
+ // PROCESS_STATE_* must come from frameworks/base/core/java/android/app/ProcessStateEnum.aidl.
+ // This is to make sure that Java side uses the same values as native.
enum {
- PROCESS_STATE_UNKNOWN = -1,
- PROCESS_STATE_PERSISTENT = 0,
- PROCESS_STATE_PERSISTENT_UI = 1,
- PROCESS_STATE_TOP = 2,
- PROCESS_STATE_FOREGROUND_SERVICE_LOCATION = 3,
- PROCESS_STATE_BOUND_TOP = 4,
- PROCESS_STATE_FOREGROUND_SERVICE = 5,
- PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 6,
- PROCESS_STATE_IMPORTANT_FOREGROUND = 7,
- PROCESS_STATE_IMPORTANT_BACKGROUND = 8,
- PROCESS_STATE_TRANSIENT_BACKGROUND = 9,
- PROCESS_STATE_BACKUP = 10,
- PROCESS_STATE_SERVICE = 11,
- PROCESS_STATE_RECEIVER = 12,
- PROCESS_STATE_TOP_SLEEPING = 13,
- PROCESS_STATE_HEAVY_WEIGHT = 14,
- PROCESS_STATE_HOME = 15,
- PROCESS_STATE_LAST_ACTIVITY = 16,
- PROCESS_STATE_CACHED_ACTIVITY = 17,
- PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 18,
- PROCESS_STATE_CACHED_RECENT = 19,
- PROCESS_STATE_CACHED_EMPTY = 20,
- PROCESS_STATE_NONEXISTENT = 21,
+ DECLARE_PROCESS_STATE(UNKNOWN),
+ DECLARE_PROCESS_STATE(PERSISTENT),
+ DECLARE_PROCESS_STATE(PERSISTENT_UI),
+ DECLARE_PROCESS_STATE(TOP),
+ DECLARE_PROCESS_STATE(BOUND_TOP),
+ DECLARE_PROCESS_STATE(FOREGROUND_SERVICE),
+ DECLARE_PROCESS_STATE(BOUND_FOREGROUND_SERVICE),
+ DECLARE_PROCESS_STATE(IMPORTANT_FOREGROUND),
+ DECLARE_PROCESS_STATE(IMPORTANT_BACKGROUND),
+ DECLARE_PROCESS_STATE(TRANSIENT_BACKGROUND),
+ DECLARE_PROCESS_STATE(BACKUP),
+ DECLARE_PROCESS_STATE(SERVICE),
+ DECLARE_PROCESS_STATE(RECEIVER),
+ DECLARE_PROCESS_STATE(TOP_SLEEPING),
+ DECLARE_PROCESS_STATE(HEAVY_WEIGHT),
+ DECLARE_PROCESS_STATE(HOME),
+ DECLARE_PROCESS_STATE(LAST_ACTIVITY),
+ DECLARE_PROCESS_STATE(CACHED_ACTIVITY),
+ DECLARE_PROCESS_STATE(CACHED_ACTIVITY_CLIENT),
+ DECLARE_PROCESS_STATE(CACHED_RECENT),
+ DECLARE_PROCESS_STATE(CACHED_EMPTY),
+ DECLARE_PROCESS_STATE(NONEXISTENT),
};
ActivityManager();
int openContentUri(const String16& stringUri);
- void registerUidObserver(const sp<IUidObserver>& observer,
+ status_t registerUidObserver(const sp<IUidObserver>& observer,
const int32_t event,
const int32_t cutpoint,
const String16& callingPackage);
- void unregisterUidObserver(const sp<IUidObserver>& observer);
+ status_t unregisterUidObserver(const sp<IUidObserver>& observer);
bool isUidActive(const uid_t uid, const String16& callingPackage);
int getUidProcessState(const uid_t uid, const String16& callingPackage);
+ status_t checkPermission(const String16& permission, const pid_t pid, const uid_t uid, int32_t* outResult);
-
- status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
+ status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
status_t unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient);
private:
diff --git a/libs/binder/include_activitymanager/binder/IActivityManager.h b/libs/binder/include_activitymanager/binder/IActivityManager.h
index e3b5e43..4632b2e 100644
--- a/libs/binder/include_activitymanager/binder/IActivityManager.h
+++ b/libs/binder/include_activitymanager/binder/IActivityManager.h
@@ -31,20 +31,25 @@
DECLARE_META_INTERFACE(ActivityManager)
virtual int openContentUri(const String16& stringUri) = 0;
- virtual void registerUidObserver(const sp<IUidObserver>& observer,
+ virtual status_t registerUidObserver(const sp<IUidObserver>& observer,
const int32_t event,
const int32_t cutpoint,
const String16& callingPackage) = 0;
- virtual void unregisterUidObserver(const sp<IUidObserver>& observer) = 0;
+ virtual status_t unregisterUidObserver(const sp<IUidObserver>& observer) = 0;
virtual bool isUidActive(const uid_t uid, const String16& callingPackage) = 0;
virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage) = 0;
+ virtual status_t checkPermission(const String16& permission,
+ const pid_t pid,
+ const uid_t uid,
+ int32_t* outResult) = 0;
enum {
OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
REGISTER_UID_OBSERVER_TRANSACTION,
UNREGISTER_UID_OBSERVER_TRANSACTION,
IS_UID_ACTIVE_TRANSACTION,
- GET_UID_PROCESS_STATE_TRANSACTION
+ GET_UID_PROCESS_STATE_TRANSACTION,
+ CHECK_PERMISSION_TRANSACTION,
};
};
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 883403a..1dcb41b 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -181,7 +181,7 @@
binder_status_t status = getClass()->onTransact(this, code, &in, &out);
return PruneStatusT(status);
- } else if (code == SHELL_COMMAND_TRANSACTION) {
+ } else if (code == SHELL_COMMAND_TRANSACTION && getClass()->handleShellCommand != nullptr) {
int in = data.readFileDescriptor();
int out = data.readFileDescriptor();
int err = data.readFileDescriptor();
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h
index 22cacb4..6824306 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -116,13 +116,13 @@
const char* getInterfaceDescriptorUtf8() const { return mInterfaceDescriptor.c_str(); }
// required to be non-null, implemented for every class
- const AIBinder_Class_onCreate onCreate;
- const AIBinder_Class_onDestroy onDestroy;
- const AIBinder_Class_onTransact onTransact;
+ const AIBinder_Class_onCreate onCreate = nullptr;
+ const AIBinder_Class_onDestroy onDestroy = nullptr;
+ const AIBinder_Class_onTransact onTransact = nullptr;
// optional methods for a class
- AIBinder_onDump onDump;
- AIBinder_handleShellCommand handleShellCommand;
+ AIBinder_onDump onDump = nullptr;
+ AIBinder_handleShellCommand handleShellCommand = nullptr;
private:
// Copy of the raw char string for when we don't have to return UTF-16
diff --git a/libs/binder/ndk/tests/iface.cpp b/libs/binder/ndk/tests/iface.cpp
index 53b5c3c..2afe5d2 100644
--- a/libs/binder/ndk/tests/iface.cpp
+++ b/libs/binder/ndk/tests/iface.cpp
@@ -118,7 +118,7 @@
AIBinder_Weak_delete(mWeakBinder);
}
-binder_status_t IFoo::addService(const char* instance) {
+AIBinder* IFoo::getBinder() {
AIBinder* binder = nullptr;
if (mWeakBinder != nullptr) {
@@ -132,8 +132,18 @@
AIBinder_Weak_delete(mWeakBinder);
}
mWeakBinder = AIBinder_Weak_new(binder);
+
+ // WARNING: it is important that this class does not implement debug or
+ // shell functions because it does not use special C++ wrapper
+ // functions, and so this is how we test those functions.
}
+ return binder;
+}
+
+binder_status_t IFoo::addService(const char* instance) {
+ AIBinder* binder = getBinder();
+
binder_status_t status = AServiceManager_addService(binder, instance);
// Strong references we care about kept by remote process
AIBinder_decStrong(binder);
diff --git a/libs/binder/ndk/tests/include/iface/iface.h b/libs/binder/ndk/tests/include/iface/iface.h
index 244c985..7408d0c 100644
--- a/libs/binder/ndk/tests/include/iface/iface.h
+++ b/libs/binder/ndk/tests/include/iface/iface.h
@@ -31,6 +31,9 @@
static AIBinder_Class* kClass;
+ // binder representing this interface with one reference count
+ AIBinder* getBinder();
+
// Takes ownership of IFoo
binder_status_t addService(const char* instance);
static ::android::sp<IFoo> getService(const char* instance, AIBinder** outBinder = nullptr);
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 1c43948..62db3cf 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -241,6 +241,26 @@
AIBinder_decStrong(binder);
}
+TEST(NdkBinder, UnimplementedDump) {
+ sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName);
+ ASSERT_NE(foo, nullptr);
+ AIBinder* binder = foo->getBinder();
+ EXPECT_EQ(OK, AIBinder_dump(binder, STDOUT_FILENO, nullptr, 0));
+ AIBinder_decStrong(binder);
+}
+
+TEST(NdkBinder, UnimplementedShell) {
+ // libbinder_ndk doesn't support calling shell, so we are calling from the
+ // libbinder across processes to the NDK service which doesn't implement
+ // shell
+ static const sp<android::IServiceManager> sm(android::defaultServiceManager());
+ sp<IBinder> testService = sm->getService(String16(IFoo::kSomeInstanceName));
+
+ Vector<String16> argsVec;
+ EXPECT_EQ(OK, IBinder::shellCommand(testService, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO,
+ argsVec, nullptr, nullptr));
+}
+
TEST(NdkBinder, DoubleNumber) {
sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName);
ASSERT_NE(foo, nullptr);
diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/parcel_fuzzer/Android.bp
similarity index 100%
rename from libs/binder/tests/parcel_fuzzer/Android.bp
rename to libs/binder/parcel_fuzzer/Android.bp
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/parcel_fuzzer/binder.cpp
similarity index 96%
rename from libs/binder/tests/parcel_fuzzer/binder.cpp
rename to libs/binder/parcel_fuzzer/binder.cpp
index 394d222..624def1 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/parcel_fuzzer/binder.cpp
@@ -145,6 +145,13 @@
FUZZ_LOG() << "read c-str: " << (str ? str : "<empty string>");
},
PARCEL_READ_OPT_STATUS(android::String8, readString8),
+ [] (const ::android::Parcel& p, uint8_t /*data*/) {
+ FUZZ_LOG() << "about to readString8Inplace";
+ size_t outLen = 0;
+ const char* str = p.readString8Inplace(&outLen);
+ std::string bytes = hexString(str, sizeof(char) * (outLen + 1));
+ FUZZ_LOG() << "readString8Inplace: " << bytes << " size: " << outLen;
+ },
PARCEL_READ_OPT_STATUS(android::String16, readString16),
PARCEL_READ_WITH_STATUS(std::unique_ptr<android::String16>, readString16),
PARCEL_READ_WITH_STATUS(std::optional<android::String16>, readString16),
@@ -152,8 +159,8 @@
FUZZ_LOG() << "about to readString16Inplace";
size_t outLen = 0;
const char16_t* str = p.readString16Inplace(&outLen);
- FUZZ_LOG() << "readString16Inplace: " << hexString(str, sizeof(char16_t) * outLen)
- << " size: " << outLen;
+ std::string bytes = hexString(str, sizeof(char16_t) * (outLen + 1));
+ FUZZ_LOG() << "readString16Inplace: " << bytes << " size: " << outLen;
},
PARCEL_READ_WITH_STATUS(android::sp<android::IBinder>, readStrongBinder),
PARCEL_READ_WITH_STATUS(android::sp<android::IBinder>, readNullableStrongBinder),
diff --git a/libs/binder/tests/parcel_fuzzer/binder.h b/libs/binder/parcel_fuzzer/binder.h
similarity index 100%
rename from libs/binder/tests/parcel_fuzzer/binder.h
rename to libs/binder/parcel_fuzzer/binder.h
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/parcel_fuzzer/binder_ndk.cpp
similarity index 100%
rename from libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
rename to libs/binder/parcel_fuzzer/binder_ndk.cpp
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.h b/libs/binder/parcel_fuzzer/binder_ndk.h
similarity index 97%
rename from libs/binder/tests/parcel_fuzzer/binder_ndk.h
rename to libs/binder/parcel_fuzzer/binder_ndk.h
index cf24ab9..e69d9c1 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.h
+++ b/libs/binder/parcel_fuzzer/binder_ndk.h
@@ -23,7 +23,7 @@
// libbinder_ndk doesn't export this header which breaks down its API for NDK
// and APEX users, but we need access to it to fuzz.
-#include "../../ndk/parcel_internal.h"
+#include "../ndk/parcel_internal.h"
class NdkParcelAdapter {
public:
diff --git a/libs/binder/tests/parcel_fuzzer/hwbinder.cpp b/libs/binder/parcel_fuzzer/hwbinder.cpp
similarity index 100%
rename from libs/binder/tests/parcel_fuzzer/hwbinder.cpp
rename to libs/binder/parcel_fuzzer/hwbinder.cpp
diff --git a/libs/binder/tests/parcel_fuzzer/hwbinder.h b/libs/binder/parcel_fuzzer/hwbinder.h
similarity index 100%
rename from libs/binder/tests/parcel_fuzzer/hwbinder.h
rename to libs/binder/parcel_fuzzer/hwbinder.h
diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h b/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
similarity index 100%
rename from libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
rename to libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h b/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
similarity index 100%
rename from libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
rename to libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
diff --git a/libs/binder/tests/parcel_fuzzer/main.cpp b/libs/binder/parcel_fuzzer/main.cpp
similarity index 100%
rename from libs/binder/tests/parcel_fuzzer/main.cpp
rename to libs/binder/parcel_fuzzer/main.cpp
diff --git a/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h b/libs/binder/parcel_fuzzer/parcel_fuzzer.h
similarity index 100%
rename from libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h
rename to libs/binder/parcel_fuzzer/parcel_fuzzer.h
diff --git a/libs/binder/tests/parcel_fuzzer/random_fd.cpp b/libs/binder/parcel_fuzzer/random_fd.cpp
similarity index 100%
rename from libs/binder/tests/parcel_fuzzer/random_fd.cpp
rename to libs/binder/parcel_fuzzer/random_fd.cpp
diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/parcel_fuzzer/random_parcel.cpp
similarity index 100%
rename from libs/binder/tests/parcel_fuzzer/random_parcel.cpp
rename to libs/binder/parcel_fuzzer/random_parcel.cpp
diff --git a/libs/binder/tests/parcel_fuzzer/util.cpp b/libs/binder/parcel_fuzzer/util.cpp
similarity index 100%
rename from libs/binder/tests/parcel_fuzzer/util.cpp
rename to libs/binder/parcel_fuzzer/util.cpp
diff --git a/libs/binder/tests/parcel_fuzzer/util.h b/libs/binder/parcel_fuzzer/util.h
similarity index 100%
rename from libs/binder/tests/parcel_fuzzer/util.h
rename to libs/binder/parcel_fuzzer/util.h
diff --git a/libs/binder/tests/binderParcelTest.cpp b/libs/binder/tests/binderParcelTest.cpp
index 1764228..841d47b 100644
--- a/libs/binder/tests/binderParcelTest.cpp
+++ b/libs/binder/tests/binderParcelTest.cpp
@@ -25,6 +25,40 @@
using android::String8;
using android::status_t;
+TEST(Parcel, NonNullTerminatedString8) {
+ String8 kTestString = String8("test-is-good");
+
+ // write non-null terminated string
+ Parcel p;
+ p.writeString8(kTestString);
+ p.setDataPosition(0);
+ // BAD! assumption of wire format for test
+ // write over length of string
+ p.writeInt32(kTestString.size() - 2);
+
+ p.setDataPosition(0);
+ String8 output;
+ EXPECT_NE(OK, p.readString8(&output));
+ EXPECT_EQ(output.size(), 0);
+}
+
+TEST(Parcel, NonNullTerminatedString16) {
+ String16 kTestString = String16("test-is-good");
+
+ // write non-null terminated string
+ Parcel p;
+ p.writeString16(kTestString);
+ p.setDataPosition(0);
+ // BAD! assumption of wire format for test
+ // write over length of string
+ p.writeInt32(kTestString.size() - 2);
+
+ p.setDataPosition(0);
+ String16 output;
+ EXPECT_NE(OK, p.readString16(&output));
+ EXPECT_EQ(output.size(), 0);
+}
+
// Tests a second operation results in a parcel at the same location as it
// started.
void parcelOpSameLength(const std::function<void(Parcel*)>& a, const std::function<void(Parcel*)>& b) {
diff --git a/libs/binder/tests/unit_fuzzers/Android.bp b/libs/binder/tests/fuzzers/Android.bp
similarity index 100%
rename from libs/binder/tests/unit_fuzzers/Android.bp
rename to libs/binder/tests/fuzzers/Android.bp
diff --git a/libs/binder/tests/unit_fuzzers/BinderFuzz.cpp b/libs/binder/tests/fuzzers/BinderFuzz.cpp
similarity index 100%
rename from libs/binder/tests/unit_fuzzers/BinderFuzz.cpp
rename to libs/binder/tests/fuzzers/BinderFuzz.cpp
diff --git a/libs/binder/tests/unit_fuzzers/BinderFuzzFunctions.h b/libs/binder/tests/fuzzers/BinderFuzzFunctions.h
similarity index 100%
rename from libs/binder/tests/unit_fuzzers/BinderFuzzFunctions.h
rename to libs/binder/tests/fuzzers/BinderFuzzFunctions.h
diff --git a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp b/libs/binder/tests/fuzzers/BpBinderFuzz.cpp
similarity index 100%
rename from libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
rename to libs/binder/tests/fuzzers/BpBinderFuzz.cpp
diff --git a/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h b/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h
similarity index 100%
rename from libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h
rename to libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h
diff --git a/libs/binder/tests/unit_fuzzers/BufferedTextOutputFuzz.cpp b/libs/binder/tests/fuzzers/BufferedTextOutputFuzz.cpp
similarity index 100%
rename from libs/binder/tests/unit_fuzzers/BufferedTextOutputFuzz.cpp
rename to libs/binder/tests/fuzzers/BufferedTextOutputFuzz.cpp
diff --git a/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h b/libs/binder/tests/fuzzers/IBinderFuzzFunctions.h
similarity index 100%
rename from libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h
rename to libs/binder/tests/fuzzers/IBinderFuzzFunctions.h
diff --git a/libs/binder/tests/unit_fuzzers/MemoryDealerFuzz.cpp b/libs/binder/tests/fuzzers/MemoryDealerFuzz.cpp
similarity index 100%
rename from libs/binder/tests/unit_fuzzers/MemoryDealerFuzz.cpp
rename to libs/binder/tests/fuzzers/MemoryDealerFuzz.cpp
diff --git a/libs/binder/tests/unit_fuzzers/PersistableBundleFuzz.cpp b/libs/binder/tests/fuzzers/PersistableBundleFuzz.cpp
similarity index 100%
rename from libs/binder/tests/unit_fuzzers/PersistableBundleFuzz.cpp
rename to libs/binder/tests/fuzzers/PersistableBundleFuzz.cpp
diff --git a/libs/binder/tests/unit_fuzzers/PersistableBundleFuzzFunctions.h b/libs/binder/tests/fuzzers/PersistableBundleFuzzFunctions.h
similarity index 100%
rename from libs/binder/tests/unit_fuzzers/PersistableBundleFuzzFunctions.h
rename to libs/binder/tests/fuzzers/PersistableBundleFuzzFunctions.h
diff --git a/libs/binder/tests/unit_fuzzers/StabilityFuzz.cpp b/libs/binder/tests/fuzzers/StabilityFuzz.cpp
similarity index 100%
rename from libs/binder/tests/unit_fuzzers/StabilityFuzz.cpp
rename to libs/binder/tests/fuzzers/StabilityFuzz.cpp
diff --git a/libs/binder/tests/unit_fuzzers/StabilityFuzzFunctions.h b/libs/binder/tests/fuzzers/StabilityFuzzFunctions.h
similarity index 100%
rename from libs/binder/tests/unit_fuzzers/StabilityFuzzFunctions.h
rename to libs/binder/tests/fuzzers/StabilityFuzzFunctions.h
diff --git a/libs/binder/tests/unit_fuzzers/StatusFuzz.cpp b/libs/binder/tests/fuzzers/StatusFuzz.cpp
similarity index 100%
rename from libs/binder/tests/unit_fuzzers/StatusFuzz.cpp
rename to libs/binder/tests/fuzzers/StatusFuzz.cpp
diff --git a/libs/binder/tests/unit_fuzzers/StatusFuzzFunctions.h b/libs/binder/tests/fuzzers/StatusFuzzFunctions.h
similarity index 100%
rename from libs/binder/tests/unit_fuzzers/StatusFuzzFunctions.h
rename to libs/binder/tests/fuzzers/StatusFuzzFunctions.h
diff --git a/libs/binder/tests/unit_fuzzers/TextOutputFuzz.cpp b/libs/binder/tests/fuzzers/TextOutputFuzz.cpp
similarity index 100%
rename from libs/binder/tests/unit_fuzzers/TextOutputFuzz.cpp
rename to libs/binder/tests/fuzzers/TextOutputFuzz.cpp
diff --git a/libs/binder/tests/unit_fuzzers/commonFuzzHelpers.h b/libs/binder/tests/fuzzers/commonFuzzHelpers.h
similarity index 100%
rename from libs/binder/tests/unit_fuzzers/commonFuzzHelpers.h
rename to libs/binder/tests/fuzzers/commonFuzzHelpers.h
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 8b3c3ad..8a82c2a 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -33,6 +33,7 @@
"/system/bin/mediaextractor", // media.extractor
"/system/bin/mediametrics", // media.metrics
"/system/bin/mediaserver",
+ "/system/bin/mediatranscoding", // media.transcoding
"/system/bin/netd",
"/system/bin/sdcard",
"/apex/com.android.os.statsd/bin/statsd",
@@ -86,7 +87,7 @@
static void read_extra_hals_to_dump_from_property() {
// extra hals to dump are already filled
- if (extra_hal_interfaces_to_dump.size() > 0) {
+ if (!extra_hal_interfaces_to_dump.empty()) {
return;
}
std::string value = android::base::GetProperty("ro.dump.hals.extra", "");
diff --git a/libs/ftl/.clang-format b/libs/ftl/.clang-format
new file mode 120000
index 0000000..86b1593
--- /dev/null
+++ b/libs/ftl/.clang-format
@@ -0,0 +1 @@
+../../../../build/soong/scripts/system-clang-format-2
\ No newline at end of file
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
new file mode 100644
index 0000000..97626be
--- /dev/null
+++ b/libs/ftl/Android.bp
@@ -0,0 +1,28 @@
+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"],
+}
+
+cc_test {
+ name: "ftl_test",
+ test_suites: ["device-tests"],
+ sanitize: {
+ address: true,
+ },
+ srcs: [
+ "future_test.cpp",
+ "small_map_test.cpp",
+ "small_vector_test.cpp",
+ "static_vector_test.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ "-Wpedantic",
+ ],
+}
diff --git a/libs/ftl/README.md b/libs/ftl/README.md
new file mode 100644
index 0000000..bdd750f
--- /dev/null
+++ b/libs/ftl/README.md
@@ -0,0 +1,41 @@
+# FTL
+
+FTL is a template library shared by SurfaceFlinger and InputFlinger, inspired by
+and supplementing the C++ Standard Library. The intent is to fill gaps for areas
+not (yet) covered—like cache-efficient data structures and lock-free concurrency
+primitives—and implement proposals that are missing or experimental in Android's
+libc++ branch. The design takes some liberties with standard compliance, notably
+assuming that exceptions are disabled.
+
+## Tests
+
+ atest ftl_test
+
+## Style
+
+- Based on [Google C++ Style](https://google.github.io/styleguide/cppguide.html).
+- Informed by [C++ Core Guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines).
+
+Naming conventions are as follows:
+
+- `PascalCase`
+ - Types and aliases, except standard interfaces.
+ - Template parameters, including non-type ones.
+- `snake_case`
+ - Variables, and data members with trailing underscore.
+ - Functions, free and member alike.
+ - Type traits, with standard `_t` and `_v` suffixes.
+- `kCamelCase`
+ - Enumerators and `constexpr` constants with static storage duration.
+- `MACRO_CASE`
+ - Macros, with `FTL_` prefix unless `#undef`ed.
+
+Template parameter packs are named with the following convention:
+
+ typename T, typename... Ts
+ typename Arg, typename... Args
+
+ std::size_t I, std::size_t... Is
+ std::size_t Size, std::size_t... Sizes
+
+The `details` namespace contains implementation details.
diff --git a/libs/ftl/future_test.cpp b/libs/ftl/future_test.cpp
new file mode 100644
index 0000000..9b3e936
--- /dev/null
+++ b/libs/ftl/future_test.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2020 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 <ftl/future.h>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <future>
+#include <string>
+#include <thread>
+#include <vector>
+
+namespace android::test {
+
+// Keep in sync with example usage in header file.
+TEST(Future, Example) {
+ {
+ auto future = ftl::defer([](int x) { return x + 1; }, 99);
+ EXPECT_EQ(future.get(), 100);
+ }
+ {
+ auto future = ftl::yield(42);
+ EXPECT_EQ(future.get(), 42);
+ }
+ {
+ auto ptr = std::make_unique<char>('!');
+ auto future = ftl::yield(std::move(ptr));
+ EXPECT_EQ(*future.get(), '!');
+ }
+ {
+ auto future = ftl::yield(123);
+ std::future<char> futures[] = {ftl::yield('a'), ftl::yield('b')};
+
+ std::future<char> chain = ftl::chain(std::move(future))
+ .then([](int x) { return static_cast<size_t>(x % 2); })
+ .then([&futures](size_t i) { return std::move(futures[i]); });
+
+ EXPECT_EQ(chain.get(), 'b');
+ }
+}
+
+namespace {
+
+using ByteVector = std::vector<uint8_t>;
+
+ByteVector decrement(ByteVector bytes) {
+ std::transform(bytes.begin(), bytes.end(), bytes.begin(), [](auto b) { return b - 1; });
+ return bytes;
+}
+
+} // namespace
+
+TEST(Future, Chain) {
+ std::packaged_task<const char*()> fetch_string([] { return "ifmmp-"; });
+
+ std::packaged_task<ByteVector(std::string)> append_string([](std::string str) {
+ str += "!xpsme";
+ return ByteVector{str.begin(), str.end()};
+ });
+
+ std::packaged_task<std::future<ByteVector>(ByteVector)> decrement_bytes(
+ [](ByteVector bytes) { return ftl::defer(decrement, std::move(bytes)); });
+
+ auto fetch = fetch_string.get_future();
+ std::thread fetch_thread(std::move(fetch_string));
+
+ std::thread append_thread, decrement_thread;
+
+ EXPECT_EQ(
+ "hello, world",
+ ftl::chain(std::move(fetch))
+ .then([](const char* str) { return std::string(str); })
+ .then([&](std::string str) {
+ auto append = append_string.get_future();
+ append_thread = std::thread(std::move(append_string), std::move(str));
+ return append;
+ })
+ .then([&](ByteVector bytes) {
+ auto decrement = decrement_bytes.get_future();
+ decrement_thread = std::thread(std::move(decrement_bytes), std::move(bytes));
+ return decrement;
+ })
+ .then([](std::future<ByteVector> bytes) { return bytes; })
+ .then([](const ByteVector& bytes) { return std::string(bytes.begin(), bytes.end()); })
+ .get());
+
+ fetch_thread.join();
+ append_thread.join();
+ decrement_thread.join();
+}
+
+} // namespace android::test
diff --git a/libs/ftl/small_map_test.cpp b/libs/ftl/small_map_test.cpp
new file mode 100644
index 0000000..323b9f9
--- /dev/null
+++ b/libs/ftl/small_map_test.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2020 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 <ftl/small_map.h>
+#include <gtest/gtest.h>
+
+#include <cctype>
+
+namespace android::test {
+
+using ftl::SmallMap;
+
+// Keep in sync with example usage in header file.
+TEST(SmallMap, Example) {
+ ftl::SmallMap<int, std::string, 3> map;
+ EXPECT_TRUE(map.empty());
+ EXPECT_FALSE(map.dynamic());
+
+ map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
+ EXPECT_EQ(map.size(), 3u);
+ EXPECT_FALSE(map.dynamic());
+
+ EXPECT_TRUE(map.contains(123));
+
+ EXPECT_EQ(map.find(42, [](const std::string& s) { return s.size(); }), 3u);
+
+ const auto opt = map.find(-1);
+ ASSERT_TRUE(opt);
+
+ std::string& ref = *opt;
+ EXPECT_TRUE(ref.empty());
+ ref = "xyz";
+
+ EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "xyz")(42, "???")(123, "abc")));
+}
+
+TEST(SmallMap, Construct) {
+ {
+ // Default constructor.
+ SmallMap<int, std::string, 2> map;
+
+ EXPECT_TRUE(map.empty());
+ EXPECT_FALSE(map.dynamic());
+ }
+ {
+ // In-place constructor with same types.
+ SmallMap<int, std::string, 5> map =
+ ftl::init::map<int, std::string>(123, "abc")(456, "def")(789, "ghi");
+
+ EXPECT_EQ(map.size(), 3u);
+ EXPECT_EQ(map.max_size(), 5u);
+ EXPECT_FALSE(map.dynamic());
+
+ EXPECT_EQ(map, SmallMap(ftl::init::map(123, "abc")(456, "def")(789, "ghi")));
+ }
+ {
+ // In-place constructor with different types.
+ SmallMap<int, std::string, 5> map =
+ ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
+
+ EXPECT_EQ(map.size(), 3u);
+ EXPECT_EQ(map.max_size(), 5u);
+ EXPECT_FALSE(map.dynamic());
+
+ EXPECT_EQ(map, SmallMap(ftl::init::map(42, "???")(123, "abc")(-1, "\0\0\0")));
+ }
+ {
+ // In-place constructor with implicit size.
+ SmallMap map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
+
+ static_assert(std::is_same_v<decltype(map), SmallMap<int, std::string, 3>>);
+ EXPECT_EQ(map.size(), 3u);
+ EXPECT_EQ(map.max_size(), 3u);
+ EXPECT_FALSE(map.dynamic());
+
+ EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "\0\0\0")(42, "???")(123, "abc")));
+ }
+}
+
+TEST(SmallMap, Find) {
+ {
+ // Constant reference.
+ const ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
+
+ const auto opt = map.find('b');
+ EXPECT_EQ(opt, 'B');
+
+ const char d = 'D';
+ const auto ref = map.find('d').value_or(std::cref(d));
+ EXPECT_EQ(ref.get(), 'D');
+ }
+ {
+ // Mutable reference.
+ ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
+
+ const auto opt = map.find('c');
+ EXPECT_EQ(opt, 'C');
+
+ char d = 'd';
+ const auto ref = map.find('d').value_or(std::ref(d));
+ ref.get() = 'D';
+ EXPECT_EQ(d, 'D');
+ }
+ {
+ // Constant unary operation.
+ const ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
+ EXPECT_EQ(map.find('c', [](char c) { return std::toupper(c); }), 'Z');
+ }
+ {
+ // Mutable unary operation.
+ ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
+ EXPECT_TRUE(map.find('c', [](char& c) { c = std::toupper(c); }));
+
+ EXPECT_EQ(map, SmallMap(ftl::init::map('c', 'Z')('b', 'y')('a', 'x')));
+ }
+}
+
+} // namespace android::test
diff --git a/libs/ftl/small_vector_test.cpp b/libs/ftl/small_vector_test.cpp
new file mode 100644
index 0000000..3a03e69
--- /dev/null
+++ b/libs/ftl/small_vector_test.cpp
@@ -0,0 +1,463 @@
+/*
+ * Copyright 2020 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 <ftl/small_vector.h>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <iterator>
+#include <string>
+#include <utility>
+
+using namespace std::string_literals;
+
+namespace android::test {
+
+using ftl::SmallVector;
+
+// Keep in sync with example usage in header file.
+TEST(SmallVector, Example) {
+ ftl::SmallVector<char, 3> vector;
+ EXPECT_TRUE(vector.empty());
+ EXPECT_FALSE(vector.dynamic());
+
+ vector = {'a', 'b', 'c'};
+ EXPECT_EQ(vector.size(), 3u);
+ EXPECT_FALSE(vector.dynamic());
+
+ vector.push_back('d');
+ EXPECT_TRUE(vector.dynamic());
+
+ vector.unstable_erase(vector.begin());
+ EXPECT_EQ(vector, (ftl::SmallVector{'d', 'b', 'c'}));
+
+ vector.pop_back();
+ EXPECT_EQ(vector.back(), 'b');
+ EXPECT_TRUE(vector.dynamic());
+
+ const char array[] = "hi";
+ vector = ftl::SmallVector(array);
+ EXPECT_EQ(vector, (ftl::SmallVector{'h', 'i', '\0'}));
+ EXPECT_FALSE(vector.dynamic());
+
+ ftl::SmallVector strings = ftl::init::list<std::string>("abc")("123456", 3u)(3u, '?');
+ ASSERT_EQ(strings.size(), 3u);
+ EXPECT_FALSE(strings.dynamic());
+
+ EXPECT_EQ(strings[0], "abc");
+ EXPECT_EQ(strings[1], "123");
+ EXPECT_EQ(strings[2], "???");
+}
+
+TEST(SmallVector, Construct) {
+ {
+ // Default constructor.
+ SmallVector<std::string, 2> vector;
+
+ EXPECT_TRUE(vector.empty());
+ EXPECT_FALSE(vector.dynamic());
+ }
+ {
+ // Array constructor.
+ const float floats[] = {.1f, .2f, .3f};
+ SmallVector vector(floats);
+
+ EXPECT_EQ(vector, (SmallVector{.1f, .2f, .3f}));
+ EXPECT_FALSE(vector.dynamic());
+ }
+ {
+ // Iterator constructor.
+ const char chars[] = "abcdef";
+ std::string string(chars);
+ SmallVector<char, sizeof(chars)> vector(string.begin(), string.end());
+
+ EXPECT_STREQ(vector.begin(), chars);
+ EXPECT_FALSE(vector.dynamic());
+ }
+ {
+ // Variadic constructor with same types.
+ SmallVector vector = {1, 2, 3};
+
+ static_assert(std::is_same_v<decltype(vector), SmallVector<int, 3>>);
+ EXPECT_EQ(vector, (SmallVector{1, 2, 3}));
+ EXPECT_FALSE(vector.dynamic());
+ }
+ {
+ // Variadic constructor with different types.
+ const auto copy = "quince"s;
+ auto move = "tart"s;
+ SmallVector vector = {copy, std::move(move)};
+
+ static_assert(std::is_same_v<decltype(vector), SmallVector<std::string, 2>>);
+ EXPECT_EQ(vector, (SmallVector{"quince"s, "tart"s}));
+ EXPECT_FALSE(vector.dynamic());
+ }
+ {
+ // In-place constructor with same types.
+ SmallVector vector =
+ ftl::init::list<std::string>("redolent", 3u)("velveteen", 6u)("cakewalk", 4u);
+
+ static_assert(std::is_same_v<decltype(vector), SmallVector<std::string, 3>>);
+ EXPECT_EQ(vector, (SmallVector{"red"s, "velvet"s, "cake"s}));
+ EXPECT_FALSE(vector.dynamic());
+ }
+ {
+ // In-place constructor with different types.
+ const auto copy = "red"s;
+ auto move = "velvet"s;
+ std::initializer_list<char> list = {'c', 'a', 'k', 'e'};
+ SmallVector vector = ftl::init::list<std::string>(copy.c_str())(std::move(move))(list);
+
+ static_assert(std::is_same_v<decltype(vector), SmallVector<std::string, 3>>);
+ EXPECT_TRUE(move.empty());
+ EXPECT_EQ(vector, (SmallVector{"red"s, "velvet"s, "cake"s}));
+ EXPECT_FALSE(vector.dynamic());
+ }
+ {
+ // Conversion from StaticVector.
+ ftl::StaticVector doubles = {.1, .2, .3};
+ SmallVector vector = std::move(doubles);
+ EXPECT_TRUE(doubles.empty());
+
+ static_assert(std::is_same_v<decltype(vector), SmallVector<double, 3>>);
+ EXPECT_EQ(vector, (SmallVector{.1, .2, .3}));
+ EXPECT_FALSE(vector.dynamic());
+ }
+}
+
+TEST(SmallVector, String) {
+ SmallVector<char, 10> chars;
+ char c = 'a';
+ std::generate_n(std::back_inserter(chars), chars.max_size(), [&c] { return c++; });
+ chars.push_back('\0');
+
+ EXPECT_TRUE(chars.dynamic());
+ EXPECT_EQ(chars.size(), 11u);
+ EXPECT_STREQ(chars.begin(), "abcdefghij");
+
+ // Constructor takes iterator range.
+ const char numbers[] = "123456";
+ SmallVector<char, 10> string(std::begin(numbers), std::end(numbers));
+
+ EXPECT_FALSE(string.dynamic());
+ EXPECT_STREQ(string.begin(), "123456");
+ EXPECT_EQ(string.size(), 7u);
+
+ // Similar to emplace, but replaces rather than inserts.
+ string.replace(string.begin() + 5, '\0');
+ EXPECT_STREQ(string.begin(), "12345");
+
+ swap(chars, string);
+
+ EXPECT_STREQ(chars.begin(), "12345");
+ EXPECT_STREQ(string.begin(), "abcdefghij");
+
+ EXPECT_FALSE(chars.dynamic());
+ EXPECT_TRUE(string.dynamic());
+}
+
+TEST(SmallVector, CopyableElement) {
+ struct Pair {
+ // Needed because std::vector does not use list initialization to emplace.
+ Pair(int a, int b) : a(a), b(b) {}
+
+ const int a, b;
+ bool operator==(Pair p) const { return p.a == a && p.b == b; }
+ };
+
+ SmallVector<Pair, 5> pairs;
+
+ EXPECT_TRUE(pairs.empty());
+ EXPECT_EQ(pairs.max_size(), 5u);
+
+ for (size_t i = 0; i < pairs.max_size(); ++i) {
+ EXPECT_EQ(pairs.size(), i);
+
+ const int a = static_cast<int>(i) * 2;
+ EXPECT_EQ(pairs.emplace_back(a, a + 1), Pair(a, a + 1));
+ }
+
+ EXPECT_EQ(pairs.size(), 5u);
+ EXPECT_FALSE(pairs.dynamic());
+
+ // The vector is promoted when full.
+ EXPECT_EQ(pairs.emplace_back(10, 11), Pair(10, 11));
+ EXPECT_TRUE(pairs.dynamic());
+
+ EXPECT_EQ(pairs, (SmallVector{Pair{0, 1}, Pair{2, 3}, Pair{4, 5}, Pair{6, 7}, Pair{8, 9},
+ Pair{10, 11}}));
+
+ // Constructor takes at most N elements.
+ SmallVector<int, 6> sums = {0, 0, 0, 0, 0, 0};
+ EXPECT_FALSE(sums.dynamic());
+
+ // Random-access iterators comply with standard.
+ std::transform(pairs.begin(), pairs.end(), sums.begin(), [](Pair p) { return p.a + p.b; });
+ EXPECT_EQ(sums, (SmallVector{1, 5, 9, 13, 17, 21}));
+
+ sums.pop_back();
+ std::reverse(sums.begin(), sums.end());
+
+ EXPECT_EQ(sums, (SmallVector{17, 13, 9, 5, 1}));
+}
+
+TEST(SmallVector, MovableElement) {
+ // Construct std::string elements in place from per-element arguments.
+ SmallVector strings = ftl::init::list<std::string>()()()("cake")("velvet")("red")();
+ strings.pop_back();
+
+ EXPECT_EQ(strings.max_size(), 7u);
+ EXPECT_EQ(strings.size(), 6u);
+
+ // Erase "cake" and append a substring copy.
+ {
+ const auto it =
+ std::find_if(strings.begin(), strings.end(), [](const auto& s) { return !s.empty(); });
+ ASSERT_FALSE(it == strings.end());
+ EXPECT_EQ(*it, "cake");
+
+ // Construct std::string from first 4 characters of string literal.
+ strings.unstable_erase(it);
+ EXPECT_EQ(strings.emplace_back("cakewalk", 4u), "cake"s);
+ }
+
+ strings[1] = "quince"s;
+
+ // Replace last empty string with "tart".
+ {
+ const auto rit = std::find(strings.rbegin(), strings.rend(), std::string());
+ ASSERT_FALSE(rit == strings.rend());
+
+ std::initializer_list<char> list = {'t', 'a', 'r', 't'};
+ strings.replace(rit.base() - 1, list);
+ }
+
+ strings.front().assign("pie");
+
+ EXPECT_EQ(strings, (SmallVector{"pie"s, "quince"s, "tart"s, "red"s, "velvet"s, "cake"s}));
+
+ strings.push_back("nougat");
+ strings.push_back("oreo");
+ EXPECT_TRUE(strings.dynamic());
+
+ std::rotate(strings.begin(), strings.end() - 2, strings.end());
+
+ EXPECT_EQ(strings, (SmallVector{"nougat"s, "oreo"s, "pie"s, "quince"s, "tart"s, "red"s, "velvet"s,
+ "cake"s}));
+}
+
+TEST(SmallVector, Replace) {
+ // Replacing does not require a copy/move assignment operator.
+ struct Word {
+ explicit Word(std::string str) : str(std::move(str)) {}
+ const std::string str;
+
+ bool operator==(const Word& other) const { return other.str == str; }
+ };
+
+ SmallVector words = ftl::init::list<Word>("colored")("velour");
+
+ // The replaced element can be referenced by the replacement.
+ {
+ const Word& word = words.replace(words.last(), words.back().str.substr(0, 3) + "vet");
+ EXPECT_EQ(word, Word("velvet"));
+ }
+
+ // The vector is not promoted if replacing while full.
+ EXPECT_FALSE(words.dynamic());
+
+ words.emplace_back("cake");
+ EXPECT_TRUE(words.dynamic());
+
+ {
+ const Word& word = words.replace(words.begin(), words.front().str.substr(4));
+ EXPECT_EQ(word, Word("red"));
+ }
+
+ EXPECT_EQ(words, (SmallVector{Word("red"), Word("velvet"), Word("cake")}));
+}
+
+TEST(SmallVector, ReverseAppend) {
+ SmallVector strings = {"red"s, "velvet"s, "cake"s};
+ EXPECT_FALSE(strings.dynamic());
+
+ auto rit = strings.rbegin();
+ while (rit != strings.rend()) {
+ // Iterator and reference are invalidated on insertion.
+ const auto i = std::distance(strings.begin(), rit.base());
+ std::string s = *rit;
+
+ strings.push_back(std::move(s));
+ rit = std::make_reverse_iterator(strings.begin() + i) + 1;
+ }
+
+ EXPECT_EQ(strings, (SmallVector{"red"s, "velvet"s, "cake"s, "cake"s, "velvet"s, "red"s}));
+ EXPECT_TRUE(strings.dynamic());
+}
+
+TEST(SmallVector, Sort) {
+ SmallVector strings = ftl::init::list<std::string>("pie")("quince")("tart")("red")("velvet");
+ strings.push_back("cake"s);
+
+ auto sorted = std::move(strings);
+ EXPECT_TRUE(strings.empty());
+
+ EXPECT_TRUE(sorted.dynamic());
+ EXPECT_TRUE(strings.dynamic());
+
+ std::sort(sorted.begin(), sorted.end());
+ EXPECT_EQ(sorted, (SmallVector{"cake"s, "pie"s, "quince"s, "red"s, "tart"s, "velvet"s}));
+
+ // Constructor takes array reference.
+ {
+ const char* array[] = {"cake", "lie"};
+ strings = SmallVector(array);
+ EXPECT_FALSE(strings.dynamic());
+ }
+
+ EXPECT_GT(sorted, strings);
+ swap(sorted, strings);
+ EXPECT_LT(sorted, strings);
+
+ EXPECT_FALSE(sorted.dynamic());
+ EXPECT_TRUE(strings.dynamic());
+
+ // Append remaining elements, such that "pie" is the only difference.
+ for (const char* str : {"quince", "red", "tart", "velvet"}) {
+ sorted.emplace_back(str);
+ }
+ EXPECT_TRUE(sorted.dynamic());
+
+ EXPECT_NE(sorted, strings);
+
+ // Replace second element with "pie".
+ const auto it = sorted.begin() + 1;
+ EXPECT_EQ(sorted.replace(it, 'p' + it->substr(1)), "pie");
+
+ EXPECT_EQ(sorted, strings);
+}
+
+namespace {
+
+struct DestroyCounts {
+ DestroyCounts(int& live, int& dead) : counts{live, dead} {}
+ DestroyCounts(const DestroyCounts& other) : counts(other.counts) {}
+ DestroyCounts(DestroyCounts&& other) : counts(other.counts) { other.alive = false; }
+ ~DestroyCounts() { ++(alive ? counts.live : counts.dead); }
+
+ struct {
+ int& live;
+ int& dead;
+ } counts;
+
+ bool alive = true;
+};
+
+void swap(DestroyCounts& lhs, DestroyCounts& rhs) {
+ std::swap(lhs.alive, rhs.alive);
+}
+
+} // namespace
+
+TEST(SmallVector, Destroy) {
+ int live = 0;
+ int dead = 0;
+
+ { SmallVector<DestroyCounts, 3> counts; }
+ EXPECT_EQ(0, live);
+ EXPECT_EQ(0, dead);
+
+ {
+ SmallVector<DestroyCounts, 3> counts;
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+
+ EXPECT_FALSE(counts.dynamic());
+ }
+ EXPECT_EQ(3, live);
+ EXPECT_EQ(0, dead);
+
+ live = 0;
+ {
+ SmallVector<DestroyCounts, 3> counts;
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+
+ EXPECT_TRUE(counts.dynamic());
+ }
+ EXPECT_EQ(4, live);
+ EXPECT_EQ(3, dead);
+
+ live = dead = 0;
+ {
+ SmallVector<DestroyCounts, 2> counts;
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+
+ auto copy = counts;
+ EXPECT_TRUE(copy.dynamic());
+ }
+ EXPECT_EQ(6, live);
+ EXPECT_EQ(2, dead);
+
+ live = dead = 0;
+ {
+ SmallVector<DestroyCounts, 2> counts;
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+
+ auto move = std::move(counts);
+ EXPECT_TRUE(move.dynamic());
+ }
+ EXPECT_EQ(3, live);
+ EXPECT_EQ(2, dead);
+
+ live = dead = 0;
+ {
+ SmallVector<DestroyCounts, 2> counts1;
+ counts1.emplace_back(live, dead);
+ counts1.emplace_back(live, dead);
+ counts1.emplace_back(live, dead);
+
+ EXPECT_TRUE(counts1.dynamic());
+ EXPECT_EQ(2, dead);
+ dead = 0;
+
+ SmallVector<DestroyCounts, 2> counts2;
+ counts2.emplace_back(live, dead);
+
+ EXPECT_FALSE(counts2.dynamic());
+
+ swap(counts1, counts2);
+
+ EXPECT_FALSE(counts1.dynamic());
+ EXPECT_TRUE(counts2.dynamic());
+
+ EXPECT_EQ(0, live);
+ EXPECT_EQ(1, dead);
+
+ dead = 0;
+ }
+ EXPECT_EQ(4, live);
+ EXPECT_EQ(0, dead);
+}
+
+} // namespace android::test
diff --git a/libs/ftl/static_vector_test.cpp b/libs/ftl/static_vector_test.cpp
new file mode 100644
index 0000000..cbe8dff
--- /dev/null
+++ b/libs/ftl/static_vector_test.cpp
@@ -0,0 +1,399 @@
+/*
+ * Copyright 2020 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 <ftl/static_vector.h>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <iterator>
+#include <string>
+#include <utility>
+
+using namespace std::string_literals;
+
+namespace android::test {
+
+using ftl::StaticVector;
+
+// Keep in sync with example usage in header file.
+TEST(StaticVector, Example) {
+ ftl::StaticVector<char, 3> vector;
+ EXPECT_TRUE(vector.empty());
+
+ vector = {'a', 'b'};
+ EXPECT_EQ(vector.size(), 2u);
+
+ vector.push_back('c');
+ EXPECT_TRUE(vector.full());
+
+ EXPECT_FALSE(vector.push_back('d'));
+ EXPECT_EQ(vector.size(), 3u);
+
+ vector.unstable_erase(vector.begin());
+ EXPECT_EQ(vector, (ftl::StaticVector{'c', 'b'}));
+
+ vector.pop_back();
+ EXPECT_EQ(vector.back(), 'c');
+
+ const char array[] = "hi";
+ vector = ftl::StaticVector(array);
+ EXPECT_EQ(vector, (ftl::StaticVector{'h', 'i', '\0'}));
+
+ ftl::StaticVector strings = ftl::init::list<std::string>("abc")("123456", 3u)(3u, '?');
+ ASSERT_EQ(strings.size(), 3u);
+
+ EXPECT_EQ(strings[0], "abc");
+ EXPECT_EQ(strings[1], "123");
+ EXPECT_EQ(strings[2], "???");
+}
+
+TEST(StaticVector, Construct) {
+ {
+ // Default constructor.
+ StaticVector<std::string, 2> vector;
+ EXPECT_TRUE(vector.empty());
+ }
+ {
+ // Array constructor.
+ const float floats[] = {.1f, .2f, .3f};
+ StaticVector vector(floats);
+ EXPECT_EQ(vector, (StaticVector{.1f, .2f, .3f}));
+ }
+ {
+ // Iterator constructor.
+ const char chars[] = "abcdef";
+ std::string string(chars);
+ StaticVector<char, sizeof(chars)> vector(string.begin(), string.end());
+
+ EXPECT_STREQ(vector.begin(), chars);
+ }
+ {
+ // Variadic constructor with same types.
+ StaticVector vector = {1, 2, 3};
+
+ static_assert(std::is_same_v<decltype(vector), StaticVector<int, 3>>);
+ EXPECT_EQ(vector, (StaticVector{1, 2, 3}));
+ }
+ {
+ // Variadic constructor with different types.
+ const auto copy = "quince"s;
+ auto move = "tart"s;
+ StaticVector vector = {copy, std::move(move)};
+
+ static_assert(std::is_same_v<decltype(vector), StaticVector<std::string, 2>>);
+ EXPECT_EQ(vector, (StaticVector{"quince"s, "tart"s}));
+ }
+ {
+ // In-place constructor with same types.
+ StaticVector vector =
+ ftl::init::list<std::string>("redolent", 3u)("velveteen", 6u)("cakewalk", 4u);
+
+ static_assert(std::is_same_v<decltype(vector), StaticVector<std::string, 3>>);
+ EXPECT_EQ(vector, (StaticVector{"red"s, "velvet"s, "cake"s}));
+ }
+ {
+ // In-place constructor with different types.
+ const auto copy = "red"s;
+ auto move = "velvet"s;
+ std::initializer_list<char> list = {'c', 'a', 'k', 'e'};
+ StaticVector vector = ftl::init::list<std::string>(copy.c_str())(std::move(move))(list);
+
+ static_assert(std::is_same_v<decltype(vector), StaticVector<std::string, 3>>);
+ EXPECT_TRUE(move.empty());
+ EXPECT_EQ(vector, (StaticVector{"red"s, "velvet"s, "cake"s}));
+ }
+ {
+ struct String {
+ explicit String(const char* str) : str(str) {}
+ explicit String(const char** ptr) : str(*ptr) {}
+ const char* str;
+ };
+
+ const char* strings[] = {"a", "b", "c", "d"};
+
+ {
+ // Two iterator-like elements.
+ StaticVector<String, 3> vector(strings, strings + 3);
+ ASSERT_EQ(vector.size(), 2u);
+
+ EXPECT_STREQ(vector[0].str, "a");
+ EXPECT_STREQ(vector[1].str, "d");
+ }
+ {
+ // Disambiguating iterator constructor.
+ StaticVector<String, 3> vector(ftl::kIteratorRange, strings, strings + 3);
+ ASSERT_EQ(vector.size(), 3u);
+
+ EXPECT_STREQ(vector[0].str, "a");
+ EXPECT_STREQ(vector[1].str, "b");
+ EXPECT_STREQ(vector[2].str, "c");
+ }
+ }
+}
+
+TEST(StaticVector, String) {
+ StaticVector<char, 10> chars;
+ char c = 'a';
+ std::generate_n(std::back_inserter(chars), chars.max_size(), [&c] { return c++; });
+ chars.back() = '\0';
+
+ EXPECT_STREQ(chars.begin(), "abcdefghi");
+
+ // Constructor takes iterator range.
+ const char numbers[] = "123456";
+ StaticVector<char, 10> string(std::begin(numbers), std::end(numbers));
+
+ EXPECT_STREQ(string.begin(), "123456");
+ EXPECT_EQ(string.size(), 7u);
+
+ // Similar to emplace, but replaces rather than inserts.
+ string.replace(string.begin() + 5, '\0');
+ EXPECT_STREQ(string.begin(), "12345");
+
+ swap(chars, string);
+
+ EXPECT_STREQ(chars.begin(), "12345");
+ EXPECT_STREQ(string.begin(), "abcdefghi");
+}
+
+TEST(StaticVector, CopyableElement) {
+ struct Pair {
+ const int a, b;
+ bool operator==(Pair p) const { return p.a == a && p.b == b; }
+ };
+
+ StaticVector<Pair, 5> pairs;
+
+ EXPECT_TRUE(pairs.empty());
+ EXPECT_EQ(pairs.max_size(), 5u);
+
+ for (size_t i = 0; i < pairs.max_size(); ++i) {
+ EXPECT_EQ(pairs.size(), i);
+
+ const int a = static_cast<int>(i) * 2;
+ const auto it = pairs.emplace_back(a, a + 1);
+ ASSERT_NE(it, pairs.end());
+ EXPECT_EQ(*it, (Pair{a, a + 1}));
+ }
+
+ EXPECT_TRUE(pairs.full());
+ EXPECT_EQ(pairs.size(), 5u);
+
+ // Insertion fails if the vector is full.
+ const auto it = pairs.emplace_back(10, 11);
+ EXPECT_EQ(it, pairs.end());
+
+ EXPECT_EQ(pairs, (StaticVector{Pair{0, 1}, Pair{2, 3}, Pair{4, 5}, Pair{6, 7}, Pair{8, 9}}));
+
+ // Constructor takes at most N elements.
+ StaticVector<int, 6> sums = {0, 0, 0, 0, 0, -1};
+ EXPECT_TRUE(sums.full());
+
+ // Random-access iterators comply with standard.
+ std::transform(pairs.begin(), pairs.end(), sums.begin(), [](Pair p) { return p.a + p.b; });
+ EXPECT_EQ(sums, (StaticVector{1, 5, 9, 13, 17, -1}));
+
+ sums.pop_back();
+ std::reverse(sums.begin(), sums.end());
+
+ EXPECT_EQ(sums, (StaticVector{17, 13, 9, 5, 1}));
+}
+
+TEST(StaticVector, MovableElement) {
+ // Construct std::string elements in place from per-element arguments.
+ StaticVector strings = ftl::init::list<std::string>()()()("cake")("velvet")("red")();
+ strings.pop_back();
+
+ EXPECT_EQ(strings.max_size(), 7u);
+ EXPECT_EQ(strings.size(), 6u);
+
+ // Erase "cake" and append a substring copy.
+ {
+ auto it =
+ std::find_if(strings.begin(), strings.end(), [](const auto& s) { return !s.empty(); });
+ ASSERT_FALSE(it == strings.end());
+ EXPECT_EQ(*it, "cake");
+
+ strings.unstable_erase(it);
+
+ // Construct std::string from first 4 characters of string literal.
+ it = strings.emplace_back("cakewalk", 4u);
+ ASSERT_NE(it, strings.end());
+ EXPECT_EQ(*it, "cake"s);
+ }
+
+ strings[1] = "quince"s;
+
+ // Replace last empty string with "tart".
+ {
+ const auto rit = std::find(strings.rbegin(), strings.rend(), std::string());
+ ASSERT_FALSE(rit == strings.rend());
+
+ std::initializer_list<char> list = {'t', 'a', 'r', 't'};
+ strings.replace(rit.base() - 1, list);
+ }
+
+ strings.front().assign("pie");
+
+ EXPECT_EQ(strings, (StaticVector{"pie"s, "quince"s, "tart"s, "red"s, "velvet"s, "cake"s}));
+}
+
+TEST(StaticVector, Replace) {
+ // Replacing does not require a copy/move assignment operator.
+ struct Word {
+ explicit Word(std::string str) : str(std::move(str)) {}
+ const std::string str;
+ };
+
+ StaticVector words = ftl::init::list<Word>("red")("velour")("cake");
+
+ // The replaced element can be referenced by the replacement.
+ const auto it = words.begin() + 1;
+ const Word& word = words.replace(it, it->str.substr(0, 3) + "vet");
+ EXPECT_EQ(word.str, "velvet");
+}
+
+TEST(StaticVector, ReverseTruncate) {
+ StaticVector<std::string, 10> strings("pie", "quince", "tart", "red", "velvet", "cake");
+ EXPECT_FALSE(strings.full());
+
+ for (auto it = strings.begin(); it != strings.end(); ++it) {
+ strings.replace(it, strings.back());
+ strings.pop_back();
+ }
+
+ EXPECT_EQ(strings, (StaticVector{"cake"s, "velvet"s, "red"s}));
+}
+
+TEST(StaticVector, Sort) {
+ StaticVector<std::string, 7> strings("pie", "quince", "tart", "red", "velvet", "cake");
+ EXPECT_FALSE(strings.full());
+
+ auto sorted = std::move(strings);
+ EXPECT_TRUE(strings.empty());
+
+ std::sort(sorted.begin(), sorted.end());
+ EXPECT_EQ(sorted, (StaticVector{"cake"s, "pie"s, "quince"s, "red"s, "tart"s, "velvet"s}));
+
+ // Constructor takes array reference.
+ {
+ const char* array[] = {"cake", "lie"};
+ strings = StaticVector(array);
+ }
+
+ EXPECT_GT(sorted, strings);
+ swap(sorted, strings);
+ EXPECT_LT(sorted, strings);
+
+ // Append remaining elements, such that "pie" is the only difference.
+ for (const char* str : {"quince", "red", "tart", "velvet"}) {
+ sorted.emplace_back(str);
+ }
+
+ EXPECT_NE(sorted, strings);
+
+ // Replace second element with "pie".
+ const auto it = sorted.begin() + 1;
+ EXPECT_EQ(sorted.replace(it, 'p' + it->substr(1)), "pie");
+
+ EXPECT_EQ(sorted, strings);
+}
+
+namespace {
+
+struct DestroyCounts {
+ DestroyCounts(int& live, int& dead) : counts{live, dead} {}
+ DestroyCounts(const DestroyCounts& other) : counts(other.counts) {}
+ DestroyCounts(DestroyCounts&& other) : counts(other.counts) { other.alive = false; }
+ ~DestroyCounts() { ++(alive ? counts.live : counts.dead); }
+
+ struct {
+ int& live;
+ int& dead;
+ } counts;
+
+ bool alive = true;
+};
+
+void swap(DestroyCounts& lhs, DestroyCounts& rhs) {
+ std::swap(lhs.alive, rhs.alive);
+}
+
+} // namespace
+
+TEST(StaticVector, Destroy) {
+ int live = 0;
+ int dead = 0;
+
+ { StaticVector<DestroyCounts, 5> counts; }
+ EXPECT_EQ(0, live);
+ EXPECT_EQ(0, dead);
+
+ {
+ StaticVector<DestroyCounts, 5> counts;
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+ }
+ EXPECT_EQ(3, live);
+ EXPECT_EQ(0, dead);
+
+ live = 0;
+ {
+ StaticVector<DestroyCounts, 5> counts;
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+
+ auto copy = counts;
+ }
+ EXPECT_EQ(6, live);
+ EXPECT_EQ(0, dead);
+
+ live = 0;
+ {
+ StaticVector<DestroyCounts, 5> counts;
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+
+ auto move = std::move(counts);
+ }
+ EXPECT_EQ(3, live);
+ EXPECT_EQ(3, dead);
+
+ live = dead = 0;
+ {
+ StaticVector<DestroyCounts, 5> counts1;
+ counts1.emplace_back(live, dead);
+ counts1.emplace_back(live, dead);
+ counts1.emplace_back(live, dead);
+
+ StaticVector<DestroyCounts, 5> counts2;
+ counts2.emplace_back(live, dead);
+
+ swap(counts1, counts2);
+
+ EXPECT_EQ(0, live);
+ EXPECT_EQ(2, dead);
+
+ dead = 0;
+ }
+ EXPECT_EQ(4, live);
+ EXPECT_EQ(0, dead);
+}
+
+} // namespace android::test
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 3d0f8bb..d54de49 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -29,7 +29,6 @@
#include <android-base/strings.h>
#include <android/dlext.h>
#include <binder/IServiceManager.h>
-#include <cutils/properties.h>
#include <graphicsenv/IGpuService.h>
#include <log/log.h>
#include <nativeloader/dlext_namespaces.h>
@@ -74,7 +73,7 @@
static std::string vndkVersionStr() {
#ifdef __BIONIC__
- return android::base::GetProperty("ro.vndk.version", "");
+ return base::GetProperty("ro.vndk.version", "");
#endif
return "";
}
@@ -345,10 +344,8 @@
}
bool GraphicsEnv::checkAngleRules(void* so) {
- char manufacturer[PROPERTY_VALUE_MAX];
- char model[PROPERTY_VALUE_MAX];
- property_get("ro.product.manufacturer", manufacturer, "UNSET");
- property_get("ro.product.model", model, "UNSET");
+ auto manufacturer = base::GetProperty("ro.product.manufacturer", "UNSET");
+ auto model = base::GetProperty("ro.product.model", "UNSET");
auto ANGLEGetFeatureSupportUtilAPIVersion =
(fpANGLEGetFeatureSupportUtilAPIVersion)dlsym(so,
@@ -401,7 +398,8 @@
ALOGW("ANGLE feature-support library cannot obtain SystemInfo");
break;
}
- if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer, model, systemInfoHandle)) {
+ if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer.c_str(), model.c_str(),
+ systemInfoHandle)) {
ALOGW("ANGLE feature-support library cannot add device info to SystemInfo");
break;
}
@@ -468,7 +466,8 @@
}
void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName,
- const std::string developerOptIn, const int rulesFd,
+ const std::string developerOptIn,
+ const std::vector<std::string> eglFeatures, const int rulesFd,
const long rulesOffset, const long rulesLength) {
if (mUseAngle != UNKNOWN) {
// We've already figured out an answer for this app, so just return.
@@ -477,6 +476,8 @@
return;
}
+ mAngleEglFeatures = std::move(eglFeatures);
+
ALOGV("setting ANGLE path to '%s'", path.c_str());
mAnglePath = path;
ALOGV("setting ANGLE app name to '%s'", appName.c_str());
@@ -522,6 +523,10 @@
return mAngleAppName;
}
+const std::vector<std::string>& GraphicsEnv::getAngleEglFeatures() {
+ return mAngleEglFeatures;
+}
+
const std::string& GraphicsEnv::getLayerPaths() {
return mLayerPaths;
}
@@ -543,7 +548,7 @@
}
// Return true if all the required libraries from vndk and sphal namespace are
-// linked to the Game Driver namespace correctly.
+// linked to the updatable gfx driver namespace correctly.
bool GraphicsEnv::linkDriverNamespaceLocked(android_namespace_t* vndkNamespace) {
const std::string llndkLibraries = getSystemNativeLibraries(NativeLibrary::LLNDK);
if (llndkLibraries.empty()) {
@@ -653,8 +658,7 @@
mAngleNamespace = android_create_namespace("ANGLE",
nullptr, // ld_library_path
mAnglePath.c_str(), // default_library_path
- ANDROID_NAMESPACE_TYPE_SHARED |
- ANDROID_NAMESPACE_TYPE_ISOLATED,
+ ANDROID_NAMESPACE_TYPE_SHARED_ISOLATED,
nullptr, // permitted_when_isolated_path
nullptr);
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 22a2332..900fc49 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -97,12 +97,15 @@
// in the search path must have a '!' after the zip filename, e.g.
// /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a
void setAngleInfo(const std::string path, const std::string appName, std::string devOptIn,
- const int rulesFd, const long rulesOffset, const long rulesLength);
+ const std::vector<std::string> eglFeatures, const int rulesFd,
+ const long rulesOffset, const long rulesLength);
// Get the ANGLE driver namespace.
android_namespace_t* getAngleNamespace();
// Get the app name for ANGLE debug message.
std::string& getAngleAppName();
+ const std::vector<std::string>& getAngleEglFeatures();
+
/*
* Apis for debug layer
*/
@@ -154,6 +157,8 @@
std::string mAngleAppName;
// ANGLE developer opt in status.
std::string mAngleDeveloperOptIn;
+ // ANGLE EGL features;
+ std::vector<std::string> mAngleEglFeatures;
// ANGLE rules.
std::vector<char> mRulesBuffer;
// Use ANGLE flag.
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 8398794..64203f7 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -39,6 +39,50 @@
min_sdk_version: "29",
}
+cc_library_headers {
+ name: "libgui_aidl_headers",
+ vendor_available: true,
+ static_libs: [
+ "libgui_aidl_static",
+ ],
+
+ export_static_lib_headers: [
+ "libgui_aidl_static",
+ ],
+}
+
+filegroup {
+ name: "libgui_aidl",
+ srcs: ["aidl/**/*.aidl"],
+ path: "aidl/",
+}
+
+cc_library_static {
+ name: "libgui_aidl_static",
+ vendor_available: true,
+ srcs: [
+ ":libgui_aidl",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "libui",
+ ],
+
+ local_include_dirs: [
+ "include",
+ ],
+
+ export_shared_lib_headers: [
+ "libbinder",
+ ],
+
+ aidl: {
+ export_aidl_headers: true
+ }
+}
+
+
cc_library_shared {
name: "libgui",
vendor_available: true,
@@ -50,8 +94,16 @@
defaults: ["libgui_bufferqueue-defaults"],
+ static_libs: [
+ "libgui_aidl_static",
+ ],
+ export_static_lib_headers: [
+ "libgui_aidl_static",
+ ],
+
srcs: [
":framework_native_aidl",
+ ":inputconstants_aidl",
":libgui_bufferqueue_sources",
"BitTube.cpp",
@@ -64,8 +116,8 @@
"DebugEGLImageTracker.cpp",
"DisplayEventDispatcher.cpp",
"DisplayEventReceiver.cpp",
+ "FrameTimelineInfo.cpp",
"GLConsumer.cpp",
- "GuiConfig.cpp",
"IConsumerListener.cpp",
"IDisplayEventConnection.cpp",
"IGraphicBufferConsumer.cpp",
@@ -80,10 +132,12 @@
"LayerState.cpp",
"OccupancyTracker.cpp",
"StreamSplitter.cpp",
+ "ScreenCaptureResults.cpp",
"Surface.cpp",
"SurfaceControl.cpp",
"SurfaceComposerClient.cpp",
"SyncFeatures.cpp",
+ "TransactionTracing.cpp",
"view/Surface.cpp",
"bufferqueue/1.0/B2HProducerListener.cpp",
"bufferqueue/1.0/H2BGraphicBufferProducer.cpp",
@@ -102,6 +156,11 @@
export_shared_lib_headers: [
"libbinder",
+ "libinput",
+ ],
+
+ export_header_lib_headers: [
+ "libgui_aidl_headers",
],
// bufferhub is not used when building libgui for vendors
@@ -125,15 +184,16 @@
},
},
- header_libs: [
- "libdvr_headers",
- "libpdx_headers",
- ],
-
aidl: {
export_aidl_headers: true,
},
+ header_libs: [
+ "libdvr_headers",
+ "libgui_aidl_headers",
+ "libpdx_headers",
+ ],
+
pgo: {
sampling: true,
profile_file: "libgui/libgui.profdata",
@@ -163,13 +223,16 @@
defaults: ["libgui_bufferqueue-defaults"],
srcs: [
+ ":inputconstants_aidl",
":libgui_bufferqueue_sources",
+ ":libgui_aidl",
],
}
filegroup {
name: "libgui_bufferqueue_sources",
srcs: [
+ "BatchBufferOps.cpp",
"BufferItem.cpp",
"BufferQueue.cpp",
"BufferQueueConsumer.cpp",
@@ -180,7 +243,7 @@
"FrameTimestamps.cpp",
"GLConsumerUtils.cpp",
"HdrMetadata.cpp",
- "QueueBufferInputOutput.cpp",
+ "IGraphicBufferProducerFlattenables.cpp",
"bufferqueue/1.0/Conversion.cpp",
"bufferqueue/1.0/H2BProducerListener.cpp",
"bufferqueue/1.0/WProducerListener.cpp",
@@ -246,6 +309,10 @@
"libnativebase_headers",
],
+ include_dirs: [
+ "frameworks/native/include",
+ ],
+
export_shared_lib_headers: [
"libEGL",
"libnativewindow",
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 56591bd..3d854c2 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -18,10 +18,17 @@
#define LOG_TAG "BLASTBufferQueue"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+//#define LOG_NDEBUG 0
#include <gui/BLASTBufferQueue.h>
#include <gui/BufferItemConsumer.h>
+#include <gui/BufferQueueConsumer.h>
+#include <gui/BufferQueueCore.h>
+#include <gui/BufferQueueProducer.h>
#include <gui/GLConsumer.h>
+#include <gui/IProducerListener.h>
+#include <gui/Surface.h>
+#include <utils/Singleton.h>
#include <utils/Trace.h>
@@ -29,10 +36,26 @@
using namespace std::chrono_literals;
+namespace {
+inline const char* toString(bool b) {
+ return b ? "true" : "false";
+}
+} // namespace
+
namespace android {
+// Macros to include adapter info in log messages
+#define BQA_LOGV(x, ...) \
+ ALOGV("[%s](f:%u,a:%u) " x, mName.c_str(), mNumFrameAvailable, mNumAcquired, ##__VA_ARGS__)
+// enable logs for a single layer
+//#define BQA_LOGV(x, ...) \
+// ALOGV_IF((strstr(mName.c_str(), "SurfaceView") != nullptr), "[%s](f:%u,a:%u) " x, \
+// mName.c_str(), mNumFrameAvailable, mNumAcquired, ##__VA_ARGS__)
+#define BQA_LOGE(x, ...) \
+ ALOGE("[%s](f:%u,a:%u) " x, mName.c_str(), mNumFrameAvailable, mNumAcquired, ##__VA_ARGS__)
+
void BLASTBufferItemConsumer::onDisconnect() {
- Mutex::Autolock lock(mFrameEventHistoryMutex);
+ Mutex::Autolock lock(mMutex);
mPreviouslyConnected = mCurrentlyConnected;
mCurrentlyConnected = false;
if (mPreviouslyConnected) {
@@ -43,7 +66,7 @@
void BLASTBufferItemConsumer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
FrameEventHistoryDelta* outDelta) {
- Mutex::Autolock lock(mFrameEventHistoryMutex);
+ Mutex::Autolock lock(mMutex);
if (newTimestamps) {
// BufferQueueProducer only adds a new timestamp on
// queueBuffer
@@ -67,7 +90,7 @@
const sp<Fence>& prevReleaseFence,
CompositorTiming compositorTiming,
nsecs_t latchTime, nsecs_t dequeueReadyTime) {
- Mutex::Autolock lock(mFrameEventHistoryMutex);
+ Mutex::Autolock lock(mMutex);
// if the producer is not connected, don't bother updating,
// the next producer that connects won't access this frame event
@@ -85,7 +108,7 @@
void BLASTBufferItemConsumer::getConnectionEvents(uint64_t frameNumber, bool* needsDisconnect) {
bool disconnect = false;
- Mutex::Autolock lock(mFrameEventHistoryMutex);
+ Mutex::Autolock lock(mMutex);
while (!mDisconnectEvents.empty() && mDisconnectEvents.front() <= frameNumber) {
disconnect = true;
mDisconnectEvents.pop();
@@ -93,48 +116,110 @@
if (needsDisconnect != nullptr) *needsDisconnect = disconnect;
}
-BLASTBufferQueue::BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, int height,
- bool enableTripleBuffering)
- : mSurfaceControl(surface),
- mWidth(width),
- mHeight(height),
+void BLASTBufferItemConsumer::setBlastBufferQueue(BLASTBufferQueue* blastbufferqueue) {
+ Mutex::Autolock lock(mMutex);
+ mBLASTBufferQueue = blastbufferqueue;
+}
+
+void BLASTBufferItemConsumer::onSidebandStreamChanged() {
+ Mutex::Autolock lock(mMutex);
+ if (mBLASTBufferQueue != nullptr) {
+ sp<NativeHandle> stream = getSidebandStream();
+ mBLASTBufferQueue->setSidebandStream(stream);
+ }
+}
+
+BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface,
+ int width, int height, int32_t format)
+ : mName(name),
+ mSurfaceControl(surface),
+ mSize(width, height),
+ mRequestedSize(mSize),
+ mFormat(format),
mNextTransaction(nullptr) {
- BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+ createBufferQueue(&mProducer, &mConsumer);
// since the adapter is in the client process, set dequeue timeout
// explicitly so that dequeueBuffer will block
mProducer->setDequeueTimeout(std::numeric_limits<int64_t>::max());
- if (enableTripleBuffering) {
- mProducer->setMaxDequeuedBufferCount(2);
- }
- mBufferItemConsumer =
- new BLASTBufferItemConsumer(mConsumer, GraphicBuffer::USAGE_HW_COMPOSER, 1, true);
+ // safe default, most producers are expected to override this
+ mProducer->setMaxDequeuedBufferCount(2);
+ mBufferItemConsumer = new BLASTBufferItemConsumer(mConsumer,
+ GraphicBuffer::USAGE_HW_COMPOSER |
+ GraphicBuffer::USAGE_HW_TEXTURE,
+ 1, false);
static int32_t id = 0;
- auto name = std::string("BLAST Consumer") + std::to_string(id);
+ auto consumerName = mName + "(BLAST Consumer)" + std::to_string(id);
id++;
- mBufferItemConsumer->setName(String8(name.c_str()));
+ mBufferItemConsumer->setName(String8(consumerName.c_str()));
mBufferItemConsumer->setFrameAvailableListener(this);
mBufferItemConsumer->setBufferFreedListener(this);
- mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight);
- mBufferItemConsumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888);
+ mBufferItemConsumer->setDefaultBufferSize(mSize.width, mSize.height);
+ mBufferItemConsumer->setDefaultBufferFormat(convertBufferFormat(format));
+ mBufferItemConsumer->setBlastBufferQueue(this);
mTransformHint = mSurfaceControl->getTransformHint();
mBufferItemConsumer->setTransformHint(mTransformHint);
+ SurfaceComposerClient::Transaction()
+ .setFlags(surface, layer_state_t::eEnableBackpressure,
+ layer_state_t::eEnableBackpressure)
+ .apply();
mNumAcquired = 0;
mNumFrameAvailable = 0;
- mPendingReleaseItem.item = BufferItem();
- mPendingReleaseItem.releaseFence = nullptr;
}
-void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, int width, int height) {
- std::unique_lock _lock{mMutex};
- mSurfaceControl = surface;
+BLASTBufferQueue::~BLASTBufferQueue() {
+ mBufferItemConsumer->setBlastBufferQueue(nullptr);
+ if (mPendingTransactions.empty()) {
+ return;
+ }
+ BQA_LOGE("Applying pending transactions on dtor %d",
+ static_cast<uint32_t>(mPendingTransactions.size()));
+ SurfaceComposerClient::Transaction t;
+ for (auto& [targetFrameNumber, transaction] : mPendingTransactions) {
+ t.merge(std::move(transaction));
+ }
+ t.apply();
+}
- if (mWidth != width || mHeight != height) {
- mWidth = width;
- mHeight = height;
- mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight);
+void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height,
+ int32_t format) {
+ std::unique_lock _lock{mMutex};
+ BQA_LOGV("update width=%d height=%d format=%d", width, height, format);
+ if (mFormat != format) {
+ mFormat = format;
+ mBufferItemConsumer->setDefaultBufferFormat(convertBufferFormat(format));
+ }
+
+ SurfaceComposerClient::Transaction t;
+ bool applyTransaction = false;
+ if (!SurfaceControl::isSameSurface(mSurfaceControl, surface)) {
+ mSurfaceControl = surface;
+ t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure,
+ layer_state_t::eEnableBackpressure);
+ applyTransaction = true;
+ }
+
+ ui::Size newSize(width, height);
+ if (mRequestedSize != newSize) {
+ mRequestedSize.set(newSize);
+ mBufferItemConsumer->setDefaultBufferSize(mRequestedSize.width, mRequestedSize.height);
+ if (mLastBufferInfo.scalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE) {
+ // If the buffer supports scaling, update the frame immediately since the client may
+ // want to scale the existing buffer to the new size.
+ mSize = mRequestedSize;
+ // We only need to update the scale if we've received at least one buffer. The reason
+ // for this is the scale is calculated based on the requested size and buffer size.
+ // If there's no buffer, the scale will always be 1.
+ if (mLastBufferInfo.hasBuffer) {
+ setMatrix(&t, mLastBufferInfo);
+ }
+ applyTransaction = true;
+ }
+ }
+ if (applyTransaction) {
+ t.apply();
}
}
@@ -144,63 +229,119 @@
if (context == nullptr) {
return;
}
- BLASTBufferQueue* bq = static_cast<BLASTBufferQueue*>(context);
+ sp<BLASTBufferQueue> bq = static_cast<BLASTBufferQueue*>(context);
bq->transactionCallback(latchTime, presentFence, stats);
}
void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence>& /*presentFence*/,
const std::vector<SurfaceControlStats>& stats) {
- std::unique_lock _lock{mMutex};
- ATRACE_CALL();
+ std::function<void(int64_t)> transactionCompleteCallback = nullptr;
+ uint64_t currFrameNumber = 0;
- if (!stats.empty()) {
- mTransformHint = stats[0].transformHint;
- mBufferItemConsumer->setTransformHint(mTransformHint);
- mBufferItemConsumer->updateFrameTimestamps(stats[0].frameEventStats.frameNumber,
- stats[0].frameEventStats.refreshStartTime,
- stats[0].frameEventStats.gpuCompositionDoneFence,
- stats[0].presentFence,
- stats[0].previousReleaseFence,
- stats[0].frameEventStats.compositorTiming,
- stats[0].latchTime,
- stats[0].frameEventStats.dequeueReadyTime);
- }
- if (mPendingReleaseItem.item.mGraphicBuffer != nullptr) {
- if (!stats.empty()) {
- mPendingReleaseItem.releaseFence = stats[0].previousReleaseFence;
+ {
+ std::unique_lock _lock{mMutex};
+ ATRACE_CALL();
+ BQA_LOGV("transactionCallback");
+
+ if (!mSurfaceControlsWithPendingCallback.empty()) {
+ sp<SurfaceControl> pendingSC = mSurfaceControlsWithPendingCallback.front();
+ mSurfaceControlsWithPendingCallback.pop();
+ bool found = false;
+ for (auto stat : stats) {
+ if (!SurfaceControl::isSameSurface(pendingSC, stat.surfaceControl)) {
+ continue;
+ }
+
+ mTransformHint = stat.transformHint;
+ mBufferItemConsumer->setTransformHint(mTransformHint);
+ mBufferItemConsumer
+ ->updateFrameTimestamps(stat.frameEventStats.frameNumber,
+ stat.frameEventStats.refreshStartTime,
+ stat.frameEventStats.gpuCompositionDoneFence,
+ stat.presentFence, stat.previousReleaseFence,
+ stat.frameEventStats.compositorTiming,
+ stat.latchTime,
+ stat.frameEventStats.dequeueReadyTime);
+
+ currFrameNumber = stat.frameEventStats.frameNumber;
+
+ if (mTransactionCompleteCallback &&
+ currFrameNumber >= mTransactionCompleteFrameNumber) {
+ if (currFrameNumber > mTransactionCompleteFrameNumber) {
+ BQA_LOGE("transactionCallback received for a newer framenumber=%" PRIu64
+ " than expected=%" PRIu64,
+ currFrameNumber, mTransactionCompleteFrameNumber);
+ }
+ transactionCompleteCallback = std::move(mTransactionCompleteCallback);
+ mTransactionCompleteFrameNumber = 0;
+ }
+
+ found = true;
+ break;
+ }
+
+ if (!found) {
+ BQA_LOGE("Failed to find matching SurfaceControl in transaction callback");
+ }
} else {
- ALOGE("Warning: no SurfaceControlStats returned in BLASTBufferQueue callback");
- mPendingReleaseItem.releaseFence = nullptr;
+ BQA_LOGE("No matching SurfaceControls found: mSurfaceControlsWithPendingCallback was "
+ "empty.");
}
- mBufferItemConsumer->releaseBuffer(mPendingReleaseItem.item,
- mPendingReleaseItem.releaseFence
- ? mPendingReleaseItem.releaseFence
- : Fence::NO_FENCE);
- mNumAcquired--;
- mPendingReleaseItem.item = BufferItem();
- mPendingReleaseItem.releaseFence = nullptr;
+
+ decStrong((void*)transactionCallbackThunk);
}
- if (mSubmitted.empty()) {
- ALOGE("ERROR: callback with no corresponding submitted buffer item");
+ if (transactionCompleteCallback) {
+ transactionCompleteCallback(currFrameNumber);
}
- mPendingReleaseItem.item = std::move(mSubmitted.front());
- mSubmitted.pop();
+}
- processNextBufferLocked(false);
+// Unlike transactionCallbackThunk the release buffer callback does not extend the life of the
+// BBQ. This is because if the BBQ is destroyed, then the buffers will be released by the client.
+// So we pass in a weak pointer to the BBQ and if it still alive, then we release the buffer.
+// Otherwise, this is a no-op.
+static void releaseBufferCallbackThunk(wp<BLASTBufferQueue> context, uint64_t graphicBufferId,
+ const sp<Fence>& releaseFence) {
+ sp<BLASTBufferQueue> blastBufferQueue = context.promote();
+ ALOGV("releaseBufferCallbackThunk graphicBufferId=%" PRIu64 " blastBufferQueue=%s",
+ graphicBufferId, blastBufferQueue ? "alive" : "dead");
+ if (blastBufferQueue) {
+ blastBufferQueue->releaseBufferCallback(graphicBufferId, releaseFence);
+ }
+}
+void BLASTBufferQueue::releaseBufferCallback(uint64_t graphicBufferId,
+ const sp<Fence>& releaseFence) {
+ ATRACE_CALL();
+ std::unique_lock _lock{mMutex};
+ BQA_LOGV("releaseBufferCallback graphicBufferId=%" PRIu64, graphicBufferId);
+
+ auto it = mSubmitted.find(graphicBufferId);
+ if (it == mSubmitted.end()) {
+ BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %" PRIu64,
+ graphicBufferId);
+ return;
+ }
+
+ mBufferItemConsumer->releaseBuffer(it->second, releaseFence);
+ mSubmitted.erase(it);
+ mNumAcquired--;
+ processNextBufferLocked(false /* useNextTransaction */);
mCallbackCV.notify_all();
- decStrong((void*)transactionCallbackThunk);
}
void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) {
ATRACE_CALL();
- if (mNumFrameAvailable == 0 || mNumAcquired == MAX_ACQUIRED_BUFFERS + 1) {
+ // If the next transaction is set, we want to guarantee the our acquire will not fail, so don't
+ // include the extra buffer when checking if we can acquire the next buffer.
+ const bool includeExtraAcquire = !useNextTransaction;
+ if (mNumFrameAvailable == 0 || maxBuffersAcquired(includeExtraAcquire)) {
+ mCallbackCV.notify_all();
return;
}
if (mSurfaceControl == nullptr) {
- ALOGE("ERROR : surface control is null");
+ BQA_LOGE("ERROR : surface control is null");
return;
}
@@ -215,8 +356,13 @@
BufferItem bufferItem;
- status_t status = mBufferItemConsumer->acquireBuffer(&bufferItem, -1, false);
- if (status != OK) {
+ status_t status =
+ mBufferItemConsumer->acquireBuffer(&bufferItem, 0 /* expectedPresent */, false);
+ if (status == BufferQueue::NO_BUFFER_AVAILABLE) {
+ BQA_LOGV("Failed to acquire a buffer, err=NO_BUFFER_AVAILABLE");
+ return;
+ } else if (status != OK) {
+ BQA_LOGE("Failed to acquire a buffer, err=%s", statusToString(status).c_str());
return;
}
auto buffer = bufferItem.mGraphicBuffer;
@@ -224,11 +370,22 @@
if (buffer == nullptr) {
mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE);
+ BQA_LOGE("Buffer was empty");
+ return;
+ }
+
+ if (rejectBuffer(bufferItem)) {
+ BQA_LOGE("rejecting buffer:active_size=%dx%d, requested_size=%dx%d"
+ "buffer{size=%dx%d transform=%d}",
+ mSize.width, mSize.height, mRequestedSize.width, mRequestedSize.height,
+ buffer->getWidth(), buffer->getHeight(), bufferItem.mTransform);
+ mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE);
+ processNextBufferLocked(useNextTransaction);
return;
}
mNumAcquired++;
- mSubmitted.push(bufferItem);
+ mSubmitted[buffer->getId()] = bufferItem;
bool needsDisconnect = false;
mBufferItemConsumer->getConnectionEvents(bufferItem.mFrameNumber, &needsDisconnect);
@@ -241,46 +398,383 @@
// Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback.
incStrong((void*)transactionCallbackThunk);
- t->setBuffer(mSurfaceControl, buffer);
+ Rect crop = computeCrop(bufferItem);
+ mLastAcquiredFrameNumber = bufferItem.mFrameNumber;
+ mLastBufferInfo.update(true /* hasBuffer */, bufferItem.mGraphicBuffer->getWidth(),
+ bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform,
+ bufferItem.mScalingMode, crop);
+
+ auto releaseBufferCallback =
+ std::bind(releaseBufferCallbackThunk, wp<BLASTBufferQueue>(this) /* callbackContext */,
+ std::placeholders::_1, std::placeholders::_2);
+ t->setBuffer(mSurfaceControl, buffer, releaseBufferCallback);
+ t->setDataspace(mSurfaceControl, static_cast<ui::Dataspace>(bufferItem.mDataSpace));
+ t->setHdrMetadata(mSurfaceControl, bufferItem.mHdrMetadata);
+ t->setSurfaceDamageRegion(mSurfaceControl, bufferItem.mSurfaceDamage);
t->setAcquireFence(mSurfaceControl,
bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE);
t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
+ mSurfaceControlsWithPendingCallback.push(mSurfaceControl);
- t->setFrame(mSurfaceControl, {0, 0, mWidth, mHeight});
- t->setCrop(mSurfaceControl, computeCrop(bufferItem));
+ setMatrix(t, mLastBufferInfo);
+ t->setCrop(mSurfaceControl, crop);
t->setTransform(mSurfaceControl, bufferItem.mTransform);
t->setTransformToDisplayInverse(mSurfaceControl, bufferItem.mTransformToDisplayInverse);
- t->setDesiredPresentTime(bufferItem.mTimestamp);
+ if (!bufferItem.mIsAutoTimestamp) {
+ t->setDesiredPresentTime(bufferItem.mTimestamp);
+ }
+ t->setFrameNumber(mSurfaceControl, bufferItem.mFrameNumber);
+
+ if (!mNextFrameTimelineInfoQueue.empty()) {
+ t->setFrameTimelineInfo(mNextFrameTimelineInfoQueue.front());
+ mNextFrameTimelineInfoQueue.pop();
+ }
+
+ if (mAutoRefresh != bufferItem.mAutoRefresh) {
+ t->setAutoRefresh(mSurfaceControl, bufferItem.mAutoRefresh);
+ mAutoRefresh = bufferItem.mAutoRefresh;
+ }
+ {
+ std::unique_lock _lock{mTimestampMutex};
+ auto dequeueTime = mDequeueTimestamps.find(buffer->getId());
+ if (dequeueTime != mDequeueTimestamps.end()) {
+ Parcel p;
+ p.writeInt64(dequeueTime->second);
+ t->setMetadata(mSurfaceControl, METADATA_DEQUEUE_TIME, p);
+ mDequeueTimestamps.erase(dequeueTime);
+ }
+ }
+
+ auto mergeTransaction =
+ [&t, currentFrameNumber = bufferItem.mFrameNumber](
+ std::tuple<uint64_t, SurfaceComposerClient::Transaction> pendingTransaction) {
+ auto& [targetFrameNumber, transaction] = pendingTransaction;
+ if (currentFrameNumber < targetFrameNumber) {
+ return false;
+ }
+ t->merge(std::move(transaction));
+ return true;
+ };
+
+ mPendingTransactions.erase(std::remove_if(mPendingTransactions.begin(),
+ mPendingTransactions.end(), mergeTransaction),
+ mPendingTransactions.end());
if (applyTransaction) {
- t->apply();
+ t->setApplyToken(mApplyToken).apply();
}
+
+ BQA_LOGV("processNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64
+ " applyTransaction=%s mTimestamp=%" PRId64 "%s mPendingTransactions.size=%d"
+ " graphicBufferId=%" PRIu64,
+ mSize.width, mSize.height, bufferItem.mFrameNumber, toString(applyTransaction),
+ bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp ? "(auto)" : "",
+ static_cast<uint32_t>(mPendingTransactions.size()),
+ bufferItem.mGraphicBuffer->getId());
}
Rect BLASTBufferQueue::computeCrop(const BufferItem& item) {
if (item.mScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) {
- return GLConsumer::scaleDownCrop(item.mCrop, mWidth, mHeight);
+ return GLConsumer::scaleDownCrop(item.mCrop, mSize.width, mSize.height);
}
return item.mCrop;
}
-void BLASTBufferQueue::onFrameAvailable(const BufferItem& /*item*/) {
+void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
ATRACE_CALL();
std::unique_lock _lock{mMutex};
- if (mNextTransaction != nullptr) {
- while (mNumFrameAvailable > 0 || mNumAcquired == MAX_ACQUIRED_BUFFERS + 1) {
+ const bool nextTransactionSet = mNextTransaction != nullptr;
+ if (nextTransactionSet) {
+ while (mNumFrameAvailable > 0 || maxBuffersAcquired(false /* includeExtraAcquire */)) {
+ BQA_LOGV("waiting in onFrameAvailable...");
mCallbackCV.wait(_lock);
}
}
// add to shadow queue
mNumFrameAvailable++;
- processNextBufferLocked(true);
+
+ BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " nextTransactionSet=%s", item.mFrameNumber,
+ toString(nextTransactionSet));
+ processNextBufferLocked(nextTransactionSet /* useNextTransaction */);
}
+void BLASTBufferQueue::onFrameReplaced(const BufferItem& item) {
+ BQA_LOGV("onFrameReplaced framenumber=%" PRIu64, item.mFrameNumber);
+ // Do nothing since we are not storing unacquired buffer items locally.
+}
+
+void BLASTBufferQueue::onFrameDequeued(const uint64_t bufferId) {
+ std::unique_lock _lock{mTimestampMutex};
+ mDequeueTimestamps[bufferId] = systemTime();
+};
+
+void BLASTBufferQueue::onFrameCancelled(const uint64_t bufferId) {
+ std::unique_lock _lock{mTimestampMutex};
+ mDequeueTimestamps.erase(bufferId);
+};
+
void BLASTBufferQueue::setNextTransaction(SurfaceComposerClient::Transaction* t) {
std::lock_guard _lock{mMutex};
mNextTransaction = t;
}
+bool BLASTBufferQueue::rejectBuffer(const BufferItem& item) {
+ if (item.mScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE) {
+ mSize = mRequestedSize;
+ // Only reject buffers if scaling mode is freeze.
+ return false;
+ }
+
+ uint32_t bufWidth = item.mGraphicBuffer->getWidth();
+ uint32_t bufHeight = item.mGraphicBuffer->getHeight();
+
+ // Take the buffer's orientation into account
+ if (item.mTransform & ui::Transform::ROT_90) {
+ std::swap(bufWidth, bufHeight);
+ }
+ ui::Size bufferSize(bufWidth, bufHeight);
+ if (mRequestedSize != mSize && mRequestedSize == bufferSize) {
+ mSize = mRequestedSize;
+ return false;
+ }
+
+ // reject buffers if the buffer size doesn't match.
+ return mSize != bufferSize;
+}
+
+void BLASTBufferQueue::setMatrix(SurfaceComposerClient::Transaction* t,
+ const BufferInfo& bufferInfo) {
+ uint32_t bufWidth = bufferInfo.crop.getWidth();
+ uint32_t bufHeight = bufferInfo.crop.getHeight();
+
+ float sx = mSize.width / static_cast<float>(bufWidth);
+ float sy = mSize.height / static_cast<float>(bufHeight);
+
+ t->setMatrix(mSurfaceControl, sx, 0, 0, sy);
+ // Update position based on crop.
+ t->setPosition(mSurfaceControl, bufferInfo.crop.left * sx * -1, bufferInfo.crop.top * sy * -1);
+}
+
+void BLASTBufferQueue::setTransactionCompleteCallback(
+ uint64_t frameNumber, std::function<void(int64_t)>&& transactionCompleteCallback) {
+ std::lock_guard _lock{mMutex};
+ if (transactionCompleteCallback == nullptr) {
+ mTransactionCompleteCallback = nullptr;
+ } else {
+ mTransactionCompleteCallback = std::move(transactionCompleteCallback);
+ mTransactionCompleteFrameNumber = frameNumber;
+ }
+}
+
+// Check if we have acquired the maximum number of buffers.
+// Consumer can acquire an additional buffer if that buffer is not droppable. Set
+// includeExtraAcquire is true to include this buffer to the count. Since this depends on the state
+// of the buffer, the next acquire may return with NO_BUFFER_AVAILABLE.
+bool BLASTBufferQueue::maxBuffersAcquired(bool includeExtraAcquire) const {
+ int maxAcquiredBuffers = MAX_ACQUIRED_BUFFERS + (includeExtraAcquire ? 2 : 1);
+ return mNumAcquired == maxAcquiredBuffers;
+}
+
+class BBQSurface : public Surface {
+private:
+ sp<BLASTBufferQueue> mBbq;
+public:
+ BBQSurface(const sp<IGraphicBufferProducer>& igbp, bool controlledByApp,
+ const sp<IBinder>& scHandle, const sp<BLASTBufferQueue>& bbq)
+ : Surface(igbp, controlledByApp, scHandle), mBbq(bbq) {}
+
+ void allocateBuffers() override {
+ uint32_t reqWidth = mReqWidth ? mReqWidth : mUserWidth;
+ uint32_t reqHeight = mReqHeight ? mReqHeight : mUserHeight;
+ auto gbp = getIGraphicBufferProducer();
+ std::thread ([reqWidth, reqHeight, gbp=getIGraphicBufferProducer(),
+ reqFormat=mReqFormat, reqUsage=mReqUsage] () {
+ gbp->allocateBuffers(reqWidth, reqHeight,
+ reqFormat, reqUsage);
+
+ }).detach();
+ }
+
+ status_t setFrameRate(float frameRate, int8_t compatibility,
+ int8_t changeFrameRateStrategy) override {
+ if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy,
+ "BBQSurface::setFrameRate")) {
+ return BAD_VALUE;
+ }
+ return mBbq->setFrameRate(frameRate, compatibility, changeFrameRateStrategy);
+ }
+
+ status_t setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) override {
+ return mBbq->setFrameTimelineInfo(frameTimelineInfo);
+ }
+};
+
+// TODO: Can we coalesce this with frame updates? Need to confirm
+// no timing issues.
+status_t BLASTBufferQueue::setFrameRate(float frameRate, int8_t compatibility,
+ bool shouldBeSeamless) {
+ std::unique_lock _lock{mMutex};
+ SurfaceComposerClient::Transaction t;
+
+ return t.setFrameRate(mSurfaceControl, frameRate, compatibility, shouldBeSeamless).apply();
+}
+
+status_t BLASTBufferQueue::setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) {
+ std::unique_lock _lock{mMutex};
+ mNextFrameTimelineInfoQueue.push(frameTimelineInfo);
+ return OK;
+}
+
+void BLASTBufferQueue::setSidebandStream(const sp<NativeHandle>& stream) {
+ std::unique_lock _lock{mMutex};
+ SurfaceComposerClient::Transaction t;
+
+ t.setSidebandStream(mSurfaceControl, stream).apply();
+}
+
+sp<Surface> BLASTBufferQueue::getSurface(bool includeSurfaceControlHandle) {
+ std::unique_lock _lock{mMutex};
+ sp<IBinder> scHandle = nullptr;
+ if (includeSurfaceControlHandle && mSurfaceControl) {
+ scHandle = mSurfaceControl->getHandle();
+ }
+ return new BBQSurface(mProducer, true, scHandle, this);
+}
+
+void BLASTBufferQueue::mergeWithNextTransaction(SurfaceComposerClient::Transaction* t,
+ uint64_t frameNumber) {
+ std::lock_guard _lock{mMutex};
+ if (mLastAcquiredFrameNumber >= frameNumber) {
+ // Apply the transaction since we have already acquired the desired frame.
+ t->apply();
+ } else {
+ mPendingTransactions.emplace_back(frameNumber, *t);
+ // Clear the transaction so it can't be applied elsewhere.
+ t->clear();
+ }
+}
+
+// Maintains a single worker thread per process that services a list of runnables.
+class AsyncWorker : public Singleton<AsyncWorker> {
+private:
+ std::thread mThread;
+ bool mDone = false;
+ std::deque<std::function<void()>> mRunnables;
+ std::mutex mMutex;
+ std::condition_variable mCv;
+ void run() {
+ std::unique_lock<std::mutex> lock(mMutex);
+ while (!mDone) {
+ mCv.wait(lock);
+ while (!mRunnables.empty()) {
+ std::function<void()> runnable = mRunnables.front();
+ mRunnables.pop_front();
+ runnable();
+ }
+ }
+ }
+
+public:
+ AsyncWorker() : Singleton<AsyncWorker>() { mThread = std::thread(&AsyncWorker::run, this); }
+
+ ~AsyncWorker() {
+ mDone = true;
+ mCv.notify_all();
+ if (mThread.joinable()) {
+ mThread.join();
+ }
+ }
+
+ void post(std::function<void()> runnable) {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mRunnables.emplace_back(std::move(runnable));
+ mCv.notify_one();
+ }
+};
+ANDROID_SINGLETON_STATIC_INSTANCE(AsyncWorker);
+
+// Asynchronously calls ProducerListener functions so we can emulate one way binder calls.
+class AsyncProducerListener : public BnProducerListener {
+private:
+ const sp<IProducerListener> mListener;
+
+public:
+ AsyncProducerListener(const sp<IProducerListener>& listener) : mListener(listener) {}
+
+ void onBufferReleased() override {
+ AsyncWorker::getInstance().post([listener = mListener]() { listener->onBufferReleased(); });
+ }
+
+ void onBuffersDiscarded(const std::vector<int32_t>& slots) override {
+ AsyncWorker::getInstance().post(
+ [listener = mListener, slots = slots]() { listener->onBuffersDiscarded(slots); });
+ }
+};
+
+// Extends the BufferQueueProducer to create a wrapper around the listener so the listener calls
+// can be non-blocking when the producer is in the client process.
+class BBQBufferQueueProducer : public BufferQueueProducer {
+public:
+ BBQBufferQueueProducer(const sp<BufferQueueCore>& core)
+ : BufferQueueProducer(core, false /* consumerIsSurfaceFlinger*/) {}
+
+ status_t connect(const sp<IProducerListener>& listener, int api, bool producerControlledByApp,
+ QueueBufferOutput* output) override {
+ if (!listener) {
+ return BufferQueueProducer::connect(listener, api, producerControlledByApp, output);
+ }
+
+ return BufferQueueProducer::connect(new AsyncProducerListener(listener), api,
+ producerControlledByApp, output);
+ }
+
+ int query(int what, int* value) override {
+ if (what == NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER) {
+ *value = 1;
+ return NO_ERROR;
+ }
+ return BufferQueueProducer::query(what, value);
+ }
+};
+
+// Similar to BufferQueue::createBufferQueue but creates an adapter specific bufferqueue producer.
+// This BQP allows invoking client specified ProducerListeners and invoke them asynchronously,
+// emulating one way binder call behavior. Without this, if the listener calls back into the queue,
+// we can deadlock.
+void BLASTBufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
+ sp<IGraphicBufferConsumer>* outConsumer) {
+ LOG_ALWAYS_FATAL_IF(outProducer == nullptr, "BLASTBufferQueue: outProducer must not be NULL");
+ LOG_ALWAYS_FATAL_IF(outConsumer == nullptr, "BLASTBufferQueue: outConsumer must not be NULL");
+
+ sp<BufferQueueCore> core(new BufferQueueCore());
+ LOG_ALWAYS_FATAL_IF(core == nullptr, "BLASTBufferQueue: failed to create BufferQueueCore");
+
+ sp<IGraphicBufferProducer> producer(new BBQBufferQueueProducer(core));
+ LOG_ALWAYS_FATAL_IF(producer == nullptr,
+ "BLASTBufferQueue: failed to create BBQBufferQueueProducer");
+
+ sp<BufferQueueConsumer> consumer(new BufferQueueConsumer(core));
+ consumer->setAllowExtraAcquire(true);
+ LOG_ALWAYS_FATAL_IF(consumer == nullptr,
+ "BLASTBufferQueue: failed to create BufferQueueConsumer");
+
+ *outProducer = producer;
+ *outConsumer = consumer;
+}
+
+PixelFormat BLASTBufferQueue::convertBufferFormat(PixelFormat& format) {
+ PixelFormat convertedFormat = format;
+ switch (format) {
+ case PIXEL_FORMAT_TRANSPARENT:
+ case PIXEL_FORMAT_TRANSLUCENT:
+ convertedFormat = PIXEL_FORMAT_RGBA_8888;
+ break;
+ case PIXEL_FORMAT_OPAQUE:
+ convertedFormat = PIXEL_FORMAT_RGBX_8888;
+ break;
+ }
+ return convertedFormat;
+}
+
} // namespace android
diff --git a/libs/gui/BatchBufferOps.cpp b/libs/gui/BatchBufferOps.cpp
new file mode 100644
index 0000000..60aceb1
--- /dev/null
+++ b/libs/gui/BatchBufferOps.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2021 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>
+
+#define LOG_TAG "IGBPBatchOps"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+//#define LOG_NDEBUG 0
+
+#include <gui/IGraphicBufferProducer.h>
+
+namespace android {
+
+/**
+ * Default implementation of batched buffer operations. These default
+ * implementations call into the non-batched version of the same operation.
+ */
+
+status_t IGraphicBufferProducer::requestBuffers(
+ const std::vector<int32_t>& slots,
+ std::vector<RequestBufferOutput>* outputs) {
+ outputs->clear();
+ outputs->reserve(slots.size());
+ for (int32_t slot : slots) {
+ RequestBufferOutput& output = outputs->emplace_back();
+ output.result = requestBuffer(static_cast<int>(slot),
+ &output.buffer);
+ }
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::dequeueBuffers(
+ const std::vector<DequeueBufferInput>& inputs,
+ std::vector<DequeueBufferOutput>* outputs) {
+ outputs->clear();
+ outputs->reserve(inputs.size());
+ for (const DequeueBufferInput& input : inputs) {
+ DequeueBufferOutput& output = outputs->emplace_back();
+ output.result = dequeueBuffer(
+ &output.slot,
+ &output.fence,
+ input.width,
+ input.height,
+ input.format,
+ input.usage,
+ &output.bufferAge,
+ input.getTimestamps ? &output.timestamps.emplace() : nullptr);
+ }
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::detachBuffers(
+ const std::vector<int32_t>& slots,
+ std::vector<status_t>* results) {
+ results->clear();
+ results->reserve(slots.size());
+ for (int32_t slot : slots) {
+ results->emplace_back(detachBuffer(slot));
+ }
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::attachBuffers(
+ const std::vector<sp<GraphicBuffer>>& buffers,
+ std::vector<AttachBufferOutput>* outputs) {
+ outputs->clear();
+ outputs->reserve(buffers.size());
+ for (const sp<GraphicBuffer>& buffer : buffers) {
+ AttachBufferOutput& output = outputs->emplace_back();
+ output.result = attachBuffer(&output.slot, buffer);
+ }
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::queueBuffers(
+ const std::vector<QueueBufferInput>& inputs,
+ std::vector<QueueBufferOutput>* outputs) {
+ outputs->clear();
+ outputs->reserve(inputs.size());
+ for (const QueueBufferInput& input : inputs) {
+ QueueBufferOutput& output = outputs->emplace_back();
+ output.result = queueBuffer(input.slot, input, &output);
+ }
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::cancelBuffers(
+ const std::vector<CancelBufferInput>& inputs,
+ std::vector<status_t>* results) {
+ results->clear();
+ results->reserve(inputs.size());
+ for (const CancelBufferInput& input : inputs) {
+ results->emplace_back() = cancelBuffer(input.slot, input.fence);
+ }
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::query(const std::vector<int32_t> inputs,
+ std::vector<QueryOutput>* outputs) {
+ outputs->clear();
+ outputs->reserve(inputs.size());
+ for (int32_t input : inputs) {
+ QueryOutput& output = outputs->emplace_back();
+ int value{};
+ output.result = static_cast<status_t>(
+ query(static_cast<int>(input), &value));
+ output.value = static_cast<int64_t>(value);
+ }
+ return NO_ERROR;
+}
+
+} // namespace android
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index da6143c..7f7a043 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -94,7 +94,10 @@
++numAcquiredBuffers;
}
}
- if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) {
+ const bool acquireNonDroppableBuffer = mCore->mAllowExtraAcquire &&
+ numAcquiredBuffers == mCore->mMaxAcquiredBufferCount + 1;
+ if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1 &&
+ !acquireNonDroppableBuffer) {
BQ_LOGE("acquireBuffer: max acquired buffer count reached: %d (max %d)",
numAcquiredBuffers, mCore->mMaxAcquiredBufferCount);
return INVALID_OPERATION;
@@ -254,6 +257,9 @@
outBuffer->mIsStale = false;
outBuffer->mAutoRefresh = mCore->mSharedBufferMode &&
mCore->mAutoRefresh;
+ } else if (acquireNonDroppableBuffer && front->mIsDroppable) {
+ BQ_LOGV("acquireBuffer: front buffer is not droppable");
+ return NO_BUFFER_AVAILABLE;
} else {
slot = front->mSlot;
*outBuffer = *front;
@@ -824,4 +830,9 @@
return NO_ERROR;
}
+void BufferQueueConsumer::setAllowExtraAcquire(bool allow) {
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
+ mCore->mAllowExtraAcquire = allow;
+}
+
} // namespace android
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index 682fe91..e1b1efc 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -33,10 +33,10 @@
// using just a few large reads.
static const size_t EVENT_BUFFER_SIZE = 100;
-DisplayEventDispatcher::DisplayEventDispatcher(const sp<Looper>& looper,
- ISurfaceComposer::VsyncSource vsyncSource,
- ISurfaceComposer::ConfigChanged configChanged)
- : mLooper(looper), mReceiver(vsyncSource, configChanged), mWaitingForVsync(false) {
+DisplayEventDispatcher::DisplayEventDispatcher(
+ const sp<Looper>& looper, ISurfaceComposer::VsyncSource vsyncSource,
+ ISurfaceComposer::EventRegistrationFlags eventRegistration)
+ : mLooper(looper), mReceiver(vsyncSource, eventRegistration), mWaitingForVsync(false) {
ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this);
}
@@ -73,7 +73,8 @@
nsecs_t vsyncTimestamp;
PhysicalDisplayId vsyncDisplayId;
uint32_t vsyncCount;
- if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
+ VsyncEventData vsyncEventData;
+ if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount, &vsyncEventData)) {
ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "", this,
ns2ms(static_cast<nsecs_t>(vsyncTimestamp)));
}
@@ -116,12 +117,14 @@
nsecs_t vsyncTimestamp;
PhysicalDisplayId vsyncDisplayId;
uint32_t vsyncCount;
- if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
+ VsyncEventData vsyncEventData;
+ if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount, &vsyncEventData)) {
ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64
- ", displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%d",
- this, ns2ms(vsyncTimestamp), vsyncDisplayId, vsyncCount);
+ ", displayId=%s, count=%d, vsyncId=%" PRId64,
+ this, ns2ms(vsyncTimestamp), to_string(vsyncDisplayId).c_str(), vsyncCount,
+ vsyncEventData.id);
mWaitingForVsync = false;
- dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
+ dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount, vsyncEventData);
}
return 1; // keep the callback
@@ -129,12 +132,14 @@
bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp,
PhysicalDisplayId* outDisplayId,
- uint32_t* outCount) {
+ uint32_t* outCount,
+ VsyncEventData* outVsyncEventData) {
bool gotVsync = false;
DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
ssize_t n;
while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
ALOGV("dispatcher %p ~ Read %d events.", this, int(n));
+ mFrameRateOverrides.reserve(n);
for (ssize_t i = 0; i < n; i++) {
const DisplayEventReceiver::Event& ev = buf[i];
switch (ev.header.type) {
@@ -145,17 +150,27 @@
*outTimestamp = ev.header.timestamp;
*outDisplayId = ev.header.displayId;
*outCount = ev.vsync.count;
+ outVsyncEventData->id = ev.vsync.vsyncId;
+ outVsyncEventData->deadlineTimestamp = ev.vsync.deadlineTimestamp;
+ outVsyncEventData->frameInterval = ev.vsync.frameInterval;
break;
case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected);
break;
- case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
- dispatchConfigChanged(ev.header.timestamp, ev.header.displayId,
- ev.config.configId, ev.config.vsyncPeriod);
+ case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE:
+ dispatchModeChanged(ev.header.timestamp, ev.header.displayId,
+ ev.modeChange.modeId, ev.modeChange.vsyncPeriod);
break;
case DisplayEventReceiver::DISPLAY_EVENT_NULL:
dispatchNullEvent(ev.header.timestamp, ev.header.displayId);
break;
+ case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE:
+ mFrameRateOverrides.emplace_back(ev.frameRateOverride);
+ break;
+ case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH:
+ dispatchFrameRateOverrides(ev.header.timestamp, ev.header.displayId,
+ std::move(mFrameRateOverrides));
+ break;
default:
ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type);
break;
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index f2b0962..03b33c7 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -32,11 +32,12 @@
// ---------------------------------------------------------------------------
-DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource,
- ISurfaceComposer::ConfigChanged configChanged) {
+DisplayEventReceiver::DisplayEventReceiver(
+ ISurfaceComposer::VsyncSource vsyncSource,
+ ISurfaceComposer::EventRegistrationFlags eventRegistration) {
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
if (sf != nullptr) {
- mEventConnection = sf->createDisplayEventConnection(vsyncSource, configChanged);
+ mEventConnection = sf->createDisplayEventConnection(vsyncSource, eventRegistration);
if (mEventConnection != nullptr) {
mDataChannel = std::make_unique<gui::BitTube>();
mEventConnection->stealReceiveChannel(mDataChannel.get());
diff --git a/libs/gui/FrameTimelineInfo.cpp b/libs/gui/FrameTimelineInfo.cpp
new file mode 100644
index 0000000..9231a57
--- /dev/null
+++ b/libs/gui/FrameTimelineInfo.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#define LOG_TAG "FrameTimelineInfo"
+
+#include <inttypes.h>
+
+#include <android/os/IInputConstants.h>
+#include <gui/FrameTimelineInfo.h>
+#include <gui/LayerState.h>
+#include <private/gui/ParcelUtils.h>
+#include <utils/Errors.h>
+
+#include <cmath>
+
+using android::os::IInputConstants;
+
+namespace android {
+
+status_t FrameTimelineInfo::write(Parcel& output) const {
+ SAFE_PARCEL(output.writeInt64, vsyncId);
+ SAFE_PARCEL(output.writeInt32, inputEventId);
+ return NO_ERROR;
+}
+
+status_t FrameTimelineInfo::read(const Parcel& input) {
+ SAFE_PARCEL(input.readInt64, &vsyncId);
+ SAFE_PARCEL(input.readInt32, &inputEventId);
+ return NO_ERROR;
+}
+
+void FrameTimelineInfo::merge(const FrameTimelineInfo& other) {
+ // When merging vsync Ids we take the oldest valid one
+ if (vsyncId != INVALID_VSYNC_ID && other.vsyncId != INVALID_VSYNC_ID) {
+ if (other.vsyncId > vsyncId) {
+ vsyncId = other.vsyncId;
+ inputEventId = other.inputEventId;
+ }
+ } else if (vsyncId == INVALID_VSYNC_ID) {
+ vsyncId = other.vsyncId;
+ inputEventId = other.inputEventId;
+ }
+}
+
+void FrameTimelineInfo::clear() {
+ vsyncId = INVALID_VSYNC_ID;
+ inputEventId = IInputConstants::INVALID_INPUT_EVENT_ID;
+}
+
+}; // namespace android
diff --git a/libs/gui/GuiConfig.cpp b/libs/gui/GuiConfig.cpp
deleted file mode 100644
index 3ec20ee..0000000
--- a/libs/gui/GuiConfig.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2012 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 <gui/GuiConfig.h>
-
-namespace android {
-
-void appendGuiConfigString(std::string& configStr) {
- static const char* config =
- " [libgui"
-#ifdef DONT_USE_FENCE_SYNC
- " DONT_USE_FENCE_SYNC"
-#endif
- "]";
- configStr.append(config);
-}
-
-}; // namespace android
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index ad00939..c1f9b85 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -74,6 +74,13 @@
GET_CONSUMER_USAGE,
SET_LEGACY_BUFFER_DROP,
SET_AUTO_PREROTATION,
+ REQUEST_BUFFERS,
+ DEQUEUE_BUFFERS,
+ DETACH_BUFFERS,
+ ATTACH_BUFFERS,
+ QUEUE_BUFFERS,
+ CANCEL_BUFFERS,
+ QUERY_MULTIPLE,
};
class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -90,7 +97,7 @@
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeInt32(bufferIdx);
- status_t result =remote()->transact(REQUEST_BUFFER, data, &reply);
+ status_t result = remote()->transact(REQUEST_BUFFER, data, &reply);
if (result != NO_ERROR) {
return result;
}
@@ -107,6 +114,27 @@
return result;
}
+ virtual status_t requestBuffers(
+ const std::vector<int32_t>& slots,
+ std::vector<RequestBufferOutput>* outputs) override {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeInt32Vector(slots);
+ status_t result = remote()->transact(REQUEST_BUFFERS, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.resizeOutVector(outputs);
+ for (RequestBufferOutput& output : *outputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.read(output);
+ }
+
+ return result;
+ }
+
virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) {
Parcel data, reply;
data.writeInterfaceToken(
@@ -183,6 +211,29 @@
return result;
}
+ virtual status_t dequeueBuffers(
+ const std::vector<DequeueBufferInput>& inputs,
+ std::vector<DequeueBufferOutput>* outputs) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeVectorSize(inputs);
+ for (const auto& input : inputs) {
+ data.write(input);
+ }
+ status_t result = remote()->transact(DEQUEUE_BUFFERS, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.resizeOutVector(outputs);
+ for (auto& output : *outputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.read(output);
+ }
+ return result;
+ }
+
virtual status_t detachBuffer(int slot) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
@@ -195,6 +246,19 @@
return result;
}
+ virtual status_t detachBuffers(const std::vector<int32_t>& slots,
+ std::vector<status_t>* results) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeInt32Vector(slots);
+ status_t result = remote()->transact(DETACH_BUFFERS, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.readInt32Vector(results);
+ return result;
+ }
+
virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence) {
if (outBuffer == nullptr) {
@@ -256,6 +320,39 @@
return result;
}
+ virtual status_t attachBuffers(
+ const std::vector<sp<GraphicBuffer>>& buffers,
+ std::vector<AttachBufferOutput>* outputs) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeVectorSize(buffers);
+ for (const sp<GraphicBuffer>& buffer : buffers) {
+ data.write(*buffer.get());
+ }
+ status_t result = remote()->transact(ATTACH_BUFFERS, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.resizeOutVector(outputs);
+ for (AttachBufferOutput& output : *outputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.read(output);
+ }
+ if (result == NO_ERROR) {
+ for (AttachBufferOutput& output : *outputs) {
+ if (output.result == NO_ERROR && output.slot < 0) {
+ ALOGE("attachBuffers returned invalid slot %d",
+ output.slot);
+ android_errorWriteLog(0x534e4554, "37478824");
+ output.result = UNKNOWN_ERROR;
+ }
+ }
+ }
+ return result;
+ }
+
virtual status_t queueBuffer(int buf,
const QueueBufferInput& input, QueueBufferOutput* output) {
Parcel data, reply;
@@ -278,6 +375,28 @@
return result;
}
+ virtual status_t queueBuffers(const std::vector<QueueBufferInput>& inputs,
+ std::vector<QueueBufferOutput>* outputs) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeVectorSize(inputs);
+ for (const QueueBufferInput& input : inputs) {
+ data.write(input);
+ }
+ status_t result = remote()->transact(QUEUE_BUFFERS, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.resizeOutVector(outputs);
+ for (QueueBufferOutput& output : *outputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.read(output);
+ }
+ return result;
+ }
+
virtual status_t cancelBuffer(int buf, const sp<Fence>& fence) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
@@ -291,6 +410,23 @@
return result;
}
+ virtual status_t cancelBuffers(
+ const std::vector<CancelBufferInput>& inputs,
+ std::vector<status_t>* results) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeVectorSize(inputs);
+ for (const CancelBufferInput& input : inputs) {
+ data.write(input);
+ }
+ status_t result = remote()->transact(CANCEL_BUFFERS, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.readInt32Vector(results);
+ return result;
+ }
+
virtual int query(int what, int* value) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
@@ -304,6 +440,25 @@
return result;
}
+ virtual status_t query(const std::vector<int32_t> inputs,
+ std::vector<QueryOutput>* outputs) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeInt32Vector(inputs);
+ status_t result = remote()->transact(QUERY_MULTIPLE, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.resizeOutVector(outputs);
+ for (QueryOutput& output : *outputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.read(output);
+ }
+ return result;
+ }
+
virtual status_t connect(const sp<IProducerListener>& listener,
int api, bool producerControlledByApp, QueueBufferOutput* output) {
Parcel data, reply;
@@ -576,6 +731,12 @@
return mBase->requestBuffer(slot, buf);
}
+ status_t requestBuffers(
+ const std::vector<int32_t>& slots,
+ std::vector<RequestBufferOutput>* outputs) override {
+ return mBase->requestBuffers(slots, outputs);
+ }
+
status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) override {
return mBase->setMaxDequeuedBufferCount(maxDequeuedBuffers);
}
@@ -590,10 +751,21 @@
return mBase->dequeueBuffer(slot, fence, w, h, format, usage, outBufferAge, outTimestamps);
}
+ status_t dequeueBuffers(
+ const std::vector<DequeueBufferInput>& inputs,
+ std::vector<DequeueBufferOutput>* outputs) override {
+ return mBase->dequeueBuffers(inputs, outputs);
+ }
+
status_t detachBuffer(int slot) override {
return mBase->detachBuffer(slot);
}
+ status_t detachBuffers(const std::vector<int32_t>& slots,
+ std::vector<status_t>* results) override {
+ return mBase->detachBuffers(slots, results);
+ }
+
status_t detachNextBuffer(
sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) override {
return mBase->detachNextBuffer(outBuffer, outFence);
@@ -604,6 +776,12 @@
return mBase->attachBuffer(outSlot, buffer);
}
+ status_t attachBuffers(
+ const std::vector<sp<GraphicBuffer>>& buffers,
+ std::vector<AttachBufferOutput>* outputs) override {
+ return mBase->attachBuffers(buffers, outputs);
+ }
+
status_t queueBuffer(
int slot,
const QueueBufferInput& input,
@@ -611,14 +789,30 @@
return mBase->queueBuffer(slot, input, output);
}
+ status_t queueBuffers(const std::vector<QueueBufferInput>& inputs,
+ std::vector<QueueBufferOutput>* outputs) override {
+ return mBase->queueBuffers(inputs, outputs);
+ }
+
status_t cancelBuffer(int slot, const sp<Fence>& fence) override {
return mBase->cancelBuffer(slot, fence);
}
+ status_t cancelBuffers(
+ const std::vector<CancelBufferInput>& inputs,
+ std::vector<status_t>* results) override {
+ return mBase->cancelBuffers(inputs, results);
+ }
+
int query(int what, int* value) override {
return mBase->query(what, value);
}
+ status_t query(const std::vector<int32_t> inputs,
+ std::vector<QueryOutput>* outputs) override {
+ return mBase->query(inputs, outputs);
+ }
+
status_t connect(
const sp<IProducerListener>& listener,
int api, bool producerControlledByApp,
@@ -789,7 +983,7 @@
switch(code) {
case REQUEST_BUFFER: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
- int bufferIdx = data.readInt32();
+ int bufferIdx = data.readInt32();
sp<GraphicBuffer> buffer;
int result = requestBuffer(bufferIdx, &buffer);
reply->writeInt32(buffer != nullptr);
@@ -799,6 +993,24 @@
reply->writeInt32(result);
return NO_ERROR;
}
+ case REQUEST_BUFFERS: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ std::vector<int32_t> slots;
+ std::vector<RequestBufferOutput> outputs;
+ status_t result = data.readInt32Vector(&slots);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ (void)requestBuffers(slots, &outputs);
+ result = reply->writeVectorSize(outputs);
+ for (const RequestBufferOutput& output : outputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply->write(output);
+ }
+ return result;
+ }
case SET_MAX_DEQUEUED_BUFFER_COUNT: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int maxDequeuedBuffers = data.readInt32();
@@ -841,6 +1053,30 @@
reply->writeInt32(result);
return NO_ERROR;
}
+ case DEQUEUE_BUFFERS: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ std::vector<DequeueBufferInput> inputs;
+ std::vector<DequeueBufferOutput> outputs;
+ status_t result = data.resizeOutVector(&inputs);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ for (DequeueBufferInput& input : inputs) {
+ result = data.read(input);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ }
+ (void)dequeueBuffers(inputs, &outputs);
+ result = reply->writeVectorSize(outputs);
+ for (const DequeueBufferOutput& output : outputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply->write(output);
+ }
+ return result;
+ }
case DETACH_BUFFER: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int slot = data.readInt32();
@@ -848,6 +1084,17 @@
reply->writeInt32(result);
return NO_ERROR;
}
+ case DETACH_BUFFERS: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ std::vector<int32_t> slots;
+ std::vector<status_t> results;
+ status_t result = data.readInt32Vector(&slots);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ (void)detachBuffers(slots, &results);
+ return reply->writeInt32Vector(results);
+ }
case DETACH_NEXT_BUFFER: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
sp<GraphicBuffer> buffer;
@@ -878,6 +1125,31 @@
reply->writeInt32(result);
return NO_ERROR;
}
+ case ATTACH_BUFFERS: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ std::vector<sp<GraphicBuffer>> buffers;
+ status_t result = data.resizeOutVector(&buffers);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ for (sp<GraphicBuffer>& buffer : buffers) {
+ buffer = new GraphicBuffer();
+ result = data.read(*buffer.get());
+ if (result != NO_ERROR) {
+ return result;
+ }
+ }
+ std::vector<AttachBufferOutput> outputs;
+ (void)attachBuffers(buffers, &outputs);
+ result = reply->writeVectorSize(outputs);
+ for (const AttachBufferOutput& output : outputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply->write(output);
+ }
+ return result;
+ }
case QUEUE_BUFFER: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
@@ -890,6 +1162,30 @@
return NO_ERROR;
}
+ case QUEUE_BUFFERS: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ std::vector<QueueBufferInput> inputs;
+ status_t result = data.resizeOutVector(&inputs);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ for (QueueBufferInput& input : inputs) {
+ result = data.read(input);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ }
+ std::vector<QueueBufferOutput> outputs;
+ (void)queueBuffers(inputs, &outputs);
+ result = reply->writeVectorSize(outputs);
+ for (const QueueBufferOutput& output : outputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply->write(output);
+ }
+ return result;
+ }
case CANCEL_BUFFER: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int buf = data.readInt32();
@@ -901,6 +1197,26 @@
reply->writeInt32(result);
return NO_ERROR;
}
+ case CANCEL_BUFFERS: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ std::vector<CancelBufferInput> inputs;
+ status_t result = data.resizeOutVector(&inputs);
+ for (CancelBufferInput& input : inputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = data.read(input);
+ }
+ if (result != NO_ERROR) {
+ return result;
+ }
+ std::vector<status_t> results;
+ result = cancelBuffers(inputs, &results);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ return reply->writeInt32Vector(results);
+ }
case QUERY: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int value = 0;
@@ -910,6 +1226,27 @@
reply->writeInt32(res);
return NO_ERROR;
}
+ case QUERY_MULTIPLE: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ std::vector<int32_t> inputs;
+ status_t result = data.readInt32Vector(&inputs);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ std::vector<QueryOutput> outputs;
+ result = query(inputs, &outputs);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply->writeVectorSize(outputs);
+ for (const QueryOutput& output : outputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply->write(output);
+ }
+ return result;
+ }
case CONNECT: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
sp<IProducerListener> listener;
@@ -1083,11 +1420,4 @@
return BBinder::onTransact(code, data, reply, flags);
}
-// ----------------------------------------------------------------------------
-
-IGraphicBufferProducer::QueueBufferInput::QueueBufferInput(const Parcel& parcel) {
- parcel.read(*this);
-}
-
-
}; // namespace android
diff --git a/libs/gui/IGraphicBufferProducerFlattenables.cpp b/libs/gui/IGraphicBufferProducerFlattenables.cpp
new file mode 100644
index 0000000..c8b9b67
--- /dev/null
+++ b/libs/gui/IGraphicBufferProducerFlattenables.cpp
@@ -0,0 +1,413 @@
+/*
+ * Copyright 2021 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 <gui/IGraphicBufferProducer.h>
+
+namespace android {
+
+constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() {
+ return sizeof(timestamp) +
+ sizeof(isAutoTimestamp) +
+ sizeof(dataSpace) +
+ sizeof(crop) +
+ sizeof(scalingMode) +
+ sizeof(transform) +
+ sizeof(stickyTransform) +
+ sizeof(getFrameTimestamps) +
+ sizeof(slot);
+}
+
+size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const {
+ return minFlattenedSize() +
+ fence->getFlattenedSize() +
+ surfaceDamage.getFlattenedSize() +
+ hdrMetadata.getFlattenedSize();
+}
+
+size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const {
+ return fence->getFdCount();
+}
+
+status_t IGraphicBufferProducer::QueueBufferInput::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const
+{
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, timestamp);
+ FlattenableUtils::write(buffer, size, isAutoTimestamp);
+ FlattenableUtils::write(buffer, size, dataSpace);
+ FlattenableUtils::write(buffer, size, crop);
+ FlattenableUtils::write(buffer, size, scalingMode);
+ FlattenableUtils::write(buffer, size, transform);
+ FlattenableUtils::write(buffer, size, stickyTransform);
+ FlattenableUtils::write(buffer, size, getFrameTimestamps);
+
+ status_t result = fence->flatten(buffer, size, fds, count);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = surfaceDamage.flatten(buffer, size);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize());
+ result = hdrMetadata.flatten(buffer, size);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ FlattenableUtils::advance(buffer, size, hdrMetadata.getFlattenedSize());
+ FlattenableUtils::write(buffer, size, slot);
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::QueueBufferInput::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count)
+{
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, timestamp);
+ FlattenableUtils::read(buffer, size, isAutoTimestamp);
+ FlattenableUtils::read(buffer, size, dataSpace);
+ FlattenableUtils::read(buffer, size, crop);
+ FlattenableUtils::read(buffer, size, scalingMode);
+ FlattenableUtils::read(buffer, size, transform);
+ FlattenableUtils::read(buffer, size, stickyTransform);
+ FlattenableUtils::read(buffer, size, getFrameTimestamps);
+
+ fence = new Fence();
+ status_t result = fence->unflatten(buffer, size, fds, count);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = surfaceDamage.unflatten(buffer, size);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize());
+ result = hdrMetadata.unflatten(buffer, size);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ FlattenableUtils::advance(buffer, size, hdrMetadata.getFlattenedSize());
+ FlattenableUtils::read(buffer, size, slot);
+ return NO_ERROR;
+}
+
+////////////////////////////////////////////////////////////////////////
+constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() {
+ return sizeof(width) + sizeof(height) + sizeof(transformHint) + sizeof(numPendingBuffers) +
+ sizeof(nextFrameNumber) + sizeof(bufferReplaced) + sizeof(maxBufferCount) +
+ sizeof(result);
+}
+size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const {
+ return minFlattenedSize() + frameTimestamps.getFlattenedSize();
+}
+
+size_t IGraphicBufferProducer::QueueBufferOutput::getFdCount() const {
+ return frameTimestamps.getFdCount();
+}
+
+status_t IGraphicBufferProducer::QueueBufferOutput::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const
+{
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, width);
+ FlattenableUtils::write(buffer, size, height);
+ FlattenableUtils::write(buffer, size, transformHint);
+ FlattenableUtils::write(buffer, size, numPendingBuffers);
+ FlattenableUtils::write(buffer, size, nextFrameNumber);
+ FlattenableUtils::write(buffer, size, bufferReplaced);
+ FlattenableUtils::write(buffer, size, maxBufferCount);
+
+ status_t result = frameTimestamps.flatten(buffer, size, fds, count);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ FlattenableUtils::write(buffer, size, result);
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::QueueBufferOutput::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count)
+{
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, width);
+ FlattenableUtils::read(buffer, size, height);
+ FlattenableUtils::read(buffer, size, transformHint);
+ FlattenableUtils::read(buffer, size, numPendingBuffers);
+ FlattenableUtils::read(buffer, size, nextFrameNumber);
+ FlattenableUtils::read(buffer, size, bufferReplaced);
+ FlattenableUtils::read(buffer, size, maxBufferCount);
+
+ status_t result = frameTimestamps.unflatten(buffer, size, fds, count);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ FlattenableUtils::read(buffer, size, result);
+ return NO_ERROR;
+}
+
+////////////////////////////////////////////////////////////////////////
+constexpr size_t IGraphicBufferProducer::RequestBufferOutput::minFlattenedSize() {
+ return sizeof(result) +
+ sizeof(int32_t); // IsBufferNull
+}
+
+size_t IGraphicBufferProducer::RequestBufferOutput::getFlattenedSize() const {
+ return minFlattenedSize() + (buffer == nullptr ? 0 : buffer->getFlattenedSize());
+}
+
+size_t IGraphicBufferProducer::RequestBufferOutput::getFdCount() const {
+ return (buffer == nullptr ? 0 : buffer->getFdCount());
+}
+
+status_t IGraphicBufferProducer::RequestBufferOutput::flatten(
+ void*& fBuffer, size_t& size, int*& fds, size_t& count) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(fBuffer, size, result);
+ const int32_t isBufferNull = (buffer == nullptr ? 1 : 0);
+ FlattenableUtils::write(fBuffer, size, isBufferNull);
+
+ if (!isBufferNull) {
+ status_t status = buffer->flatten(fBuffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::RequestBufferOutput::unflatten(
+ void const*& fBuffer, size_t& size, int const*& fds, size_t& count) {
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(fBuffer, size, result);
+ int32_t isBufferNull = 0;
+ FlattenableUtils::read(fBuffer, size, isBufferNull);
+ buffer = new GraphicBuffer();
+ if (!isBufferNull) {
+ status_t status = buffer->unflatten(fBuffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+size_t IGraphicBufferProducer::DequeueBufferInput::getFlattenedSize() const {
+ return sizeof(width) + sizeof(height) + sizeof(format) + sizeof(usage) +
+ sizeof(int32_t/*getTimestamps*/);
+}
+
+status_t IGraphicBufferProducer::DequeueBufferInput::flatten(void* buffer, size_t size) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+ FlattenableUtils::write(buffer, size, width);
+ FlattenableUtils::write(buffer, size, height);
+ FlattenableUtils::write(buffer, size, format);
+ FlattenableUtils::write(buffer, size, usage);
+ const int32_t getTimestampsInt = (getTimestamps ? 1 : 0);
+ FlattenableUtils::write(buffer, size, getTimestampsInt);
+
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::DequeueBufferInput::unflatten(void const* buffer, size_t size) {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, width);
+ FlattenableUtils::read(buffer, size, height);
+ FlattenableUtils::read(buffer, size, format);
+ FlattenableUtils::read(buffer, size, usage);
+ int32_t getTimestampsInt = 0;
+ FlattenableUtils::read(buffer, size, getTimestampsInt);
+ getTimestamps = (getTimestampsInt == 1);
+
+ return NO_ERROR;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+constexpr size_t IGraphicBufferProducer::DequeueBufferOutput::minFlattenedSize() {
+ return sizeof(result) + sizeof(slot) + sizeof(bufferAge) + sizeof(int32_t/*hasTimestamps*/);
+}
+
+size_t IGraphicBufferProducer::DequeueBufferOutput::getFlattenedSize() const {
+ return minFlattenedSize() +
+ fence->getFlattenedSize() +
+ (timestamps.has_value() ? timestamps->getFlattenedSize() : 0);
+}
+
+size_t IGraphicBufferProducer::DequeueBufferOutput::getFdCount() const {
+ return fence->getFdCount() +
+ (timestamps.has_value() ? timestamps->getFdCount() : 0);
+}
+
+status_t IGraphicBufferProducer::DequeueBufferOutput::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, result);
+ FlattenableUtils::write(buffer, size, slot);
+ FlattenableUtils::write(buffer, size, bufferAge);
+ status_t status = fence->flatten(buffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return result;
+ }
+ const int32_t hasTimestamps = timestamps.has_value() ? 1 : 0;
+ FlattenableUtils::write(buffer, size, hasTimestamps);
+ if (timestamps.has_value()) {
+ status = timestamps->flatten(buffer, size, fds, count);
+ }
+ return status;
+}
+
+status_t IGraphicBufferProducer::DequeueBufferOutput::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count) {
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, result);
+ FlattenableUtils::read(buffer, size, slot);
+ FlattenableUtils::read(buffer, size, bufferAge);
+
+ fence = new Fence();
+ status_t status = fence->unflatten(buffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ int32_t hasTimestamps = 0;
+ FlattenableUtils::read(buffer, size, hasTimestamps);
+ if (hasTimestamps) {
+ timestamps.emplace();
+ status = timestamps->unflatten(buffer, size, fds, count);
+ }
+ return status;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+size_t IGraphicBufferProducer::AttachBufferOutput::getFlattenedSize() const {
+ return sizeof(result) + sizeof(slot);
+}
+
+status_t IGraphicBufferProducer::AttachBufferOutput::flatten(void* buffer, size_t size) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+ FlattenableUtils::write(buffer, size, result);
+ FlattenableUtils::write(buffer, size, slot);
+
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::AttachBufferOutput::unflatten(void const* buffer, size_t size) {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+ FlattenableUtils::read(buffer, size, result);
+ FlattenableUtils::read(buffer, size, slot);
+
+ return NO_ERROR;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+constexpr size_t IGraphicBufferProducer::CancelBufferInput::minFlattenedSize() {
+ return sizeof(slot);
+}
+
+size_t IGraphicBufferProducer::CancelBufferInput::getFlattenedSize() const {
+ return minFlattenedSize() + fence->getFlattenedSize();
+}
+
+size_t IGraphicBufferProducer::CancelBufferInput::getFdCount() const {
+ return fence->getFdCount();
+}
+
+status_t IGraphicBufferProducer::CancelBufferInput::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, slot);
+ return fence->flatten(buffer, size, fds, count);
+}
+
+status_t IGraphicBufferProducer::CancelBufferInput::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count) {
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, slot);
+
+ fence = new Fence();
+ return fence->unflatten(buffer, size, fds, count);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+size_t IGraphicBufferProducer::QueryOutput::getFlattenedSize() const {
+ return sizeof(result) + sizeof(value);
+}
+
+status_t IGraphicBufferProducer::QueryOutput::flatten(void* buffer, size_t size) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+ FlattenableUtils::write(buffer, size, result);
+ FlattenableUtils::write(buffer, size, value);
+
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::QueryOutput::unflatten(void const* buffer, size_t size) {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+ FlattenableUtils::read(buffer, size, result);
+ FlattenableUtils::read(buffer, size, value);
+
+ return NO_ERROR;
+}
+
+} // namespace android
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index e62a61f..53721cf 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -17,13 +17,10 @@
// tag as surfaceflinger
#define LOG_TAG "SurfaceFlinger"
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/Parcel.h>
+#include <android/gui/ITransactionTraceListener.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
-
+#include <binder/Parcel.h>
#include <gui/IDisplayEventConnection.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/IRegionSamplingListener.h>
@@ -31,15 +28,16 @@
#include <gui/ISurfaceComposerClient.h>
#include <gui/LayerDebugInfo.h>
#include <gui/LayerState.h>
-
+#include <private/gui/ParcelUtils.h>
+#include <stdint.h>
+#include <sys/types.h>
#include <system/graphics.h>
-
-#include <ui/DisplayConfig.h>
-#include <ui/DisplayInfo.h>
+#include <ui/DisplayMode.h>
#include <ui/DisplayStatInfo.h>
#include <ui/DisplayState.h>
+#include <ui/DynamicDisplayInfo.h>
#include <ui/HdrCapabilities.h>
-
+#include <ui/StaticDisplayInfo.h>
#include <utils/Log.h>
// ---------------------------------------------------------------------------
@@ -66,145 +64,87 @@
return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder());
}
- virtual void setTransactionState(const Vector<ComposerState>& state,
- const Vector<DisplayState>& displays, uint32_t flags,
- const sp<IBinder>& applyToken,
- const InputWindowCommands& commands,
- int64_t desiredPresentTime,
- const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
- const std::vector<ListenerCallbacks>& listenerCallbacks) {
+ status_t setTransactionState(const FrameTimelineInfo& frameTimelineInfo,
+ const Vector<ComposerState>& state,
+ const Vector<DisplayState>& displays, uint32_t flags,
+ const sp<IBinder>& applyToken, const InputWindowCommands& commands,
+ int64_t desiredPresentTime, bool isAutoTimestamp,
+ const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+ const std::vector<ListenerCallbacks>& listenerCallbacks,
+ uint64_t transactionId) override {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- data.writeUint32(static_cast<uint32_t>(state.size()));
+ SAFE_PARCEL(frameTimelineInfo.write, data);
+
+ SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(state.size()));
for (const auto& s : state) {
- s.write(data);
+ SAFE_PARCEL(s.write, data);
}
- data.writeUint32(static_cast<uint32_t>(displays.size()));
+ SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(displays.size()));
for (const auto& d : displays) {
- d.write(data);
+ SAFE_PARCEL(d.write, data);
}
- data.writeUint32(flags);
- data.writeStrongBinder(applyToken);
- commands.write(data);
- data.writeInt64(desiredPresentTime);
- data.writeStrongBinder(uncacheBuffer.token.promote());
- data.writeUint64(uncacheBuffer.id);
- data.writeBool(hasListenerCallbacks);
+ SAFE_PARCEL(data.writeUint32, flags);
+ SAFE_PARCEL(data.writeStrongBinder, applyToken);
+ SAFE_PARCEL(commands.write, data);
+ SAFE_PARCEL(data.writeInt64, desiredPresentTime);
+ SAFE_PARCEL(data.writeBool, isAutoTimestamp);
+ SAFE_PARCEL(data.writeStrongBinder, uncacheBuffer.token.promote());
+ SAFE_PARCEL(data.writeUint64, uncacheBuffer.id);
+ SAFE_PARCEL(data.writeBool, hasListenerCallbacks);
- if (data.writeVectorSize(listenerCallbacks) == NO_ERROR) {
- for (const auto& [listener, callbackIds] : listenerCallbacks) {
- data.writeStrongBinder(listener);
- data.writeInt64Vector(callbackIds);
- }
+ SAFE_PARCEL(data.writeVectorSize, listenerCallbacks);
+ for (const auto& [listener, callbackIds] : listenerCallbacks) {
+ SAFE_PARCEL(data.writeStrongBinder, listener);
+ SAFE_PARCEL(data.writeParcelableVector, callbackIds);
}
- remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply);
+ SAFE_PARCEL(data.writeUint64, transactionId);
+
+ return remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply);
}
- virtual void bootFinished()
- {
+ void bootFinished() override {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
remote()->transact(BnSurfaceComposer::BOOT_FINISHED, data, &reply);
}
- virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
- bool& outCapturedSecureLayers, ui::Dataspace reqDataspace,
- ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
- uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
- ui::Rotation rotation, bool captureSecureLayers) {
+ status_t captureDisplay(const DisplayCaptureArgs& args,
+ const sp<IScreenCaptureListener>& captureListener) override {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- data.writeStrongBinder(display);
- data.writeInt32(static_cast<int32_t>(reqDataspace));
- data.writeInt32(static_cast<int32_t>(reqPixelFormat));
- data.write(sourceCrop);
- data.writeUint32(reqWidth);
- data.writeUint32(reqHeight);
- data.writeInt32(static_cast<int32_t>(useIdentityTransform));
- data.writeInt32(static_cast<int32_t>(rotation));
- data.writeInt32(static_cast<int32_t>(captureSecureLayers));
- status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply);
- if (result != NO_ERROR) {
- ALOGE("captureScreen failed to transact: %d", result);
- return result;
- }
- result = reply.readInt32();
- if (result != NO_ERROR) {
- ALOGE("captureScreen failed to readInt32: %d", result);
- return result;
- }
+ SAFE_PARCEL(args.write, data);
+ SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener));
- *outBuffer = new GraphicBuffer();
- reply.read(**outBuffer);
- outCapturedSecureLayers = reply.readBool();
-
- return result;
+ return remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY, data, &reply);
}
- virtual status_t captureScreen(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace,
- sp<GraphicBuffer>* outBuffer) {
+ status_t captureDisplay(uint64_t displayOrLayerStack,
+ const sp<IScreenCaptureListener>& captureListener) override {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- data.writeUint64(displayOrLayerStack);
- status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN_BY_ID, data, &reply);
- if (result != NO_ERROR) {
- ALOGE("captureScreen failed to transact: %d", result);
- return result;
- }
- result = reply.readInt32();
- if (result != NO_ERROR) {
- ALOGE("captureScreen failed to readInt32: %d", result);
- return result;
- }
+ SAFE_PARCEL(data.writeUint64, displayOrLayerStack);
+ SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener));
- *outDataspace = static_cast<ui::Dataspace>(reply.readInt32());
- *outBuffer = new GraphicBuffer();
- reply.read(**outBuffer);
- return result;
+ return remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY_BY_ID, data, &reply);
}
- virtual status_t captureLayers(
- const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer,
- const ui::Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat,
- const Rect& sourceCrop,
- const std::unordered_set<sp<IBinder>, SpHash<IBinder>>& excludeLayers, float frameScale,
- bool childrenOnly) {
+ status_t captureLayers(const LayerCaptureArgs& args,
+ const sp<IScreenCaptureListener>& captureListener) override {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- data.writeStrongBinder(layerHandleBinder);
- data.writeInt32(static_cast<int32_t>(reqDataspace));
- data.writeInt32(static_cast<int32_t>(reqPixelFormat));
- data.write(sourceCrop);
- data.writeInt32(excludeLayers.size());
- for (auto el : excludeLayers) {
- data.writeStrongBinder(el);
- }
- data.writeFloat(frameScale);
- data.writeBool(childrenOnly);
- status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_LAYERS, data, &reply);
- if (result != NO_ERROR) {
- ALOGE("captureLayers failed to transact: %d", result);
- return result;
- }
- result = reply.readInt32();
- if (result != NO_ERROR) {
- ALOGE("captureLayers failed to readInt32: %d", result);
- return result;
- }
+ SAFE_PARCEL(args.write, data);
+ SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener));
- *outBuffer = new GraphicBuffer();
- reply.read(**outBuffer);
-
- return result;
+ return remote()->transact(BnSurfaceComposer::CAPTURE_LAYERS, data, &reply);
}
- virtual bool authenticateSurfaceTexture(
- const sp<IGraphicBufferProducer>& bufferProducer) const
- {
+ bool authenticateSurfaceTexture(
+ const sp<IGraphicBufferProducer>& bufferProducer) const override {
Parcel data, reply;
int err = NO_ERROR;
err = data.writeInterfaceToken(
@@ -237,8 +177,7 @@
return result != 0;
}
- virtual status_t getSupportedFrameTimestamps(
- std::vector<FrameEvent>* outSupported) const {
+ status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) const override {
if (!outSupported) {
return UNEXPECTED_NULL;
}
@@ -281,8 +220,8 @@
return NO_ERROR;
}
- virtual sp<IDisplayEventConnection> createDisplayEventConnection(VsyncSource vsyncSource,
- ConfigChanged configChanged) {
+ sp<IDisplayEventConnection> createDisplayEventConnection(
+ VsyncSource vsyncSource, EventRegistrationFlags eventRegistration) override {
Parcel data, reply;
sp<IDisplayEventConnection> result;
int err = data.writeInterfaceToken(
@@ -291,7 +230,7 @@
return result;
}
data.writeInt32(static_cast<int32_t>(vsyncSource));
- data.writeInt32(static_cast<int32_t>(configChanged));
+ data.writeUint32(eventRegistration.get());
err = remote()->transact(
BnSurfaceComposer::CREATE_DISPLAY_EVENT_CONNECTION,
data, &reply);
@@ -304,31 +243,47 @@
return result;
}
- virtual sp<IBinder> createDisplay(const String8& displayName, bool secure)
- {
+ sp<IBinder> createDisplay(const String8& displayName, bool secure) override {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- data.writeString8(displayName);
- data.writeInt32(secure ? 1 : 0);
- remote()->transact(BnSurfaceComposer::CREATE_DISPLAY, data, &reply);
- return reply.readStrongBinder();
+ status_t status = data.writeString8(displayName);
+ if (status) {
+ return nullptr;
+ }
+ status = data.writeBool(secure);
+ if (status) {
+ return nullptr;
+ }
+
+ status = remote()->transact(BnSurfaceComposer::CREATE_DISPLAY, data, &reply);
+ if (status) {
+ return nullptr;
+ }
+ sp<IBinder> display;
+ status = reply.readNullableStrongBinder(&display);
+ if (status) {
+ return nullptr;
+ }
+ return display;
}
- virtual void destroyDisplay(const sp<IBinder>& display)
- {
+ void destroyDisplay(const sp<IBinder>& display) override {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
data.writeStrongBinder(display);
remote()->transact(BnSurfaceComposer::DESTROY_DISPLAY, data, &reply);
}
- virtual std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const {
+ std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const override {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
if (remote()->transact(BnSurfaceComposer::GET_PHYSICAL_DISPLAY_IDS, data, &reply) ==
NO_ERROR) {
- std::vector<PhysicalDisplayId> displayIds;
- if (reply.readUint64Vector(&displayIds) == NO_ERROR) {
+ std::vector<uint64_t> rawIds;
+ if (reply.readUint64Vector(&rawIds) == NO_ERROR) {
+ std::vector<PhysicalDisplayId> displayIds(rawIds.size());
+ std::transform(rawIds.begin(), rawIds.end(), displayIds.begin(),
+ [](uint64_t rawId) { return PhysicalDisplayId(rawId); });
return displayIds;
}
}
@@ -336,16 +291,15 @@
return {};
}
- virtual sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const {
+ sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const override {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- data.writeUint64(displayId);
+ data.writeUint64(displayId.value);
remote()->transact(BnSurfaceComposer::GET_PHYSICAL_DISPLAY_TOKEN, data, &reply);
return reply.readStrongBinder();
}
- virtual void setPowerMode(const sp<IBinder>& display, int mode)
- {
+ void setPowerMode(const sp<IBinder>& display, int mode) override {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
data.writeStrongBinder(display);
@@ -353,7 +307,7 @@
remote()->transact(BnSurfaceComposer::SET_POWER_MODE, data, &reply);
}
- virtual status_t getDisplayState(const sp<IBinder>& display, ui::DisplayState* state) {
+ status_t getDisplayState(const sp<IBinder>& display, ui::DisplayState* state) override {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
data.writeStrongBinder(display);
@@ -365,39 +319,29 @@
return result;
}
- virtual status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo* info) {
+ status_t getStaticDisplayInfo(const sp<IBinder>& display,
+ ui::StaticDisplayInfo* info) override {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
data.writeStrongBinder(display);
- remote()->transact(BnSurfaceComposer::GET_DISPLAY_INFO, data, &reply);
+ remote()->transact(BnSurfaceComposer::GET_STATIC_DISPLAY_INFO, data, &reply);
const status_t result = reply.readInt32();
- if (result == NO_ERROR) {
- memcpy(info, reply.readInplace(sizeof(DisplayInfo)), sizeof(DisplayInfo));
- }
- return result;
+ if (result != NO_ERROR) return result;
+ return reply.read(*info);
}
- virtual status_t getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayConfig>* configs) {
+ status_t getDynamicDisplayInfo(const sp<IBinder>& display,
+ ui::DynamicDisplayInfo* info) override {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
data.writeStrongBinder(display);
- remote()->transact(BnSurfaceComposer::GET_DISPLAY_CONFIGS, data, &reply);
+ remote()->transact(BnSurfaceComposer::GET_DYNAMIC_DISPLAY_INFO, data, &reply);
const status_t result = reply.readInt32();
- if (result == NO_ERROR) {
- const size_t numConfigs = reply.readUint32();
- configs->clear();
- configs->resize(numConfigs);
- for (size_t c = 0; c < numConfigs; ++c) {
- memcpy(&(configs->editItemAt(c)), reply.readInplace(sizeof(DisplayConfig)),
- sizeof(DisplayConfig));
- }
- }
- return result;
+ if (result != NO_ERROR) return result;
+ return reply.read(*info);
}
- virtual status_t getDisplayStats(const sp<IBinder>& display,
- DisplayStatInfo* stats)
- {
+ status_t getDisplayStats(const sp<IBinder>& display, DisplayStatInfo* stats) override {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
data.writeStrongBinder(display);
@@ -411,47 +355,8 @@
return result;
}
- virtual int getActiveConfig(const sp<IBinder>& display)
- {
- Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- data.writeStrongBinder(display);
- remote()->transact(BnSurfaceComposer::GET_ACTIVE_CONFIG, data, &reply);
- return reply.readInt32();
- }
-
- virtual status_t getDisplayColorModes(const sp<IBinder>& display,
- Vector<ColorMode>* outColorModes) {
- Parcel data, reply;
- status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- if (result != NO_ERROR) {
- ALOGE("getDisplayColorModes failed to writeInterfaceToken: %d", result);
- return result;
- }
- result = data.writeStrongBinder(display);
- if (result != NO_ERROR) {
- ALOGE("getDisplayColorModes failed to writeStrongBinder: %d", result);
- return result;
- }
- result = remote()->transact(BnSurfaceComposer::GET_DISPLAY_COLOR_MODES, data, &reply);
- if (result != NO_ERROR) {
- ALOGE("getDisplayColorModes failed to transact: %d", result);
- return result;
- }
- result = static_cast<status_t>(reply.readInt32());
- if (result == NO_ERROR) {
- size_t numModes = reply.readUint32();
- outColorModes->clear();
- outColorModes->resize(numModes);
- for (size_t i = 0; i < numModes; ++i) {
- outColorModes->replaceAt(static_cast<ColorMode>(reply.readInt32()), i);
- }
- }
- return result;
- }
-
- virtual status_t getDisplayNativePrimaries(const sp<IBinder>& display,
- ui::DisplayPrimaries& primaries) {
+ status_t getDisplayNativePrimaries(const sp<IBinder>& display,
+ ui::DisplayPrimaries& primaries) override {
Parcel data, reply;
status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
if (result != NO_ERROR) {
@@ -476,28 +381,7 @@
return result;
}
- virtual ColorMode getActiveColorMode(const sp<IBinder>& display) {
- Parcel data, reply;
- status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- if (result != NO_ERROR) {
- ALOGE("getActiveColorMode failed to writeInterfaceToken: %d", result);
- return static_cast<ColorMode>(result);
- }
- result = data.writeStrongBinder(display);
- if (result != NO_ERROR) {
- ALOGE("getActiveColorMode failed to writeStrongBinder: %d", result);
- return static_cast<ColorMode>(result);
- }
- result = remote()->transact(BnSurfaceComposer::GET_ACTIVE_COLOR_MODE, data, &reply);
- if (result != NO_ERROR) {
- ALOGE("getActiveColorMode failed to transact: %d", result);
- return static_cast<ColorMode>(result);
- }
- return static_cast<ColorMode>(reply.readInt32());
- }
-
- virtual status_t setActiveColorMode(const sp<IBinder>& display,
- ColorMode colorMode) {
+ status_t setActiveColorMode(const sp<IBinder>& display, ColorMode colorMode) override {
Parcel data, reply;
status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
if (result != NO_ERROR) {
@@ -522,25 +406,7 @@
return static_cast<status_t>(reply.readInt32());
}
- virtual status_t getAutoLowLatencyModeSupport(const sp<IBinder>& display,
- bool* outSupport) const {
- Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- status_t result = data.writeStrongBinder(display);
- if (result != NO_ERROR) {
- ALOGE("getAutoLowLatencyModeSupport failed to writeStrongBinder: %d", result);
- return result;
- }
- result = remote()->transact(BnSurfaceComposer::GET_AUTO_LOW_LATENCY_MODE_SUPPORT, data,
- &reply);
- if (result != NO_ERROR) {
- ALOGE("getAutoLowLatencyModeSupport failed to transact: %d", result);
- return result;
- }
- return reply.readBool(outSupport);
- }
-
- virtual void setAutoLowLatencyMode(const sp<IBinder>& display, bool on) {
+ void setAutoLowLatencyMode(const sp<IBinder>& display, bool on) override {
Parcel data, reply;
status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
if (result != NO_ERROR) {
@@ -565,23 +431,7 @@
}
}
- virtual status_t getGameContentTypeSupport(const sp<IBinder>& display, bool* outSupport) const {
- Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- status_t result = data.writeStrongBinder(display);
- if (result != NO_ERROR) {
- ALOGE("getGameContentTypeSupport failed to writeStrongBinder: %d", result);
- return result;
- }
- result = remote()->transact(BnSurfaceComposer::GET_GAME_CONTENT_TYPE_SUPPORT, data, &reply);
- if (result != NO_ERROR) {
- ALOGE("getGameContentTypeSupport failed to transact: %d", result);
- return result;
- }
- return reply.readBool(outSupport);
- }
-
- virtual void setGameContentType(const sp<IBinder>& display, bool on) {
+ void setGameContentType(const sp<IBinder>& display, bool on) override {
Parcel data, reply;
status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
if (result != NO_ERROR) {
@@ -604,7 +454,7 @@
}
}
- virtual status_t clearAnimationFrameStats() {
+ status_t clearAnimationFrameStats() override {
Parcel data, reply;
status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
if (result != NO_ERROR) {
@@ -619,7 +469,7 @@
return reply.readInt32();
}
- virtual status_t getAnimationFrameStats(FrameStats* outStats) const {
+ status_t getAnimationFrameStats(FrameStats* outStats) const override {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
remote()->transact(BnSurfaceComposer::GET_ANIMATION_FRAME_STATS, data, &reply);
@@ -627,29 +477,49 @@
return reply.readInt32();
}
- virtual status_t getHdrCapabilities(const sp<IBinder>& display,
- HdrCapabilities* outCapabilities) const {
+ virtual status_t overrideHdrTypes(const sp<IBinder>& display,
+ const std::vector<ui::Hdr>& hdrTypes) {
Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- status_t result = data.writeStrongBinder(display);
- if (result != NO_ERROR) {
- ALOGE("getHdrCapabilities failed to writeStrongBinder: %d", result);
- return result;
+ SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+ SAFE_PARCEL(data.writeStrongBinder, display);
+
+ std::vector<int32_t> hdrTypesVector;
+ for (ui::Hdr i : hdrTypes) {
+ hdrTypesVector.push_back(static_cast<int32_t>(i));
}
- result = remote()->transact(BnSurfaceComposer::GET_HDR_CAPABILITIES,
- data, &reply);
+ SAFE_PARCEL(data.writeInt32Vector, hdrTypesVector);
+
+ status_t result = remote()->transact(BnSurfaceComposer::OVERRIDE_HDR_TYPES, data, &reply);
if (result != NO_ERROR) {
- ALOGE("getHdrCapabilities failed to transact: %d", result);
+ ALOGE("overrideHdrTypes failed to transact: %d", result);
return result;
}
- result = reply.readInt32();
- if (result == NO_ERROR) {
- result = reply.read(*outCapabilities);
- }
return result;
}
- virtual status_t enableVSyncInjections(bool enable) {
+ status_t onPullAtom(const int32_t atomId, std::string* pulledData, bool* success) {
+ Parcel data, reply;
+ SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+ SAFE_PARCEL(data.writeInt32, atomId);
+
+ status_t err = remote()->transact(BnSurfaceComposer::ON_PULL_ATOM, data, &reply);
+ if (err != NO_ERROR) {
+ ALOGE("onPullAtom failed to transact: %d", err);
+ return err;
+ }
+
+ int32_t size = 0;
+ SAFE_PARCEL(reply.readInt32, &size);
+ const void* dataPtr = reply.readInplace(size);
+ if (dataPtr == nullptr) {
+ return UNEXPECTED_NULL;
+ }
+ pulledData->assign((const char*)dataPtr, size);
+ SAFE_PARCEL(reply.readBool, success);
+ return NO_ERROR;
+ }
+
+ status_t enableVSyncInjections(bool enable) override {
Parcel data, reply;
status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
if (result != NO_ERROR) {
@@ -670,7 +540,7 @@
return result;
}
- virtual status_t injectVSync(nsecs_t when) {
+ status_t injectVSync(nsecs_t when) override {
Parcel data, reply;
status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
if (result != NO_ERROR) {
@@ -691,7 +561,7 @@
return result;
}
- virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) {
+ status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) override {
if (!outLayers) {
return UNEXPECTED_NULL;
}
@@ -721,10 +591,10 @@
return reply.readParcelableVector(outLayers);
}
- virtual status_t getCompositionPreference(ui::Dataspace* defaultDataspace,
- ui::PixelFormat* defaultPixelFormat,
- ui::Dataspace* wideColorGamutDataspace,
- ui::PixelFormat* wideColorGamutPixelFormat) const {
+ status_t getCompositionPreference(ui::Dataspace* defaultDataspace,
+ ui::PixelFormat* defaultPixelFormat,
+ ui::Dataspace* wideColorGamutDataspace,
+ ui::PixelFormat* wideColorGamutPixelFormat) const override {
Parcel data, reply;
status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
if (error != NO_ERROR) {
@@ -744,7 +614,7 @@
return error;
}
- virtual status_t getColorManagement(bool* outGetColorManagement) const {
+ status_t getColorManagement(bool* outGetColorManagement) const override {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
remote()->transact(BnSurfaceComposer::GET_COLOR_MANAGEMENT, data, &reply);
@@ -756,10 +626,10 @@
return err;
}
- virtual status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& display,
- ui::PixelFormat* outFormat,
- ui::Dataspace* outDataspace,
- uint8_t* outComponentMask) const {
+ status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& display,
+ ui::PixelFormat* outFormat,
+ ui::Dataspace* outDataspace,
+ uint8_t* outComponentMask) const override {
if (!outFormat || !outDataspace || !outComponentMask) return BAD_VALUE;
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -793,8 +663,8 @@
return error;
}
- virtual status_t setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable,
- uint8_t componentMask, uint64_t maxFrames) {
+ status_t setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable,
+ uint8_t componentMask, uint64_t maxFrames) override {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
data.writeStrongBinder(display);
@@ -807,9 +677,9 @@
return result;
}
- virtual status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames,
- uint64_t timestamp,
- DisplayedFrameStats* outStats) const {
+ status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames,
+ uint64_t timestamp,
+ DisplayedFrameStats* outStats) const override {
if (!outStats) return BAD_VALUE;
Parcel data, reply;
@@ -846,7 +716,7 @@
return result;
}
- virtual status_t getProtectedContentSupport(bool* outSupported) const {
+ status_t getProtectedContentSupport(bool* outSupported) const override {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
status_t error =
@@ -858,8 +728,8 @@
return error;
}
- virtual status_t isWideColorDisplay(const sp<IBinder>& token,
- bool* outIsWideColorDisplay) const {
+ status_t isWideColorDisplay(const sp<IBinder>& token,
+ bool* outIsWideColorDisplay) const override {
Parcel data, reply;
status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
if (error != NO_ERROR) {
@@ -878,9 +748,8 @@
return error;
}
- virtual status_t addRegionSamplingListener(const Rect& samplingArea,
- const sp<IBinder>& stopLayerHandle,
- const sp<IRegionSamplingListener>& listener) {
+ status_t addRegionSamplingListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle,
+ const sp<IRegionSamplingListener>& listener) override {
Parcel data, reply;
status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
if (error != NO_ERROR) {
@@ -909,7 +778,7 @@
return error;
}
- virtual status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) {
+ status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) override {
Parcel data, reply;
status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
if (error != NO_ERROR) {
@@ -929,119 +798,160 @@
return error;
}
- virtual status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
- int32_t defaultConfig,
- float primaryRefreshRateMin,
- float primaryRefreshRateMax,
- float appRequestRefreshRateMin,
- float appRequestRefreshRateMax) {
+ virtual status_t addFpsListener(int32_t taskId, const sp<gui::IFpsListener>& listener) {
+ Parcel data, reply;
+ SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+ SAFE_PARCEL(data.writeInt32, taskId);
+ SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener));
+ const status_t error =
+ remote()->transact(BnSurfaceComposer::ADD_FPS_LISTENER, data, &reply);
+ if (error != OK) {
+ ALOGE("addFpsListener: Failed to transact");
+ }
+ return error;
+ }
+
+ virtual status_t removeFpsListener(const sp<gui::IFpsListener>& listener) {
+ Parcel data, reply;
+ SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+ SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener));
+
+ const status_t error =
+ remote()->transact(BnSurfaceComposer::REMOVE_FPS_LISTENER, data, &reply);
+ if (error != OK) {
+ ALOGE("removeFpsListener: Failed to transact");
+ }
+ return error;
+ }
+
+ status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
+ ui::DisplayModeId defaultMode, bool allowGroupSwitching,
+ float primaryRefreshRateMin, float primaryRefreshRateMax,
+ float appRequestRefreshRateMin,
+ float appRequestRefreshRateMax) override {
Parcel data, reply;
status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
if (result != NO_ERROR) {
- ALOGE("setDesiredDisplayConfigSpecs: failed to writeInterfaceToken: %d", result);
+ ALOGE("setDesiredDisplayModeSpecs: failed to writeInterfaceToken: %d", result);
return result;
}
result = data.writeStrongBinder(displayToken);
if (result != NO_ERROR) {
- ALOGE("setDesiredDisplayConfigSpecs: failed to write display token: %d", result);
+ ALOGE("setDesiredDisplayModeSpecs: failed to write display token: %d", result);
return result;
}
- result = data.writeInt32(defaultConfig);
+ result = data.writeInt32(defaultMode);
if (result != NO_ERROR) {
- ALOGE("setDesiredDisplayConfigSpecs failed to write defaultConfig: %d", result);
+ ALOGE("setDesiredDisplayModeSpecs failed to write defaultMode: %d", result);
+ return result;
+ }
+ result = data.writeBool(allowGroupSwitching);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayModeSpecs failed to write allowGroupSwitching: %d", result);
return result;
}
result = data.writeFloat(primaryRefreshRateMin);
if (result != NO_ERROR) {
- ALOGE("setDesiredDisplayConfigSpecs failed to write primaryRefreshRateMin: %d", result);
+ ALOGE("setDesiredDisplayModeSpecs failed to write primaryRefreshRateMin: %d", result);
return result;
}
result = data.writeFloat(primaryRefreshRateMax);
if (result != NO_ERROR) {
- ALOGE("setDesiredDisplayConfigSpecs failed to write primaryRefreshRateMax: %d", result);
+ ALOGE("setDesiredDisplayModeSpecs failed to write primaryRefreshRateMax: %d", result);
return result;
}
result = data.writeFloat(appRequestRefreshRateMin);
if (result != NO_ERROR) {
- ALOGE("setDesiredDisplayConfigSpecs failed to write appRequestRefreshRateMin: %d",
+ ALOGE("setDesiredDisplayModeSpecs failed to write appRequestRefreshRateMin: %d",
result);
return result;
}
result = data.writeFloat(appRequestRefreshRateMax);
if (result != NO_ERROR) {
- ALOGE("setDesiredDisplayConfigSpecs failed to write appRequestRefreshRateMax: %d",
+ ALOGE("setDesiredDisplayModeSpecs failed to write appRequestRefreshRateMax: %d",
result);
return result;
}
- result = remote()->transact(BnSurfaceComposer::SET_DESIRED_DISPLAY_CONFIG_SPECS, data,
- &reply);
+ result =
+ remote()->transact(BnSurfaceComposer::SET_DESIRED_DISPLAY_MODE_SPECS, data, &reply);
if (result != NO_ERROR) {
- ALOGE("setDesiredDisplayConfigSpecs failed to transact: %d", result);
+ ALOGE("setDesiredDisplayModeSpecs failed to transact: %d", result);
return result;
}
return reply.readInt32();
}
- virtual status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
- int32_t* outDefaultConfig,
- float* outPrimaryRefreshRateMin,
- float* outPrimaryRefreshRateMax,
- float* outAppRequestRefreshRateMin,
- float* outAppRequestRefreshRateMax) {
- if (!outDefaultConfig || !outPrimaryRefreshRateMin || !outPrimaryRefreshRateMax ||
- !outAppRequestRefreshRateMin || !outAppRequestRefreshRateMax) {
+ status_t getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
+ ui::DisplayModeId* outDefaultMode,
+ bool* outAllowGroupSwitching,
+ float* outPrimaryRefreshRateMin,
+ float* outPrimaryRefreshRateMax,
+ float* outAppRequestRefreshRateMin,
+ float* outAppRequestRefreshRateMax) override {
+ if (!outDefaultMode || !outAllowGroupSwitching || !outPrimaryRefreshRateMin ||
+ !outPrimaryRefreshRateMax || !outAppRequestRefreshRateMin ||
+ !outAppRequestRefreshRateMax) {
return BAD_VALUE;
}
Parcel data, reply;
status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
if (result != NO_ERROR) {
- ALOGE("getDesiredDisplayConfigSpecs failed to writeInterfaceToken: %d", result);
+ ALOGE("getDesiredDisplayModeSpecs failed to writeInterfaceToken: %d", result);
return result;
}
result = data.writeStrongBinder(displayToken);
if (result != NO_ERROR) {
- ALOGE("getDesiredDisplayConfigSpecs failed to writeStrongBinder: %d", result);
+ ALOGE("getDesiredDisplayModeSpecs failed to writeStrongBinder: %d", result);
return result;
}
- result = remote()->transact(BnSurfaceComposer::GET_DESIRED_DISPLAY_CONFIG_SPECS, data,
- &reply);
+ result =
+ remote()->transact(BnSurfaceComposer::GET_DESIRED_DISPLAY_MODE_SPECS, data, &reply);
if (result != NO_ERROR) {
- ALOGE("getDesiredDisplayConfigSpecs failed to transact: %d", result);
+ ALOGE("getDesiredDisplayModeSpecs failed to transact: %d", result);
return result;
}
- result = reply.readInt32(outDefaultConfig);
+
+ result = reply.readInt32(outDefaultMode);
if (result != NO_ERROR) {
- ALOGE("getDesiredDisplayConfigSpecs failed to read defaultConfig: %d", result);
+ ALOGE("getDesiredDisplayModeSpecs failed to read defaultMode: %d", result);
+ return result;
+ }
+ if (*outDefaultMode < 0) {
+ ALOGE("%s: defaultMode must be non-negative but it was %d", __func__, *outDefaultMode);
+ return BAD_VALUE;
+ }
+
+ result = reply.readBool(outAllowGroupSwitching);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayModeSpecs failed to read allowGroupSwitching: %d", result);
return result;
}
result = reply.readFloat(outPrimaryRefreshRateMin);
if (result != NO_ERROR) {
- ALOGE("getDesiredDisplayConfigSpecs failed to read primaryRefreshRateMin: %d", result);
+ ALOGE("getDesiredDisplayModeSpecs failed to read primaryRefreshRateMin: %d", result);
return result;
}
result = reply.readFloat(outPrimaryRefreshRateMax);
if (result != NO_ERROR) {
- ALOGE("getDesiredDisplayConfigSpecs failed to read primaryRefreshRateMax: %d", result);
+ ALOGE("getDesiredDisplayModeSpecs failed to read primaryRefreshRateMax: %d", result);
return result;
}
result = reply.readFloat(outAppRequestRefreshRateMin);
if (result != NO_ERROR) {
- ALOGE("getDesiredDisplayConfigSpecs failed to read appRequestRefreshRateMin: %d",
- result);
+ ALOGE("getDesiredDisplayModeSpecs failed to read appRequestRefreshRateMin: %d", result);
return result;
}
result = reply.readFloat(outAppRequestRefreshRateMax);
if (result != NO_ERROR) {
- ALOGE("getDesiredDisplayConfigSpecs failed to read appRequestRefreshRateMax: %d",
- result);
+ ALOGE("getDesiredDisplayModeSpecs failed to read appRequestRefreshRateMax: %d", result);
return result;
}
return reply.readInt32();
}
- virtual status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
- bool* outSupport) const {
+ status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
+ bool* outSupport) const override {
Parcel data, reply;
status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
if (error != NO_ERROR) {
@@ -1068,7 +978,8 @@
return NO_ERROR;
}
- virtual status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) {
+ status_t setDisplayBrightness(const sp<IBinder>& displayToken,
+ const gui::DisplayBrightness& brightness) override {
Parcel data, reply;
status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
if (error != NO_ERROR) {
@@ -1080,7 +991,7 @@
ALOGE("setDisplayBrightness: failed to write display token: %d", error);
return error;
}
- error = data.writeFloat(brightness);
+ error = data.writeParcelable(brightness);
if (error != NO_ERROR) {
ALOGE("setDisplayBrightness: failed to write brightness: %d", error);
return error;
@@ -1093,29 +1004,57 @@
return NO_ERROR;
}
- virtual status_t notifyPowerHint(int32_t hintId) {
+ status_t addHdrLayerInfoListener(const sp<IBinder>& displayToken,
+ const sp<gui::IHdrLayerInfoListener>& listener) override {
+ Parcel data, reply;
+ SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+ SAFE_PARCEL(data.writeStrongBinder, displayToken);
+ SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener));
+ const status_t error =
+ remote()->transact(BnSurfaceComposer::ADD_HDR_LAYER_INFO_LISTENER, data, &reply);
+ if (error != OK) {
+ ALOGE("addHdrLayerInfoListener: Failed to transact; error = %d", error);
+ }
+ return error;
+ }
+
+ status_t removeHdrLayerInfoListener(const sp<IBinder>& displayToken,
+ const sp<gui::IHdrLayerInfoListener>& listener) override {
+ Parcel data, reply;
+ SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+ SAFE_PARCEL(data.writeStrongBinder, displayToken);
+ SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener));
+ const status_t error =
+ remote()->transact(BnSurfaceComposer::REMOVE_HDR_LAYER_INFO_LISTENER, data, &reply);
+ if (error != OK) {
+ ALOGE("removeHdrLayerInfoListener: Failed to transact; error = %d", error);
+ }
+ return error;
+ }
+
+ status_t notifyPowerBoost(int32_t boostId) override {
Parcel data, reply;
status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
if (error != NO_ERROR) {
- ALOGE("notifyPowerHint: failed to write interface token: %d", error);
+ ALOGE("notifyPowerBoost: failed to write interface token: %d", error);
return error;
}
- error = data.writeInt32(hintId);
+ error = data.writeInt32(boostId);
if (error != NO_ERROR) {
- ALOGE("notifyPowerHint: failed to write hintId: %d", error);
+ ALOGE("notifyPowerBoost: failed to write boostId: %d", error);
return error;
}
- error = remote()->transact(BnSurfaceComposer::NOTIFY_POWER_HINT, data, &reply,
+ error = remote()->transact(BnSurfaceComposer::NOTIFY_POWER_BOOST, data, &reply,
IBinder::FLAG_ONEWAY);
if (error != NO_ERROR) {
- ALOGE("notifyPowerHint: failed to transact: %d", error);
+ ALOGE("notifyPowerBoost: failed to transact: %d", error);
return error;
}
return NO_ERROR;
}
- virtual status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
- float lightPosY, float lightPosZ, float lightRadius) {
+ status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
+ float lightPosY, float lightPosZ, float lightRadius) override {
Parcel data, reply;
status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
if (error != NO_ERROR) {
@@ -1143,34 +1082,16 @@
return NO_ERROR;
}
- virtual status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
- int8_t compatibility) {
+ status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
+ int8_t compatibility, int8_t changeFrameRateStrategy) override {
Parcel data, reply;
- status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- if (err != NO_ERROR) {
- ALOGE("setFrameRate: failed writing interface token: %s (%d)", strerror(-err), -err);
- return err;
- }
+ SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+ SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(surface));
+ SAFE_PARCEL(data.writeFloat, frameRate);
+ SAFE_PARCEL(data.writeByte, compatibility);
+ SAFE_PARCEL(data.writeByte, changeFrameRateStrategy);
- err = data.writeStrongBinder(IInterface::asBinder(surface));
- if (err != NO_ERROR) {
- ALOGE("setFrameRate: failed writing strong binder: %s (%d)", strerror(-err), -err);
- return err;
- }
-
- err = data.writeFloat(frameRate);
- if (err != NO_ERROR) {
- ALOGE("setFrameRate: failed writing float: %s (%d)", strerror(-err), -err);
- return err;
- }
-
- err = data.writeByte(compatibility);
- if (err != NO_ERROR) {
- ALOGE("setFrameRate: failed writing byte: %s (%d)", strerror(-err), -err);
- return err;
- }
-
- err = remote()->transact(BnSurfaceComposer::SET_FRAME_RATE, data, &reply);
+ status_t err = remote()->transact(BnSurfaceComposer::SET_FRAME_RATE, data, &reply);
if (err != NO_ERROR) {
ALOGE("setFrameRate: failed to transact: %s (%d)", strerror(-err), err);
return err;
@@ -1179,7 +1100,7 @@
return reply.readInt32();
}
- virtual status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) {
+ status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) override {
if (!outToken) return BAD_VALUE;
Parcel data, reply;
@@ -1213,6 +1134,68 @@
return NO_ERROR;
}
+
+ status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface,
+ const FrameTimelineInfo& frameTimelineInfo) override {
+ Parcel data, reply;
+ status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (err != NO_ERROR) {
+ ALOGE("%s: failed writing interface token: %s (%d)", __func__, strerror(-err), -err);
+ return err;
+ }
+
+ err = data.writeStrongBinder(IInterface::asBinder(surface));
+ if (err != NO_ERROR) {
+ ALOGE("%s: failed writing strong binder: %s (%d)", __func__, strerror(-err), -err);
+ return err;
+ }
+
+ SAFE_PARCEL(frameTimelineInfo.write, data);
+
+ err = remote()->transact(BnSurfaceComposer::SET_FRAME_TIMELINE_INFO, data, &reply);
+ if (err != NO_ERROR) {
+ ALOGE("%s: failed to transact: %s (%d)", __func__, strerror(-err), err);
+ return err;
+ }
+
+ return reply.readInt32();
+ }
+
+ status_t addTransactionTraceListener(
+ const sp<gui::ITransactionTraceListener>& listener) override {
+ Parcel data, reply;
+ SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+ SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener));
+
+ return remote()->transact(BnSurfaceComposer::ADD_TRANSACTION_TRACE_LISTENER, data, &reply);
+ }
+
+ /**
+ * Get priority of the RenderEngine in surface flinger.
+ */
+ int getGPUContextPriority() override {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ status_t err =
+ remote()->transact(BnSurfaceComposer::GET_GPU_CONTEXT_PRIORITY, data, &reply);
+ if (err != NO_ERROR) {
+ ALOGE("getGPUContextPriority failed to read data: %s (%d)", strerror(-err), err);
+ return 0;
+ }
+ return reply.readInt32();
+ }
+
+ status_t getExtraBufferCount(int* extraBuffers) const override {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ status_t err = remote()->transact(BnSurfaceComposer::GET_EXTRA_BUFFER_COUNT, data, &reply);
+ if (err != NO_ERROR) {
+ ALOGE("getExtraBufferCount failed to read data: %s (%d)", strerror(-err), err);
+ return err;
+ }
+
+ return reply.readInt32(extraBuffers);
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -1236,135 +1219,98 @@
case SET_TRANSACTION_STATE: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- size_t count = data.readUint32();
- if (count > data.dataSize()) {
- return BAD_VALUE;
- }
+ FrameTimelineInfo frameTimelineInfo;
+ SAFE_PARCEL(frameTimelineInfo.read, data);
+
+ uint32_t count = 0;
+ SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize());
Vector<ComposerState> state;
state.setCapacity(count);
for (size_t i = 0; i < count; i++) {
ComposerState s;
- if (s.read(data) == BAD_VALUE) {
- return BAD_VALUE;
- }
+ SAFE_PARCEL(s.read, data);
state.add(s);
}
- count = data.readUint32();
- if (count > data.dataSize()) {
- return BAD_VALUE;
- }
+ SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize());
DisplayState d;
Vector<DisplayState> displays;
displays.setCapacity(count);
for (size_t i = 0; i < count; i++) {
- if (d.read(data) == BAD_VALUE) {
- return BAD_VALUE;
- }
+ SAFE_PARCEL(d.read, data);
displays.add(d);
}
- uint32_t stateFlags = data.readUint32();
- sp<IBinder> applyToken = data.readStrongBinder();
+ uint32_t stateFlags = 0;
+ SAFE_PARCEL(data.readUint32, &stateFlags);
+ sp<IBinder> applyToken;
+ SAFE_PARCEL(data.readStrongBinder, &applyToken);
InputWindowCommands inputWindowCommands;
- inputWindowCommands.read(data);
+ SAFE_PARCEL(inputWindowCommands.read, data);
- int64_t desiredPresentTime = data.readInt64();
+ int64_t desiredPresentTime = 0;
+ bool isAutoTimestamp = true;
+ SAFE_PARCEL(data.readInt64, &desiredPresentTime);
+ SAFE_PARCEL(data.readBool, &isAutoTimestamp);
client_cache_t uncachedBuffer;
- uncachedBuffer.token = data.readStrongBinder();
- uncachedBuffer.id = data.readUint64();
+ sp<IBinder> tmpBinder;
+ SAFE_PARCEL(data.readNullableStrongBinder, &tmpBinder);
+ uncachedBuffer.token = tmpBinder;
+ SAFE_PARCEL(data.readUint64, &uncachedBuffer.id);
- bool hasListenerCallbacks = data.readBool();
+ bool hasListenerCallbacks = false;
+ SAFE_PARCEL(data.readBool, &hasListenerCallbacks);
std::vector<ListenerCallbacks> listenerCallbacks;
- int32_t listenersSize = data.readInt32();
+ int32_t listenersSize = 0;
+ SAFE_PARCEL_READ_SIZE(data.readInt32, &listenersSize, data.dataSize());
for (int32_t i = 0; i < listenersSize; i++) {
- auto listener = data.readStrongBinder();
+ SAFE_PARCEL(data.readStrongBinder, &tmpBinder);
std::vector<CallbackId> callbackIds;
- data.readInt64Vector(&callbackIds);
- listenerCallbacks.emplace_back(listener, callbackIds);
+ SAFE_PARCEL(data.readParcelableVector, &callbackIds);
+ listenerCallbacks.emplace_back(tmpBinder, callbackIds);
}
- setTransactionState(state, displays, stateFlags, applyToken, inputWindowCommands,
- desiredPresentTime, uncachedBuffer, hasListenerCallbacks,
- listenerCallbacks);
- return NO_ERROR;
+
+ uint64_t transactionId = -1;
+ SAFE_PARCEL(data.readUint64, &transactionId);
+
+ return setTransactionState(frameTimelineInfo, state, displays, stateFlags, applyToken,
+ inputWindowCommands, desiredPresentTime, isAutoTimestamp,
+ uncachedBuffer, hasListenerCallbacks, listenerCallbacks,
+ transactionId);
}
case BOOT_FINISHED: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
bootFinished();
return NO_ERROR;
}
- case CAPTURE_SCREEN: {
+ case CAPTURE_DISPLAY: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- sp<IBinder> display = data.readStrongBinder();
- ui::Dataspace reqDataspace = static_cast<ui::Dataspace>(data.readInt32());
- ui::PixelFormat reqPixelFormat = static_cast<ui::PixelFormat>(data.readInt32());
- sp<GraphicBuffer> outBuffer;
- Rect sourceCrop(Rect::EMPTY_RECT);
- data.read(sourceCrop);
- uint32_t reqWidth = data.readUint32();
- uint32_t reqHeight = data.readUint32();
- bool useIdentityTransform = static_cast<bool>(data.readInt32());
- int32_t rotation = data.readInt32();
- bool captureSecureLayers = static_cast<bool>(data.readInt32());
+ DisplayCaptureArgs args;
+ sp<IScreenCaptureListener> captureListener;
+ SAFE_PARCEL(args.read, data);
+ SAFE_PARCEL(data.readStrongBinder, &captureListener);
- bool capturedSecureLayers = false;
- status_t res = captureScreen(display, &outBuffer, capturedSecureLayers, reqDataspace,
- reqPixelFormat, sourceCrop, reqWidth, reqHeight,
- useIdentityTransform, ui::toRotation(rotation),
- captureSecureLayers);
-
- reply->writeInt32(res);
- if (res == NO_ERROR) {
- reply->write(*outBuffer);
- reply->writeBool(capturedSecureLayers);
- }
- return NO_ERROR;
+ return captureDisplay(args, captureListener);
}
- case CAPTURE_SCREEN_BY_ID: {
+ case CAPTURE_DISPLAY_BY_ID: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- uint64_t displayOrLayerStack = data.readUint64();
- ui::Dataspace outDataspace = ui::Dataspace::V0_SRGB;
- sp<GraphicBuffer> outBuffer;
- status_t res = captureScreen(displayOrLayerStack, &outDataspace, &outBuffer);
- reply->writeInt32(res);
- if (res == NO_ERROR) {
- reply->writeInt32(static_cast<int32_t>(outDataspace));
- reply->write(*outBuffer);
- }
- return NO_ERROR;
+ uint64_t displayOrLayerStack = 0;
+ sp<IScreenCaptureListener> captureListener;
+ SAFE_PARCEL(data.readUint64, &displayOrLayerStack);
+ SAFE_PARCEL(data.readStrongBinder, &captureListener);
+
+ return captureDisplay(displayOrLayerStack, captureListener);
}
case CAPTURE_LAYERS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- sp<IBinder> layerHandleBinder = data.readStrongBinder();
- ui::Dataspace reqDataspace = static_cast<ui::Dataspace>(data.readInt32());
- ui::PixelFormat reqPixelFormat = static_cast<ui::PixelFormat>(data.readInt32());
- sp<GraphicBuffer> outBuffer;
- Rect sourceCrop(Rect::EMPTY_RECT);
- data.read(sourceCrop);
+ LayerCaptureArgs args;
+ sp<IScreenCaptureListener> captureListener;
+ SAFE_PARCEL(args.read, data);
+ SAFE_PARCEL(data.readStrongBinder, &captureListener);
- std::unordered_set<sp<IBinder>, SpHash<IBinder>> excludeHandles;
- int numExcludeHandles = data.readInt32();
- if (numExcludeHandles >= static_cast<int>(MAX_LAYERS)) {
- return BAD_VALUE;
- }
- excludeHandles.reserve(numExcludeHandles);
- for (int i = 0; i < numExcludeHandles; i++) {
- excludeHandles.emplace(data.readStrongBinder());
- }
-
- float frameScale = data.readFloat();
- bool childrenOnly = data.readBool();
-
- status_t res =
- captureLayers(layerHandleBinder, &outBuffer, reqDataspace, reqPixelFormat,
- sourceCrop, excludeHandles, frameScale, childrenOnly);
- reply->writeInt32(res);
- if (res == NO_ERROR) {
- reply->write(*outBuffer);
- }
- return NO_ERROR;
+ return captureLayers(args, captureListener);
}
case AUTHENTICATE_SURFACE: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
@@ -1396,19 +1342,22 @@
case CREATE_DISPLAY_EVENT_CONNECTION: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
auto vsyncSource = static_cast<ISurfaceComposer::VsyncSource>(data.readInt32());
- auto configChanged = static_cast<ISurfaceComposer::ConfigChanged>(data.readInt32());
+ EventRegistrationFlags eventRegistration =
+ static_cast<EventRegistration>(data.readUint32());
sp<IDisplayEventConnection> connection(
- createDisplayEventConnection(vsyncSource, configChanged));
+ createDisplayEventConnection(vsyncSource, eventRegistration));
reply->writeStrongBinder(IInterface::asBinder(connection));
return NO_ERROR;
}
case CREATE_DISPLAY: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- String8 displayName = data.readString8();
- bool secure = bool(data.readInt32());
- sp<IBinder> display(createDisplay(displayName, secure));
- reply->writeStrongBinder(display);
+ String8 displayName;
+ SAFE_PARCEL(data.readString8, &displayName);
+ bool secure = false;
+ SAFE_PARCEL(data.readBool, &secure);
+ sp<IBinder> display = createDisplay(displayName, secure);
+ SAFE_PARCEL(reply->writeStrongBinder, display);
return NO_ERROR;
}
case DESTROY_DISPLAY: {
@@ -1419,7 +1368,7 @@
}
case GET_PHYSICAL_DISPLAY_TOKEN: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- PhysicalDisplayId displayId = data.readUint64();
+ PhysicalDisplayId displayId(data.readUint64());
sp<IBinder> display = getPhysicalDisplayToken(displayId);
reply->writeStrongBinder(display);
return NO_ERROR;
@@ -1436,30 +1385,24 @@
}
return NO_ERROR;
}
- case GET_DISPLAY_INFO: {
+ case GET_STATIC_DISPLAY_INFO: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- DisplayInfo info;
+ ui::StaticDisplayInfo info;
const sp<IBinder> display = data.readStrongBinder();
- const status_t result = getDisplayInfo(display, &info);
- reply->writeInt32(result);
- if (result == NO_ERROR) {
- memcpy(reply->writeInplace(sizeof(DisplayInfo)), &info, sizeof(DisplayInfo));
- }
+ const status_t result = getStaticDisplayInfo(display, &info);
+ SAFE_PARCEL(reply->writeInt32, result);
+ if (result != NO_ERROR) return result;
+ SAFE_PARCEL(reply->write, info);
return NO_ERROR;
}
- case GET_DISPLAY_CONFIGS: {
+ case GET_DYNAMIC_DISPLAY_INFO: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- Vector<DisplayConfig> configs;
+ ui::DynamicDisplayInfo info;
const sp<IBinder> display = data.readStrongBinder();
- const status_t result = getDisplayConfigs(display, &configs);
- reply->writeInt32(result);
- if (result == NO_ERROR) {
- reply->writeUint32(static_cast<uint32_t>(configs.size()));
- for (size_t c = 0; c < configs.size(); ++c) {
- memcpy(reply->writeInplace(sizeof(DisplayConfig)), &configs[c],
- sizeof(DisplayConfig));
- }
- }
+ const status_t result = getDynamicDisplayInfo(display, &info);
+ SAFE_PARCEL(reply->writeInt32, result);
+ if (result != NO_ERROR) return result;
+ SAFE_PARCEL(reply->write, info);
return NO_ERROR;
}
case GET_DISPLAY_STATS: {
@@ -1474,32 +1417,6 @@
}
return NO_ERROR;
}
- case GET_ACTIVE_CONFIG: {
- CHECK_INTERFACE(ISurfaceComposer, data, reply);
- sp<IBinder> display = data.readStrongBinder();
- int id = getActiveConfig(display);
- reply->writeInt32(id);
- return NO_ERROR;
- }
- case GET_DISPLAY_COLOR_MODES: {
- CHECK_INTERFACE(ISurfaceComposer, data, reply);
- Vector<ColorMode> colorModes;
- sp<IBinder> display = nullptr;
- status_t result = data.readStrongBinder(&display);
- if (result != NO_ERROR) {
- ALOGE("getDisplayColorModes failed to readStrongBinder: %d", result);
- return result;
- }
- result = getDisplayColorModes(display, &colorModes);
- reply->writeInt32(result);
- if (result == NO_ERROR) {
- reply->writeUint32(static_cast<uint32_t>(colorModes.size()));
- for (size_t i = 0; i < colorModes.size(); ++i) {
- reply->writeInt32(static_cast<int32_t>(colorModes[i]));
- }
- }
- return NO_ERROR;
- }
case GET_DISPLAY_NATIVE_PRIMARIES: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
ui::DisplayPrimaries primaries;
@@ -1520,18 +1437,6 @@
return NO_ERROR;
}
- case GET_ACTIVE_COLOR_MODE: {
- CHECK_INTERFACE(ISurfaceComposer, data, reply);
- sp<IBinder> display = nullptr;
- status_t result = data.readStrongBinder(&display);
- if (result != NO_ERROR) {
- ALOGE("getActiveColorMode failed to readStrongBinder: %d", result);
- return result;
- }
- ColorMode colorMode = getActiveColorMode(display);
- result = reply->writeInt32(static_cast<int32_t>(colorMode));
- return result;
- }
case SET_ACTIVE_COLOR_MODE: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> display = nullptr;
@@ -1551,23 +1456,6 @@
result = reply->writeInt32(result);
return result;
}
-
- case GET_AUTO_LOW_LATENCY_MODE_SUPPORT: {
- CHECK_INTERFACE(ISurfaceComposer, data, reply);
- sp<IBinder> display = nullptr;
- status_t result = data.readStrongBinder(&display);
- if (result != NO_ERROR) {
- ALOGE("getAutoLowLatencyModeSupport failed to readStrongBinder: %d", result);
- return result;
- }
- bool supported = false;
- result = getAutoLowLatencyModeSupport(display, &supported);
- if (result == NO_ERROR) {
- result = reply->writeBool(supported);
- }
- return result;
- }
-
case SET_AUTO_LOW_LATENCY_MODE: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> display = nullptr;
@@ -1585,23 +1473,6 @@
setAutoLowLatencyMode(display, setAllm);
return result;
}
-
- case GET_GAME_CONTENT_TYPE_SUPPORT: {
- CHECK_INTERFACE(ISurfaceComposer, data, reply);
- sp<IBinder> display = nullptr;
- status_t result = data.readStrongBinder(&display);
- if (result != NO_ERROR) {
- ALOGE("getGameContentTypeSupport failed to readStrongBinder: %d", result);
- return result;
- }
- bool supported = false;
- result = getGameContentTypeSupport(display, &supported);
- if (result == NO_ERROR) {
- result = reply->writeBool(supported);
- }
- return result;
- }
-
case SET_GAME_CONTENT_TYPE: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> display = nullptr;
@@ -1619,7 +1490,6 @@
setGameContentType(display, setGameContentTypeOn);
return result;
}
-
case CLEAR_ANIMATION_FRAME_STATS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
status_t result = clearAnimationFrameStats();
@@ -1641,23 +1511,6 @@
setPowerMode(display, mode);
return NO_ERROR;
}
- case GET_HDR_CAPABILITIES: {
- CHECK_INTERFACE(ISurfaceComposer, data, reply);
- sp<IBinder> display = nullptr;
- status_t result = data.readStrongBinder(&display);
- if (result != NO_ERROR) {
- ALOGE("getHdrCapabilities failed to readStrongBinder: %d",
- result);
- return result;
- }
- HdrCapabilities capabilities;
- result = getHdrCapabilities(display, &capabilities);
- reply->writeInt32(result);
- if (result == NO_ERROR) {
- reply->write(capabilities);
- }
- return NO_ERROR;
- }
case ENABLE_VSYNC_INJECTIONS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
bool enable = false;
@@ -1823,7 +1676,11 @@
}
case GET_PHYSICAL_DISPLAY_IDS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- return reply->writeUint64Vector(getPhysicalDisplayIds());
+ std::vector<PhysicalDisplayId> ids = getPhysicalDisplayIds();
+ std::vector<uint64_t> rawIds(ids.size());
+ std::transform(ids.begin(), ids.end(), rawIds.begin(),
+ [](PhysicalDisplayId id) { return id.value; });
+ return reply->writeUint64Vector(rawIds);
}
case ADD_REGION_SAMPLING_LISTENER: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
@@ -1857,49 +1714,84 @@
}
return removeRegionSamplingListener(listener);
}
- case SET_DESIRED_DISPLAY_CONFIG_SPECS: {
+ case ADD_FPS_LISTENER: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ int32_t taskId;
+ status_t result = data.readInt32(&taskId);
+ if (result != NO_ERROR) {
+ ALOGE("addFpsListener: Failed to read layer handle");
+ return result;
+ }
+ sp<gui::IFpsListener> listener;
+ result = data.readNullableStrongBinder(&listener);
+ if (result != NO_ERROR) {
+ ALOGE("addFpsListener: Failed to read listener");
+ return result;
+ }
+ return addFpsListener(taskId, listener);
+ }
+ case REMOVE_FPS_LISTENER: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<gui::IFpsListener> listener;
+ status_t result = data.readNullableStrongBinder(&listener);
+ if (result != NO_ERROR) {
+ ALOGE("removeFpsListener: Failed to read listener");
+ return result;
+ }
+ return removeFpsListener(listener);
+ }
+ case SET_DESIRED_DISPLAY_MODE_SPECS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> displayToken = data.readStrongBinder();
- int32_t defaultConfig;
- status_t result = data.readInt32(&defaultConfig);
+ ui::DisplayModeId defaultMode;
+ status_t result = data.readInt32(&defaultMode);
if (result != NO_ERROR) {
- ALOGE("setDesiredDisplayConfigSpecs: failed to read defaultConfig: %d", result);
+ ALOGE("setDesiredDisplayModeSpecs: failed to read defaultMode: %d", result);
+ return result;
+ }
+ if (defaultMode < 0) {
+ ALOGE("%s: defaultMode must be non-negative but it was %d", __func__, defaultMode);
+ return BAD_VALUE;
+ }
+ bool allowGroupSwitching;
+ result = data.readBool(&allowGroupSwitching);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayModeSpecs: failed to read allowGroupSwitching: %d", result);
return result;
}
float primaryRefreshRateMin;
result = data.readFloat(&primaryRefreshRateMin);
if (result != NO_ERROR) {
- ALOGE("setDesiredDisplayConfigSpecs: failed to read primaryRefreshRateMin: %d",
+ ALOGE("setDesiredDisplayModeSpecs: failed to read primaryRefreshRateMin: %d",
result);
return result;
}
float primaryRefreshRateMax;
result = data.readFloat(&primaryRefreshRateMax);
if (result != NO_ERROR) {
- ALOGE("setDesiredDisplayConfigSpecs: failed to read primaryRefreshRateMax: %d",
+ ALOGE("setDesiredDisplayModeSpecs: failed to read primaryRefreshRateMax: %d",
result);
return result;
}
float appRequestRefreshRateMin;
result = data.readFloat(&appRequestRefreshRateMin);
if (result != NO_ERROR) {
- ALOGE("setDesiredDisplayConfigSpecs: failed to read appRequestRefreshRateMin: %d",
+ ALOGE("setDesiredDisplayModeSpecs: failed to read appRequestRefreshRateMin: %d",
result);
return result;
}
float appRequestRefreshRateMax;
result = data.readFloat(&appRequestRefreshRateMax);
if (result != NO_ERROR) {
- ALOGE("setDesiredDisplayConfigSpecs: failed to read appRequestRefreshRateMax: %d",
+ ALOGE("setDesiredDisplayModeSpecs: failed to read appRequestRefreshRateMax: %d",
result);
return result;
}
- result =
- setDesiredDisplayConfigSpecs(displayToken, defaultConfig, primaryRefreshRateMin,
- primaryRefreshRateMax, appRequestRefreshRateMin,
- appRequestRefreshRateMax);
+ result = setDesiredDisplayModeSpecs(displayToken, defaultMode, allowGroupSwitching,
+ primaryRefreshRateMin, primaryRefreshRateMax,
+ appRequestRefreshRateMin, appRequestRefreshRateMax);
if (result != NO_ERROR) {
- ALOGE("setDesiredDisplayConfigSpecs: failed to call setDesiredDisplayConfigSpecs: "
+ ALOGE("setDesiredDisplayModeSpecs: failed to call setDesiredDisplayModeSpecs: "
"%d",
result);
return result;
@@ -1907,53 +1799,60 @@
reply->writeInt32(result);
return result;
}
- case GET_DESIRED_DISPLAY_CONFIG_SPECS: {
+ case GET_DESIRED_DISPLAY_MODE_SPECS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> displayToken = data.readStrongBinder();
- int32_t defaultConfig;
+ ui::DisplayModeId defaultMode;
+ bool allowGroupSwitching;
float primaryRefreshRateMin;
float primaryRefreshRateMax;
float appRequestRefreshRateMin;
float appRequestRefreshRateMax;
status_t result =
- getDesiredDisplayConfigSpecs(displayToken, &defaultConfig,
- &primaryRefreshRateMin, &primaryRefreshRateMax,
- &appRequestRefreshRateMin,
- &appRequestRefreshRateMax);
+ getDesiredDisplayModeSpecs(displayToken, &defaultMode, &allowGroupSwitching,
+ &primaryRefreshRateMin, &primaryRefreshRateMax,
+ &appRequestRefreshRateMin,
+ &appRequestRefreshRateMax);
if (result != NO_ERROR) {
- ALOGE("getDesiredDisplayConfigSpecs: failed to get getDesiredDisplayConfigSpecs: "
+ ALOGE("getDesiredDisplayModeSpecs: failed to get getDesiredDisplayModeSpecs: "
"%d",
result);
return result;
}
- result = reply->writeInt32(defaultConfig);
+ result = reply->writeInt32(defaultMode);
if (result != NO_ERROR) {
- ALOGE("getDesiredDisplayConfigSpecs: failed to write defaultConfig: %d", result);
+ ALOGE("getDesiredDisplayModeSpecs: failed to write defaultMode: %d", result);
+ return result;
+ }
+ result = reply->writeBool(allowGroupSwitching);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayModeSpecs: failed to write allowGroupSwitching: %d",
+ result);
return result;
}
result = reply->writeFloat(primaryRefreshRateMin);
if (result != NO_ERROR) {
- ALOGE("getDesiredDisplayConfigSpecs: failed to write primaryRefreshRateMin: %d",
+ ALOGE("getDesiredDisplayModeSpecs: failed to write primaryRefreshRateMin: %d",
result);
return result;
}
result = reply->writeFloat(primaryRefreshRateMax);
if (result != NO_ERROR) {
- ALOGE("getDesiredDisplayConfigSpecs: failed to write primaryRefreshRateMax: %d",
+ ALOGE("getDesiredDisplayModeSpecs: failed to write primaryRefreshRateMax: %d",
result);
return result;
}
result = reply->writeFloat(appRequestRefreshRateMin);
if (result != NO_ERROR) {
- ALOGE("getDesiredDisplayConfigSpecs: failed to write appRequestRefreshRateMin: %d",
+ ALOGE("getDesiredDisplayModeSpecs: failed to write appRequestRefreshRateMin: %d",
result);
return result;
}
result = reply->writeFloat(appRequestRefreshRateMax);
if (result != NO_ERROR) {
- ALOGE("getDesiredDisplayConfigSpecs: failed to write appRequestRefreshRateMax: %d",
+ ALOGE("getDesiredDisplayModeSpecs: failed to write appRequestRefreshRateMax: %d",
result);
return result;
}
@@ -1981,23 +1880,55 @@
ALOGE("setDisplayBrightness: failed to read display token: %d", error);
return error;
}
- float brightness = -1.0f;
- error = data.readFloat(&brightness);
+ gui::DisplayBrightness brightness;
+ error = data.readParcelable(&brightness);
if (error != NO_ERROR) {
ALOGE("setDisplayBrightness: failed to read brightness: %d", error);
return error;
}
return setDisplayBrightness(displayToken, brightness);
}
- case NOTIFY_POWER_HINT: {
+ case ADD_HDR_LAYER_INFO_LISTENER: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- int32_t hintId;
- status_t error = data.readInt32(&hintId);
+ sp<IBinder> displayToken;
+ status_t error = data.readNullableStrongBinder(&displayToken);
if (error != NO_ERROR) {
- ALOGE("notifyPowerHint: failed to read hintId: %d", error);
+ ALOGE("addHdrLayerInfoListener: Failed to read display token");
return error;
}
- return notifyPowerHint(hintId);
+ sp<gui::IHdrLayerInfoListener> listener;
+ error = data.readNullableStrongBinder(&listener);
+ if (error != NO_ERROR) {
+ ALOGE("addHdrLayerInfoListener: Failed to read listener");
+ return error;
+ }
+ return addHdrLayerInfoListener(displayToken, listener);
+ }
+ case REMOVE_HDR_LAYER_INFO_LISTENER: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> displayToken;
+ status_t error = data.readNullableStrongBinder(&displayToken);
+ if (error != NO_ERROR) {
+ ALOGE("removeHdrLayerInfoListener: Failed to read display token");
+ return error;
+ }
+ sp<gui::IHdrLayerInfoListener> listener;
+ error = data.readNullableStrongBinder(&listener);
+ if (error != NO_ERROR) {
+ ALOGE("removeHdrLayerInfoListener: Failed to read listener");
+ return error;
+ }
+ return removeHdrLayerInfoListener(displayToken, listener);
+ }
+ case NOTIFY_POWER_BOOST: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ int32_t boostId;
+ status_t error = data.readInt32(&boostId);
+ if (error != NO_ERROR) {
+ ALOGE("notifyPowerBoost: failed to read boostId: %d", error);
+ return error;
+ }
+ return notifyPowerBoost(boostId);
}
case SET_GLOBAL_SHADOW_SETTINGS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
@@ -2021,30 +1952,24 @@
case SET_FRAME_RATE: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> binder;
- status_t err = data.readStrongBinder(&binder);
- if (err != NO_ERROR) {
- ALOGE("setFrameRate: failed to read strong binder: %s (%d)", strerror(-err), -err);
- return err;
- }
+ SAFE_PARCEL(data.readStrongBinder, &binder);
+
sp<IGraphicBufferProducer> surface = interface_cast<IGraphicBufferProducer>(binder);
if (!surface) {
- ALOGE("setFrameRate: failed to cast to IGraphicBufferProducer: %s (%d)",
- strerror(-err), -err);
- return err;
+ ALOGE("setFrameRate: failed to cast to IGraphicBufferProducer");
+ return BAD_VALUE;
}
float frameRate;
- err = data.readFloat(&frameRate);
- if (err != NO_ERROR) {
- ALOGE("setFrameRate: failed to read float: %s (%d)", strerror(-err), -err);
- return err;
- }
+ SAFE_PARCEL(data.readFloat, &frameRate);
+
int8_t compatibility;
- err = data.readByte(&compatibility);
- if (err != NO_ERROR) {
- ALOGE("setFrameRate: failed to read byte: %s (%d)", strerror(-err), -err);
- return err;
- }
- status_t result = setFrameRate(surface, frameRate, compatibility);
+ SAFE_PARCEL(data.readByte, &compatibility);
+
+ int8_t changeFrameRateStrategy;
+ SAFE_PARCEL(data.readByte, &changeFrameRateStrategy);
+
+ status_t result =
+ setFrameRate(surface, frameRate, compatibility, changeFrameRateStrategy);
reply->writeInt32(result);
return NO_ERROR;
}
@@ -2058,6 +1983,79 @@
}
return NO_ERROR;
}
+ case SET_FRAME_TIMELINE_INFO: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> binder;
+ status_t err = data.readStrongBinder(&binder);
+ if (err != NO_ERROR) {
+ ALOGE("setFrameTimelineInfo: failed to read strong binder: %s (%d)", strerror(-err),
+ -err);
+ return err;
+ }
+ sp<IGraphicBufferProducer> surface = interface_cast<IGraphicBufferProducer>(binder);
+ if (!surface) {
+ ALOGE("setFrameTimelineInfo: failed to cast to IGraphicBufferProducer: %s (%d)",
+ strerror(-err), -err);
+ return err;
+ }
+
+ FrameTimelineInfo frameTimelineInfo;
+ SAFE_PARCEL(frameTimelineInfo.read, data);
+
+ status_t result = setFrameTimelineInfo(surface, frameTimelineInfo);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ }
+ case ADD_TRANSACTION_TRACE_LISTENER: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<gui::ITransactionTraceListener> listener;
+ SAFE_PARCEL(data.readStrongBinder, &listener);
+
+ return addTransactionTraceListener(listener);
+ }
+ case GET_GPU_CONTEXT_PRIORITY: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ int priority = getGPUContextPriority();
+ SAFE_PARCEL(reply->writeInt32, priority);
+ return NO_ERROR;
+ }
+ case GET_EXTRA_BUFFER_COUNT: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ int extraBuffers = 0;
+ int err = getExtraBufferCount(&extraBuffers);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ SAFE_PARCEL(reply->writeInt32, extraBuffers);
+ return NO_ERROR;
+ }
+ case OVERRIDE_HDR_TYPES: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> display = nullptr;
+ SAFE_PARCEL(data.readStrongBinder, &display);
+
+ std::vector<int32_t> hdrTypes;
+ SAFE_PARCEL(data.readInt32Vector, &hdrTypes);
+
+ std::vector<ui::Hdr> hdrTypesVector;
+ for (int i : hdrTypes) {
+ hdrTypesVector.push_back(static_cast<ui::Hdr>(i));
+ }
+ return overrideHdrTypes(display, hdrTypesVector);
+ }
+ case ON_PULL_ATOM: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ int32_t atomId = 0;
+ SAFE_PARCEL(data.readInt32, &atomId);
+
+ std::string pulledData;
+ bool success;
+ status_t err = onPullAtom(atomId, &pulledData, &success);
+ SAFE_PARCEL(reply->writeByteArray, pulledData.size(),
+ reinterpret_cast<const uint8_t*>(pulledData.data()));
+ SAFE_PARCEL(reply->writeBool, success);
+ return err;
+ }
default: {
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp
index 621cf59..5e7a7ec 100644
--- a/libs/gui/ISurfaceComposerClient.cpp
+++ b/libs/gui/ISurfaceComposerClient.cpp
@@ -50,12 +50,12 @@
status_t createSurface(const String8& name, uint32_t width, uint32_t height, PixelFormat format,
uint32_t flags, const sp<IBinder>& parent, LayerMetadata metadata,
sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp,
- uint32_t* outTransformHint) override {
+ int32_t* outLayerId, uint32_t* outTransformHint) override {
return callRemote<decltype(&ISurfaceComposerClient::createSurface)>(Tag::CREATE_SURFACE,
name, width, height,
format, flags, parent,
std::move(metadata),
- handle, gbp,
+ handle, gbp, outLayerId,
outTransformHint);
}
@@ -63,14 +63,14 @@
PixelFormat format, uint32_t flags,
const sp<IGraphicBufferProducer>& parent,
LayerMetadata metadata, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp,
+ sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
uint32_t* outTransformHint) override {
return callRemote<decltype(
&ISurfaceComposerClient::createWithSurfaceParent)>(Tag::CREATE_WITH_SURFACE_PARENT,
name, width, height, format,
flags, parent,
std::move(metadata), handle, gbp,
- outTransformHint);
+ outLayerId, outTransformHint);
}
status_t clearLayerFrameStats(const sp<IBinder>& handle) const override {
@@ -85,10 +85,11 @@
outStats);
}
- status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle) override {
+ status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle,
+ int32_t* outLayerId) override {
return callRemote<decltype(&ISurfaceComposerClient::mirrorSurface)>(Tag::MIRROR_SURFACE,
mirrorFromHandle,
- outHandle);
+ outHandle, outLayerId);
}
};
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 69f7894..f74f91e 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -17,7 +17,10 @@
#define LOG_TAG "ITransactionCompletedListener"
//#define LOG_NDEBUG 0
+#include <gui/ISurfaceComposer.h>
#include <gui/ITransactionCompletedListener.h>
+#include <gui/LayerState.h>
+#include <private/gui/ParcelUtils.h>
namespace android {
@@ -25,7 +28,8 @@
enum class Tag : uint32_t {
ON_TRANSACTION_COMPLETED = IBinder::FIRST_CALL_TRANSACTION,
- LAST = ON_TRANSACTION_COMPLETED,
+ ON_RELEASE_BUFFER,
+ LAST = ON_RELEASE_BUFFER,
};
} // Anonymous namespace
@@ -90,65 +94,65 @@
return err;
}
-status_t SurfaceStats::writeToParcel(Parcel* output) const {
- status_t err = output->writeStrongBinder(surfaceControl);
- if (err != NO_ERROR) {
- return err;
- }
- err = output->writeInt64(acquireTime);
- if (err != NO_ERROR) {
- return err;
- }
- if (previousReleaseFence) {
- err = output->writeBool(true);
- if (err != NO_ERROR) {
- return err;
- }
- err = output->write(*previousReleaseFence);
- } else {
- err = output->writeBool(false);
- }
- err = output->writeUint32(transformHint);
- if (err != NO_ERROR) {
- return err;
- }
+JankData::JankData()
+ : frameVsyncId(FrameTimelineInfo::INVALID_VSYNC_ID), jankType(JankType::None) {}
- err = output->writeParcelable(eventStats);
- return err;
+status_t JankData::writeToParcel(Parcel* output) const {
+ SAFE_PARCEL(output->writeInt64, frameVsyncId);
+ SAFE_PARCEL(output->writeInt32, jankType);
+ return NO_ERROR;
+}
+
+status_t JankData::readFromParcel(const Parcel* input) {
+ SAFE_PARCEL(input->readInt64, &frameVsyncId);
+ SAFE_PARCEL(input->readInt32, &jankType);
+ return NO_ERROR;
+}
+
+status_t SurfaceStats::writeToParcel(Parcel* output) const {
+ SAFE_PARCEL(output->writeStrongBinder, surfaceControl);
+ SAFE_PARCEL(output->writeInt64, acquireTime);
+ if (previousReleaseFence) {
+ SAFE_PARCEL(output->writeBool, true);
+ SAFE_PARCEL(output->write, *previousReleaseFence);
+ } else {
+ SAFE_PARCEL(output->writeBool, false);
+ }
+ SAFE_PARCEL(output->writeUint32, transformHint);
+ SAFE_PARCEL(output->writeParcelable, eventStats);
+ SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(jankData.size()));
+ for (const auto& data : jankData) {
+ SAFE_PARCEL(output->writeParcelable, data);
+ }
+ SAFE_PARCEL(output->writeUint64, previousBufferId);
+ return NO_ERROR;
}
status_t SurfaceStats::readFromParcel(const Parcel* input) {
- status_t err = input->readStrongBinder(&surfaceControl);
- if (err != NO_ERROR) {
- return err;
- }
- err = input->readInt64(&acquireTime);
- if (err != NO_ERROR) {
- return err;
- }
+ SAFE_PARCEL(input->readStrongBinder, &surfaceControl);
+ SAFE_PARCEL(input->readInt64, &acquireTime);
bool hasFence = false;
- err = input->readBool(&hasFence);
- if (err != NO_ERROR) {
- return err;
- }
+ SAFE_PARCEL(input->readBool, &hasFence);
if (hasFence) {
previousReleaseFence = new Fence();
- err = input->read(*previousReleaseFence);
- if (err != NO_ERROR) {
- return err;
- }
+ SAFE_PARCEL(input->read, *previousReleaseFence);
}
- err = input->readUint32(&transformHint);
- if (err != NO_ERROR) {
- return err;
- }
+ SAFE_PARCEL(input->readUint32, &transformHint);
+ SAFE_PARCEL(input->readParcelable, &eventStats);
- err = input->readParcelable(&eventStats);
- return err;
+ int32_t jankData_size = 0;
+ SAFE_PARCEL_READ_SIZE(input->readInt32, &jankData_size, input->dataSize());
+ for (int i = 0; i < jankData_size; i++) {
+ JankData data;
+ SAFE_PARCEL(input->readParcelable, &data);
+ jankData.push_back(data);
+ }
+ SAFE_PARCEL(input->readUint64, &previousBufferId);
+ return NO_ERROR;
}
status_t TransactionStats::writeToParcel(Parcel* output) const {
- status_t err = output->writeInt64Vector(callbackIds);
+ status_t err = output->writeParcelableVector(callbackIds);
if (err != NO_ERROR) {
return err;
}
@@ -172,7 +176,7 @@
}
status_t TransactionStats::readFromParcel(const Parcel* input) {
- status_t err = input->readInt64Vector(&callbackIds);
+ status_t err = input->readParcelableVector(&callbackIds);
if (err != NO_ERROR) {
return err;
}
@@ -223,8 +227,9 @@
return NO_ERROR;
}
-ListenerStats ListenerStats::createEmpty(const sp<IBinder>& listener,
- const std::unordered_set<CallbackId>& callbackIds) {
+ListenerStats ListenerStats::createEmpty(
+ const sp<IBinder>& listener,
+ const std::unordered_set<CallbackId, CallbackIdHash>& callbackIds) {
ListenerStats listenerStats;
listenerStats.listener = listener;
listenerStats.transactionStats.emplace_back(callbackIds);
@@ -245,6 +250,12 @@
onTransactionCompleted)>(Tag::ON_TRANSACTION_COMPLETED,
stats);
}
+
+ void onReleaseBuffer(uint64_t graphicBufferId, sp<Fence> releaseFence) override {
+ callRemoteAsync<decltype(
+ &ITransactionCompletedListener::onReleaseBuffer)>(Tag::ON_RELEASE_BUFFER,
+ graphicBufferId, releaseFence);
+ }
};
// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
@@ -263,7 +274,33 @@
case Tag::ON_TRANSACTION_COMPLETED:
return callLocalAsync(data, reply,
&ITransactionCompletedListener::onTransactionCompleted);
+ case Tag::ON_RELEASE_BUFFER:
+ return callLocalAsync(data, reply, &ITransactionCompletedListener::onReleaseBuffer);
}
}
+ListenerCallbacks ListenerCallbacks::filter(CallbackId::Type type) const {
+ std::vector<CallbackId> filteredCallbackIds;
+ for (const auto& callbackId : callbackIds) {
+ if (callbackId.type == type) {
+ filteredCallbackIds.push_back(callbackId);
+ }
+ }
+ return ListenerCallbacks(transactionCompletedListener, filteredCallbackIds);
+}
+
+status_t CallbackId::writeToParcel(Parcel* output) const {
+ SAFE_PARCEL(output->writeInt64, id);
+ SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(type));
+ return NO_ERROR;
+}
+
+status_t CallbackId::readFromParcel(const Parcel* input) {
+ SAFE_PARCEL(input->readInt64, &id);
+ int32_t typeAsInt;
+ SAFE_PARCEL(input->readInt32, &typeAsInt);
+ type = static_cast<CallbackId::Type>(typeAsInt);
+ return NO_ERROR;
+}
+
}; // namespace android
diff --git a/libs/gui/LayerDebugInfo.cpp b/libs/gui/LayerDebugInfo.cpp
index cdde9a2..e707684 100644
--- a/libs/gui/LayerDebugInfo.cpp
+++ b/libs/gui/LayerDebugInfo.cpp
@@ -61,6 +61,7 @@
RETURN_ON_ERROR(parcel->writeBool(mRefreshPending));
RETURN_ON_ERROR(parcel->writeBool(mIsOpaque));
RETURN_ON_ERROR(parcel->writeBool(mContentDirty));
+ RETURN_ON_ERROR(parcel->write(mStretchEffect));
return NO_ERROR;
}
@@ -105,6 +106,7 @@
RETURN_ON_ERROR(parcel->readBool(&mRefreshPending));
RETURN_ON_ERROR(parcel->readBool(&mIsOpaque));
RETURN_ON_ERROR(parcel->readBool(&mContentDirty));
+ RETURN_ON_ERROR(parcel->read(mStretchEffect));
return NO_ERROR;
}
@@ -115,6 +117,12 @@
info.mTransparentRegion.dump(result, "TransparentRegion");
info.mVisibleRegion.dump(result, "VisibleRegion");
info.mSurfaceDamageRegion.dump(result, "SurfaceDamageRegion");
+ if (info.mStretchEffect.hasEffect()) {
+ const auto& se = info.mStretchEffect;
+ StringAppendF(&result, " StretchEffect area=[%f, %f, %f, %f] vec=(%f, %f) maxAmount=%f\n",
+ se.area.left, se.area.top, se.area.right, se.area.bottom, se.vectorX,
+ se.vectorY, se.maxAmount);
+ }
StringAppendF(&result, " layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), ",
info.mLayerStack, info.mZ, static_cast<double>(info.mX),
diff --git a/libs/gui/LayerMetadata.cpp b/libs/gui/LayerMetadata.cpp
index b3eb994..634d8b7 100644
--- a/libs/gui/LayerMetadata.cpp
+++ b/libs/gui/LayerMetadata.cpp
@@ -17,6 +17,7 @@
#include <android-base/stringprintf.h>
#include <binder/Parcel.h>
#include <gui/LayerMetadata.h>
+#include <inttypes.h>
#include "android/view/LayerMetadataKey.h"
@@ -113,6 +114,15 @@
memcpy(data.data(), p.data(), p.dataSize());
}
+std::optional<int64_t> LayerMetadata::getInt64(uint32_t key) const {
+ if (!has(key)) return std::nullopt;
+ const std::vector<uint8_t>& data = mMap.at(key);
+ if (data.size() < sizeof(int64_t)) return std::nullopt;
+ Parcel p;
+ p.setData(data.data(), data.size());
+ return p.readInt64();
+}
+
std::string LayerMetadata::itemToString(uint32_t key, const char* separator) const {
if (!has(key)) return std::string();
switch (static_cast<view::LayerMetadataKey>(key)) {
@@ -122,6 +132,10 @@
return StringPrintf("windowType%s%d", separator, getInt32(key, 0));
case view::LayerMetadataKey::METADATA_TASK_ID:
return StringPrintf("taskId%s%d", separator, getInt32(key, 0));
+ case view::LayerMetadataKey::METADATA_OWNER_PID:
+ return StringPrintf("ownerPID%s%d", separator, getInt32(key, 0));
+ case view::LayerMetadataKey::METADATA_DEQUEUE_TIME:
+ return StringPrintf("dequeueTime%s%" PRId64, separator, *getInt64(key));
default:
return StringPrintf("%d%s%dbytes", key, separator,
static_cast<int>(mMap.at(key).size()));
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 0281279..517b49e 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -16,195 +16,287 @@
#define LOG_TAG "LayerState"
+#include <apex/window.h>
#include <inttypes.h>
-#include <utils/Errors.h>
+#include <android/native_window.h>
#include <binder/Parcel.h>
-#include <gui/ISurfaceComposerClient.h>
#include <gui/IGraphicBufferProducer.h>
+#include <gui/ISurfaceComposerClient.h>
#include <gui/LayerState.h>
+#include <private/gui/ParcelUtils.h>
+#include <utils/Errors.h>
#include <cmath>
namespace android {
+layer_state_t::layer_state_t()
+ : what(0),
+ x(0),
+ y(0),
+ z(0),
+ w(0),
+ h(0),
+ layerStack(0),
+ alpha(0),
+ flags(0),
+ mask(0),
+ reserved(0),
+ cornerRadius(0.0f),
+ backgroundBlurRadius(0),
+ transform(0),
+ transformToDisplayInverse(false),
+ crop(Rect::INVALID_RECT),
+ orientedDisplaySpaceRect(Rect::INVALID_RECT),
+ dataspace(ui::Dataspace::UNKNOWN),
+ surfaceDamageRegion(),
+ api(-1),
+ colorTransform(mat4()),
+ bgColorAlpha(0),
+ bgColorDataspace(ui::Dataspace::UNKNOWN),
+ colorSpaceAgnostic(false),
+ shadowRadius(0.0f),
+ frameRateSelectionPriority(-1),
+ frameRate(0.0f),
+ frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT),
+ changeFrameRateStrategy(ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS),
+ fixedTransformHint(ui::Transform::ROT_INVALID),
+ frameNumber(0),
+ autoRefresh(false),
+ releaseBufferListener(nullptr) {
+ matrix.dsdx = matrix.dtdy = 1.0f;
+ matrix.dsdy = matrix.dtdx = 0.0f;
+ hdrMetadata.validTypes = 0;
+}
+
status_t layer_state_t::write(Parcel& output) const
{
- output.writeStrongBinder(surface);
- output.writeUint64(what);
- output.writeFloat(x);
- output.writeFloat(y);
- output.writeInt32(z);
- output.writeUint32(w);
- output.writeUint32(h);
- output.writeUint32(layerStack);
- output.writeFloat(alpha);
- output.writeUint32(flags);
- output.writeUint32(mask);
- *reinterpret_cast<layer_state_t::matrix22_t *>(
- output.writeInplace(sizeof(layer_state_t::matrix22_t))) = matrix;
- output.write(crop_legacy);
- output.writeStrongBinder(barrierHandle_legacy);
- output.writeStrongBinder(reparentHandle);
- output.writeUint64(frameNumber_legacy);
- output.writeInt32(overrideScalingMode);
- output.writeStrongBinder(IInterface::asBinder(barrierGbp_legacy));
- output.writeStrongBinder(relativeLayerHandle);
- output.writeStrongBinder(parentHandleForChild);
- output.writeFloat(color.r);
- output.writeFloat(color.g);
- output.writeFloat(color.b);
+ SAFE_PARCEL(output.writeStrongBinder, surface);
+ SAFE_PARCEL(output.writeInt32, layerId);
+ SAFE_PARCEL(output.writeUint64, what);
+ SAFE_PARCEL(output.writeFloat, x);
+ SAFE_PARCEL(output.writeFloat, y);
+ SAFE_PARCEL(output.writeInt32, z);
+ SAFE_PARCEL(output.writeUint32, w);
+ SAFE_PARCEL(output.writeUint32, h);
+ SAFE_PARCEL(output.writeUint32, layerStack);
+ SAFE_PARCEL(output.writeFloat, alpha);
+ SAFE_PARCEL(output.writeUint32, flags);
+ SAFE_PARCEL(output.writeUint32, mask);
+ SAFE_PARCEL(matrix.write, output);
+ SAFE_PARCEL(output.write, crop);
+ SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, reparentSurfaceControl);
+ SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, relativeLayerSurfaceControl);
+ SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, parentSurfaceControlForChild);
+ SAFE_PARCEL(output.writeFloat, color.r);
+ SAFE_PARCEL(output.writeFloat, color.g);
+ SAFE_PARCEL(output.writeFloat, color.b);
#ifndef NO_INPUT
- inputInfo.write(output);
+ SAFE_PARCEL(inputHandle->writeToParcel, &output);
#endif
- output.write(transparentRegion);
- output.writeUint32(transform);
- output.writeBool(transformToDisplayInverse);
- output.write(crop);
- output.write(frame);
+ SAFE_PARCEL(output.write, transparentRegion);
+ SAFE_PARCEL(output.writeUint32, transform);
+ SAFE_PARCEL(output.writeBool, transformToDisplayInverse);
+ SAFE_PARCEL(output.write, crop);
+ SAFE_PARCEL(output.write, orientedDisplaySpaceRect);
+
if (buffer) {
- output.writeBool(true);
- output.write(*buffer);
+ SAFE_PARCEL(output.writeBool, true);
+ SAFE_PARCEL(output.write, *buffer);
} else {
- output.writeBool(false);
+ SAFE_PARCEL(output.writeBool, false);
}
+
if (acquireFence) {
- output.writeBool(true);
- output.write(*acquireFence);
+ SAFE_PARCEL(output.writeBool, true);
+ SAFE_PARCEL(output.write, *acquireFence);
} else {
- output.writeBool(false);
+ SAFE_PARCEL(output.writeBool, false);
}
- output.writeUint32(static_cast<uint32_t>(dataspace));
- output.write(hdrMetadata);
- output.write(surfaceDamageRegion);
- output.writeInt32(api);
+
+ SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(dataspace));
+ SAFE_PARCEL(output.write, hdrMetadata);
+ SAFE_PARCEL(output.write, surfaceDamageRegion);
+ SAFE_PARCEL(output.writeInt32, api);
+
if (sidebandStream) {
- output.writeBool(true);
- output.writeNativeHandle(sidebandStream->handle());
+ SAFE_PARCEL(output.writeBool, true);
+ SAFE_PARCEL(output.writeNativeHandle, sidebandStream->handle());
} else {
- output.writeBool(false);
+ SAFE_PARCEL(output.writeBool, false);
}
- memcpy(output.writeInplace(16 * sizeof(float)),
- colorTransform.asArray(), 16 * sizeof(float));
- output.writeFloat(cornerRadius);
- output.writeUint32(backgroundBlurRadius);
- output.writeStrongBinder(cachedBuffer.token.promote());
- output.writeUint64(cachedBuffer.id);
- output.writeParcelable(metadata);
-
- output.writeFloat(bgColorAlpha);
- output.writeUint32(static_cast<uint32_t>(bgColorDataspace));
- output.writeBool(colorSpaceAgnostic);
-
- auto err = output.writeVectorSize(listeners);
- if (err) {
- return err;
- }
+ SAFE_PARCEL(output.write, colorTransform.asArray(), 16 * sizeof(float));
+ SAFE_PARCEL(output.writeFloat, cornerRadius);
+ SAFE_PARCEL(output.writeUint32, backgroundBlurRadius);
+ SAFE_PARCEL(output.writeStrongBinder, cachedBuffer.token.promote());
+ SAFE_PARCEL(output.writeUint64, cachedBuffer.id);
+ SAFE_PARCEL(output.writeParcelable, metadata);
+ SAFE_PARCEL(output.writeFloat, bgColorAlpha);
+ SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(bgColorDataspace));
+ SAFE_PARCEL(output.writeBool, colorSpaceAgnostic);
+ SAFE_PARCEL(output.writeVectorSize, listeners);
for (auto listener : listeners) {
- err = output.writeStrongBinder(listener.transactionCompletedListener);
- if (err) {
- return err;
- }
- err = output.writeInt64Vector(listener.callbackIds);
- if (err) {
- return err;
- }
+ SAFE_PARCEL(output.writeStrongBinder, listener.transactionCompletedListener);
+ SAFE_PARCEL(output.writeParcelableVector, listener.callbackIds);
}
- output.writeFloat(shadowRadius);
- output.writeInt32(frameRateSelectionPriority);
- output.writeFloat(frameRate);
- output.writeByte(frameRateCompatibility);
- output.writeUint32(fixedTransformHint);
+ SAFE_PARCEL(output.writeFloat, shadowRadius);
+ SAFE_PARCEL(output.writeInt32, frameRateSelectionPriority);
+ SAFE_PARCEL(output.writeFloat, frameRate);
+ SAFE_PARCEL(output.writeByte, frameRateCompatibility);
+ SAFE_PARCEL(output.writeByte, changeFrameRateStrategy);
+ SAFE_PARCEL(output.writeUint32, fixedTransformHint);
+ SAFE_PARCEL(output.writeUint64, frameNumber);
+ SAFE_PARCEL(output.writeBool, autoRefresh);
+ SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(releaseBufferListener));
+
+ SAFE_PARCEL(output.writeUint32, blurRegions.size());
+ for (auto region : blurRegions) {
+ SAFE_PARCEL(output.writeUint32, region.blurRadius);
+ SAFE_PARCEL(output.writeFloat, region.cornerRadiusTL);
+ SAFE_PARCEL(output.writeFloat, region.cornerRadiusTR);
+ SAFE_PARCEL(output.writeFloat, region.cornerRadiusBL);
+ SAFE_PARCEL(output.writeFloat, region.cornerRadiusBR);
+ SAFE_PARCEL(output.writeFloat, region.alpha);
+ SAFE_PARCEL(output.writeInt32, region.left);
+ SAFE_PARCEL(output.writeInt32, region.top);
+ SAFE_PARCEL(output.writeInt32, region.right);
+ SAFE_PARCEL(output.writeInt32, region.bottom);
+ }
+
+ SAFE_PARCEL(output.write, stretchEffect);
+
return NO_ERROR;
}
status_t layer_state_t::read(const Parcel& input)
{
- surface = input.readStrongBinder();
- what = input.readUint64();
- x = input.readFloat();
- y = input.readFloat();
- z = input.readInt32();
- w = input.readUint32();
- h = input.readUint32();
- layerStack = input.readUint32();
- alpha = input.readFloat();
- flags = static_cast<uint8_t>(input.readUint32());
- mask = static_cast<uint8_t>(input.readUint32());
- const void* matrix_data = input.readInplace(sizeof(layer_state_t::matrix22_t));
- if (matrix_data) {
- matrix = *reinterpret_cast<layer_state_t::matrix22_t const *>(matrix_data);
- } else {
- return BAD_VALUE;
- }
- input.read(crop_legacy);
- barrierHandle_legacy = input.readStrongBinder();
- reparentHandle = input.readStrongBinder();
- frameNumber_legacy = input.readUint64();
- overrideScalingMode = input.readInt32();
- barrierGbp_legacy = interface_cast<IGraphicBufferProducer>(input.readStrongBinder());
- relativeLayerHandle = input.readStrongBinder();
- parentHandleForChild = input.readStrongBinder();
- color.r = input.readFloat();
- color.g = input.readFloat();
- color.b = input.readFloat();
+ SAFE_PARCEL(input.readNullableStrongBinder, &surface);
+ SAFE_PARCEL(input.readInt32, &layerId);
+ SAFE_PARCEL(input.readUint64, &what);
+ SAFE_PARCEL(input.readFloat, &x);
+ SAFE_PARCEL(input.readFloat, &y);
+ SAFE_PARCEL(input.readInt32, &z);
+ SAFE_PARCEL(input.readUint32, &w);
+ SAFE_PARCEL(input.readUint32, &h);
+ SAFE_PARCEL(input.readUint32, &layerStack);
+ SAFE_PARCEL(input.readFloat, &alpha);
+ SAFE_PARCEL(input.readUint32, &flags);
+
+ SAFE_PARCEL(input.readUint32, &mask);
+
+ SAFE_PARCEL(matrix.read, input);
+ SAFE_PARCEL(input.read, crop);
+ SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &reparentSurfaceControl);
+
+ SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &relativeLayerSurfaceControl);
+ SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &parentSurfaceControlForChild);
+
+ float tmpFloat = 0;
+ SAFE_PARCEL(input.readFloat, &tmpFloat);
+ color.r = tmpFloat;
+ SAFE_PARCEL(input.readFloat, &tmpFloat);
+ color.g = tmpFloat;
+ SAFE_PARCEL(input.readFloat, &tmpFloat);
+ color.b = tmpFloat;
#ifndef NO_INPUT
- inputInfo = InputWindowInfo::read(input);
+ SAFE_PARCEL(inputHandle->readFromParcel, &input);
#endif
- input.read(transparentRegion);
- transform = input.readUint32();
- transformToDisplayInverse = input.readBool();
- input.read(crop);
- input.read(frame);
- buffer = new GraphicBuffer();
- if (input.readBool()) {
- input.read(*buffer);
+ SAFE_PARCEL(input.read, transparentRegion);
+ SAFE_PARCEL(input.readUint32, &transform);
+ SAFE_PARCEL(input.readBool, &transformToDisplayInverse);
+ SAFE_PARCEL(input.read, crop);
+ SAFE_PARCEL(input.read, orientedDisplaySpaceRect);
+
+ bool tmpBool = false;
+ SAFE_PARCEL(input.readBool, &tmpBool);
+ if (tmpBool) {
+ buffer = new GraphicBuffer();
+ SAFE_PARCEL(input.read, *buffer);
}
- acquireFence = new Fence();
- if (input.readBool()) {
- input.read(*acquireFence);
+
+ SAFE_PARCEL(input.readBool, &tmpBool);
+ if (tmpBool) {
+ acquireFence = new Fence();
+ SAFE_PARCEL(input.read, *acquireFence);
}
- dataspace = static_cast<ui::Dataspace>(input.readUint32());
- input.read(hdrMetadata);
- input.read(surfaceDamageRegion);
- api = input.readInt32();
- if (input.readBool()) {
+
+ uint32_t tmpUint32 = 0;
+ SAFE_PARCEL(input.readUint32, &tmpUint32);
+ dataspace = static_cast<ui::Dataspace>(tmpUint32);
+
+ SAFE_PARCEL(input.read, hdrMetadata);
+ SAFE_PARCEL(input.read, surfaceDamageRegion);
+ SAFE_PARCEL(input.readInt32, &api);
+ SAFE_PARCEL(input.readBool, &tmpBool);
+ if (tmpBool) {
sidebandStream = NativeHandle::create(input.readNativeHandle(), true);
}
- const void* color_transform_data = input.readInplace(16 * sizeof(float));
- if (color_transform_data) {
- colorTransform = mat4(static_cast<const float*>(color_transform_data));
- } else {
- return BAD_VALUE;
- }
- cornerRadius = input.readFloat();
- backgroundBlurRadius = input.readUint32();
- cachedBuffer.token = input.readStrongBinder();
- cachedBuffer.id = input.readUint64();
- input.readParcelable(&metadata);
+ SAFE_PARCEL(input.read, &colorTransform, 16 * sizeof(float));
+ SAFE_PARCEL(input.readFloat, &cornerRadius);
+ SAFE_PARCEL(input.readUint32, &backgroundBlurRadius);
+ sp<IBinder> tmpBinder;
+ SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder);
+ cachedBuffer.token = tmpBinder;
+ SAFE_PARCEL(input.readUint64, &cachedBuffer.id);
+ SAFE_PARCEL(input.readParcelable, &metadata);
- bgColorAlpha = input.readFloat();
- bgColorDataspace = static_cast<ui::Dataspace>(input.readUint32());
- colorSpaceAgnostic = input.readBool();
+ SAFE_PARCEL(input.readFloat, &bgColorAlpha);
+ SAFE_PARCEL(input.readUint32, &tmpUint32);
+ bgColorDataspace = static_cast<ui::Dataspace>(tmpUint32);
+ SAFE_PARCEL(input.readBool, &colorSpaceAgnostic);
- int32_t numListeners = input.readInt32();
+ int32_t numListeners = 0;
+ SAFE_PARCEL_READ_SIZE(input.readInt32, &numListeners, input.dataSize());
listeners.clear();
for (int i = 0; i < numListeners; i++) {
- auto listener = input.readStrongBinder();
+ sp<IBinder> listener;
std::vector<CallbackId> callbackIds;
- input.readInt64Vector(&callbackIds);
+ SAFE_PARCEL(input.readNullableStrongBinder, &listener);
+ SAFE_PARCEL(input.readParcelableVector, &callbackIds);
listeners.emplace_back(listener, callbackIds);
}
- shadowRadius = input.readFloat();
- frameRateSelectionPriority = input.readInt32();
- frameRate = input.readFloat();
- frameRateCompatibility = input.readByte();
- fixedTransformHint = static_cast<ui::Transform::RotationFlags>(input.readUint32());
+ SAFE_PARCEL(input.readFloat, &shadowRadius);
+ SAFE_PARCEL(input.readInt32, &frameRateSelectionPriority);
+ SAFE_PARCEL(input.readFloat, &frameRate);
+ SAFE_PARCEL(input.readByte, &frameRateCompatibility);
+ SAFE_PARCEL(input.readByte, &changeFrameRateStrategy);
+ SAFE_PARCEL(input.readUint32, &tmpUint32);
+ fixedTransformHint = static_cast<ui::Transform::RotationFlags>(tmpUint32);
+ SAFE_PARCEL(input.readUint64, &frameNumber);
+ SAFE_PARCEL(input.readBool, &autoRefresh);
+
+ tmpBinder = nullptr;
+ SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder);
+ if (tmpBinder) {
+ releaseBufferListener = checked_interface_cast<ITransactionCompletedListener>(tmpBinder);
+ }
+
+ uint32_t numRegions = 0;
+ SAFE_PARCEL(input.readUint32, &numRegions);
+ blurRegions.clear();
+ for (uint32_t i = 0; i < numRegions; i++) {
+ BlurRegion region;
+ SAFE_PARCEL(input.readUint32, ®ion.blurRadius);
+ SAFE_PARCEL(input.readFloat, ®ion.cornerRadiusTL);
+ SAFE_PARCEL(input.readFloat, ®ion.cornerRadiusTR);
+ SAFE_PARCEL(input.readFloat, ®ion.cornerRadiusBL);
+ SAFE_PARCEL(input.readFloat, ®ion.cornerRadiusBR);
+ SAFE_PARCEL(input.readFloat, ®ion.alpha);
+ SAFE_PARCEL(input.readInt32, ®ion.left);
+ SAFE_PARCEL(input.readInt32, ®ion.top);
+ SAFE_PARCEL(input.readInt32, ®ion.right);
+ SAFE_PARCEL(input.readInt32, ®ion.bottom);
+ blurRegions.push_back(region);
+ }
+
+ SAFE_PARCEL(input.read, stretchEffect);
+
return NO_ERROR;
}
@@ -216,39 +308,43 @@
return state.read(input);
}
-
-DisplayState::DisplayState() :
- what(0),
- layerStack(0),
- viewport(Rect::EMPTY_RECT),
- frame(Rect::EMPTY_RECT),
- width(0),
- height(0) {
-}
+DisplayState::DisplayState()
+ : what(0),
+ layerStack(0),
+ layerStackSpaceRect(Rect::EMPTY_RECT),
+ orientedDisplaySpaceRect(Rect::EMPTY_RECT),
+ width(0),
+ height(0) {}
status_t DisplayState::write(Parcel& output) const {
- output.writeStrongBinder(token);
- output.writeStrongBinder(IInterface::asBinder(surface));
- output.writeUint32(what);
- output.writeUint32(layerStack);
- output.writeUint32(toRotationInt(orientation));
- output.write(viewport);
- output.write(frame);
- output.writeUint32(width);
- output.writeUint32(height);
+ SAFE_PARCEL(output.writeStrongBinder, token);
+ SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(surface));
+ SAFE_PARCEL(output.writeUint32, what);
+ SAFE_PARCEL(output.writeUint32, layerStack);
+ SAFE_PARCEL(output.writeUint32, toRotationInt(orientation));
+ SAFE_PARCEL(output.write, layerStackSpaceRect);
+ SAFE_PARCEL(output.write, orientedDisplaySpaceRect);
+ SAFE_PARCEL(output.writeUint32, width);
+ SAFE_PARCEL(output.writeUint32, height);
return NO_ERROR;
}
status_t DisplayState::read(const Parcel& input) {
- token = input.readStrongBinder();
- surface = interface_cast<IGraphicBufferProducer>(input.readStrongBinder());
- what = input.readUint32();
- layerStack = input.readUint32();
- orientation = ui::toRotation(input.readUint32());
- input.read(viewport);
- input.read(frame);
- width = input.readUint32();
- height = input.readUint32();
+ SAFE_PARCEL(input.readStrongBinder, &token);
+ sp<IBinder> tmpBinder;
+ SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder);
+ surface = interface_cast<IGraphicBufferProducer>(tmpBinder);
+
+ SAFE_PARCEL(input.readUint32, &what);
+ SAFE_PARCEL(input.readUint32, &layerStack);
+ uint32_t tmpUint = 0;
+ SAFE_PARCEL(input.readUint32, &tmpUint);
+ orientation = ui::toRotation(tmpUint);
+
+ SAFE_PARCEL(input.read, layerStackSpaceRect);
+ SAFE_PARCEL(input.read, orientedDisplaySpaceRect);
+ SAFE_PARCEL(input.readUint32, &width);
+ SAFE_PARCEL(input.readUint32, &height);
return NO_ERROR;
}
@@ -264,8 +360,8 @@
if (other.what & eDisplayProjectionChanged) {
what |= eDisplayProjectionChanged;
orientation = other.orientation;
- viewport = other.viewport;
- frame = other.frame;
+ layerStackSpaceRect = other.layerStackSpaceRect;
+ orientedDisplaySpaceRect = other.orientedDisplaySpaceRect;
}
if (other.what & eDisplaySizeChanged) {
what |= eDisplaySizeChanged;
@@ -312,10 +408,6 @@
what |= eLayerStackChanged;
layerStack = other.layerStack;
}
- if (other.what & eCropChanged_legacy) {
- what |= eCropChanged_legacy;
- crop_legacy = other.crop_legacy;
- }
if (other.what & eCornerRadiusChanged) {
what |= eCornerRadiusChanged;
cornerRadius = other.cornerRadius;
@@ -324,32 +416,19 @@
what |= eBackgroundBlurRadiusChanged;
backgroundBlurRadius = other.backgroundBlurRadius;
}
- if (other.what & eDeferTransaction_legacy) {
- what |= eDeferTransaction_legacy;
- barrierHandle_legacy = other.barrierHandle_legacy;
- barrierGbp_legacy = other.barrierGbp_legacy;
- frameNumber_legacy = other.frameNumber_legacy;
- }
- if (other.what & eOverrideScalingModeChanged) {
- what |= eOverrideScalingModeChanged;
- overrideScalingMode = other.overrideScalingMode;
- }
- if (other.what & eReparentChildren) {
- what |= eReparentChildren;
- reparentHandle = other.reparentHandle;
- }
- if (other.what & eDetachChildren) {
- what |= eDetachChildren;
+ if (other.what & eBlurRegionsChanged) {
+ what |= eBlurRegionsChanged;
+ blurRegions = other.blurRegions;
}
if (other.what & eRelativeLayerChanged) {
what |= eRelativeLayerChanged;
what &= ~eLayerChanged;
z = other.z;
- relativeLayerHandle = other.relativeLayerHandle;
+ relativeLayerSurfaceControl = other.relativeLayerSurfaceControl;
}
if (other.what & eReparent) {
what |= eReparent;
- parentHandleForChild = other.parentHandleForChild;
+ parentSurfaceControlForChild = other.parentSurfaceControlForChild;
}
if (other.what & eDestroySurface) {
what |= eDestroySurface;
@@ -366,10 +445,6 @@
what |= eCropChanged;
crop = other.crop;
}
- if (other.what & eFrameChanged) {
- what |= eFrameChanged;
- frame = other.frame;
- }
if (other.what & eBufferChanged) {
what |= eBufferChanged;
buffer = other.buffer;
@@ -409,7 +484,7 @@
#ifndef NO_INPUT
if (other.what & eInputInfoChanged) {
what |= eInputInfoChanged;
- inputInfo = other.inputInfo;
+ inputHandle = new InputWindowHandle(*other.inputHandle);
}
#endif
@@ -439,11 +514,31 @@
what |= eFrameRateChanged;
frameRate = other.frameRate;
frameRateCompatibility = other.frameRateCompatibility;
+ changeFrameRateStrategy = other.changeFrameRateStrategy;
}
if (other.what & eFixedTransformHintChanged) {
what |= eFixedTransformHintChanged;
fixedTransformHint = other.fixedTransformHint;
}
+ if (other.what & eFrameNumberChanged) {
+ what |= eFrameNumberChanged;
+ frameNumber = other.frameNumber;
+ }
+ if (other.what & eAutoRefreshChanged) {
+ what |= eAutoRefreshChanged;
+ autoRefresh = other.autoRefresh;
+ }
+ if (other.what & eReleaseBufferListenerChanged) {
+ if (releaseBufferListener) {
+ ALOGW("Overriding releaseBufferListener");
+ }
+ what |= eReleaseBufferListenerChanged;
+ releaseBufferListener = other.releaseBufferListener;
+ }
+ if (other.what & eStretchChanged) {
+ what |= eStretchChanged;
+ stretchEffect = other.stretchEffect;
+ }
if ((other.what & what) != other.what) {
ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
"other.what=0x%" PRIu64 " what=0x%" PRIu64,
@@ -451,25 +546,73 @@
}
}
+bool layer_state_t::hasBufferChanges() const {
+ return (what & layer_state_t::eBufferChanged) || (what & layer_state_t::eCachedBufferChanged);
+}
+
+status_t layer_state_t::matrix22_t::write(Parcel& output) const {
+ SAFE_PARCEL(output.writeFloat, dsdx);
+ SAFE_PARCEL(output.writeFloat, dtdx);
+ SAFE_PARCEL(output.writeFloat, dtdy);
+ SAFE_PARCEL(output.writeFloat, dsdy);
+ return NO_ERROR;
+}
+
+status_t layer_state_t::matrix22_t::read(const Parcel& input) {
+ SAFE_PARCEL(input.readFloat, &dsdx);
+ SAFE_PARCEL(input.readFloat, &dtdx);
+ SAFE_PARCEL(input.readFloat, &dtdy);
+ SAFE_PARCEL(input.readFloat, &dsdy);
+ return NO_ERROR;
+}
+
// ------------------------------- InputWindowCommands ----------------------------------------
-void InputWindowCommands::merge(const InputWindowCommands& other) {
+bool InputWindowCommands::merge(const InputWindowCommands& other) {
+ bool changes = false;
+#ifndef NO_INPUT
+ changes |= !other.focusRequests.empty();
+ focusRequests.insert(focusRequests.end(), std::make_move_iterator(other.focusRequests.begin()),
+ std::make_move_iterator(other.focusRequests.end()));
+#endif
+ changes |= other.syncInputWindows && !syncInputWindows;
syncInputWindows |= other.syncInputWindows;
+ return changes;
+}
+
+bool InputWindowCommands::empty() const {
+ bool empty = true;
+#ifndef NO_INPUT
+ empty = focusRequests.empty() && !syncInputWindows;
+#endif
+ return empty;
}
void InputWindowCommands::clear() {
+#ifndef NO_INPUT
+ focusRequests.clear();
+#endif
syncInputWindows = false;
}
-void InputWindowCommands::write(Parcel& output) const {
- output.writeBool(syncInputWindows);
+status_t InputWindowCommands::write(Parcel& output) const {
+#ifndef NO_INPUT
+ SAFE_PARCEL(output.writeParcelableVector, focusRequests);
+#endif
+ SAFE_PARCEL(output.writeBool, syncInputWindows);
+ return NO_ERROR;
}
-void InputWindowCommands::read(const Parcel& input) {
- syncInputWindows = input.readBool();
+status_t InputWindowCommands::read(const Parcel& input) {
+#ifndef NO_INPUT
+ SAFE_PARCEL(input.readParcelableVector, &focusRequests);
+#endif
+ SAFE_PARCEL(input.readBool, &syncInputWindows);
+ return NO_ERROR;
}
-bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* inFunctionName) {
+bool ValidateFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy,
+ const char* inFunctionName, bool privileged) {
const char* functionName = inFunctionName != nullptr ? inFunctionName : "call";
int floatClassification = std::fpclassify(frameRate);
if (frameRate < 0 || floatClassification == FP_INFINITE || floatClassification == FP_NAN) {
@@ -478,12 +621,101 @@
}
if (compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT &&
- compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE) {
- ALOGE("%s failed - invalid compatibility value %d", functionName, compatibility);
+ compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE &&
+ (!privileged || compatibility != ANATIVEWINDOW_FRAME_RATE_EXACT)) {
+ ALOGE("%s failed - invalid compatibility value %d privileged: %s", functionName,
+ compatibility, privileged ? "yes" : "no");
return false;
}
+ if (changeFrameRateStrategy != ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS &&
+ changeFrameRateStrategy != ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS) {
+ ALOGE("%s failed - invalid change frame rate strategy value %d", functionName,
+ changeFrameRateStrategy);
+ }
+
return true;
}
+// ----------------------------------------------------------------------------
+
+status_t CaptureArgs::write(Parcel& output) const {
+ SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(pixelFormat));
+ SAFE_PARCEL(output.write, sourceCrop);
+ SAFE_PARCEL(output.writeFloat, frameScaleX);
+ SAFE_PARCEL(output.writeFloat, frameScaleY);
+ SAFE_PARCEL(output.writeBool, captureSecureLayers);
+ SAFE_PARCEL(output.writeInt32, uid);
+ SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(dataspace));
+ SAFE_PARCEL(output.writeBool, allowProtected);
+ SAFE_PARCEL(output.writeBool, grayscale);
+ return NO_ERROR;
+}
+
+status_t CaptureArgs::read(const Parcel& input) {
+ int32_t value = 0;
+ SAFE_PARCEL(input.readInt32, &value);
+ pixelFormat = static_cast<ui::PixelFormat>(value);
+ SAFE_PARCEL(input.read, sourceCrop);
+ SAFE_PARCEL(input.readFloat, &frameScaleX);
+ SAFE_PARCEL(input.readFloat, &frameScaleY);
+ SAFE_PARCEL(input.readBool, &captureSecureLayers);
+ SAFE_PARCEL(input.readInt32, &uid);
+ SAFE_PARCEL(input.readInt32, &value);
+ dataspace = static_cast<ui::Dataspace>(value);
+ SAFE_PARCEL(input.readBool, &allowProtected);
+ SAFE_PARCEL(input.readBool, &grayscale);
+ return NO_ERROR;
+}
+
+status_t DisplayCaptureArgs::write(Parcel& output) const {
+ SAFE_PARCEL(CaptureArgs::write, output);
+
+ SAFE_PARCEL(output.writeStrongBinder, displayToken);
+ SAFE_PARCEL(output.writeUint32, width);
+ SAFE_PARCEL(output.writeUint32, height);
+ SAFE_PARCEL(output.writeBool, useIdentityTransform);
+ return NO_ERROR;
+}
+
+status_t DisplayCaptureArgs::read(const Parcel& input) {
+ SAFE_PARCEL(CaptureArgs::read, input);
+
+ SAFE_PARCEL(input.readStrongBinder, &displayToken);
+ SAFE_PARCEL(input.readUint32, &width);
+ SAFE_PARCEL(input.readUint32, &height);
+ SAFE_PARCEL(input.readBool, &useIdentityTransform);
+ return NO_ERROR;
+}
+
+status_t LayerCaptureArgs::write(Parcel& output) const {
+ SAFE_PARCEL(CaptureArgs::write, output);
+
+ SAFE_PARCEL(output.writeStrongBinder, layerHandle);
+ SAFE_PARCEL(output.writeInt32, excludeHandles.size());
+ for (auto el : excludeHandles) {
+ SAFE_PARCEL(output.writeStrongBinder, el);
+ }
+ SAFE_PARCEL(output.writeBool, childrenOnly);
+ return NO_ERROR;
+}
+
+status_t LayerCaptureArgs::read(const Parcel& input) {
+ SAFE_PARCEL(CaptureArgs::read, input);
+
+ SAFE_PARCEL(input.readStrongBinder, &layerHandle);
+
+ int32_t numExcludeHandles = 0;
+ SAFE_PARCEL_READ_SIZE(input.readInt32, &numExcludeHandles, input.dataSize());
+ excludeHandles.reserve(numExcludeHandles);
+ for (int i = 0; i < numExcludeHandles; i++) {
+ sp<IBinder> binder;
+ SAFE_PARCEL(input.readStrongBinder, &binder);
+ excludeHandles.emplace(binder);
+ }
+
+ SAFE_PARCEL(input.readBool, &childrenOnly);
+ return NO_ERROR;
+}
+
}; // namespace android
diff --git a/libs/gui/QueueBufferInputOutput.cpp b/libs/gui/QueueBufferInputOutput.cpp
deleted file mode 100644
index 30f0ef6..0000000
--- a/libs/gui/QueueBufferInputOutput.cpp
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright 2010 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>
-
-#define LOG_TAG "QueueBufferInputOutput"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-//#define LOG_NDEBUG 0
-
-#include <gui/IGraphicBufferProducer.h>
-
-namespace android {
-
-constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() {
- return sizeof(timestamp) +
- sizeof(isAutoTimestamp) +
- sizeof(dataSpace) +
- sizeof(crop) +
- sizeof(scalingMode) +
- sizeof(transform) +
- sizeof(stickyTransform) +
- sizeof(getFrameTimestamps);
-}
-
-IGraphicBufferProducer::QueueBufferInput::QueueBufferInput(const Parcel& parcel) {
- parcel.read(*this);
-}
-
-size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const {
- return minFlattenedSize() +
- fence->getFlattenedSize() +
- surfaceDamage.getFlattenedSize() +
- hdrMetadata.getFlattenedSize();
-}
-
-size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const {
- return fence->getFdCount();
-}
-
-status_t IGraphicBufferProducer::QueueBufferInput::flatten(
- void*& buffer, size_t& size, int*& fds, size_t& count) const
-{
- if (size < getFlattenedSize()) {
- return NO_MEMORY;
- }
-
- FlattenableUtils::write(buffer, size, timestamp);
- FlattenableUtils::write(buffer, size, isAutoTimestamp);
- FlattenableUtils::write(buffer, size, dataSpace);
- FlattenableUtils::write(buffer, size, crop);
- FlattenableUtils::write(buffer, size, scalingMode);
- FlattenableUtils::write(buffer, size, transform);
- FlattenableUtils::write(buffer, size, stickyTransform);
- FlattenableUtils::write(buffer, size, getFrameTimestamps);
-
- status_t result = fence->flatten(buffer, size, fds, count);
- if (result != NO_ERROR) {
- return result;
- }
- result = surfaceDamage.flatten(buffer, size);
- if (result != NO_ERROR) {
- return result;
- }
- FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize());
- return hdrMetadata.flatten(buffer, size);
-}
-
-status_t IGraphicBufferProducer::QueueBufferInput::unflatten(
- void const*& buffer, size_t& size, int const*& fds, size_t& count)
-{
- if (size < minFlattenedSize()) {
- return NO_MEMORY;
- }
-
- FlattenableUtils::read(buffer, size, timestamp);
- FlattenableUtils::read(buffer, size, isAutoTimestamp);
- FlattenableUtils::read(buffer, size, dataSpace);
- FlattenableUtils::read(buffer, size, crop);
- FlattenableUtils::read(buffer, size, scalingMode);
- FlattenableUtils::read(buffer, size, transform);
- FlattenableUtils::read(buffer, size, stickyTransform);
- FlattenableUtils::read(buffer, size, getFrameTimestamps);
-
- fence = new Fence();
- status_t result = fence->unflatten(buffer, size, fds, count);
- if (result != NO_ERROR) {
- return result;
- }
- result = surfaceDamage.unflatten(buffer, size);
- if (result != NO_ERROR) {
- return result;
- }
- FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize());
- return hdrMetadata.unflatten(buffer, size);
-}
-
-////////////////////////////////////////////////////////////////////////
-constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() {
- return sizeof(width) + sizeof(height) + sizeof(transformHint) + sizeof(numPendingBuffers) +
- sizeof(nextFrameNumber) + sizeof(bufferReplaced) + sizeof(maxBufferCount);
-}
-size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const {
- return minFlattenedSize() + frameTimestamps.getFlattenedSize();
-}
-
-size_t IGraphicBufferProducer::QueueBufferOutput::getFdCount() const {
- return frameTimestamps.getFdCount();
-}
-
-status_t IGraphicBufferProducer::QueueBufferOutput::flatten(
- void*& buffer, size_t& size, int*& fds, size_t& count) const
-{
- if (size < getFlattenedSize()) {
- return NO_MEMORY;
- }
-
- FlattenableUtils::write(buffer, size, width);
- FlattenableUtils::write(buffer, size, height);
- FlattenableUtils::write(buffer, size, transformHint);
- FlattenableUtils::write(buffer, size, numPendingBuffers);
- FlattenableUtils::write(buffer, size, nextFrameNumber);
- FlattenableUtils::write(buffer, size, bufferReplaced);
- FlattenableUtils::write(buffer, size, maxBufferCount);
-
- return frameTimestamps.flatten(buffer, size, fds, count);
-}
-
-status_t IGraphicBufferProducer::QueueBufferOutput::unflatten(
- void const*& buffer, size_t& size, int const*& fds, size_t& count)
-{
- if (size < minFlattenedSize()) {
- return NO_MEMORY;
- }
-
- FlattenableUtils::read(buffer, size, width);
- FlattenableUtils::read(buffer, size, height);
- FlattenableUtils::read(buffer, size, transformHint);
- FlattenableUtils::read(buffer, size, numPendingBuffers);
- FlattenableUtils::read(buffer, size, nextFrameNumber);
- FlattenableUtils::read(buffer, size, bufferReplaced);
- FlattenableUtils::read(buffer, size, maxBufferCount);
-
- return frameTimestamps.unflatten(buffer, size, fds, count);
-}
-
-} // namespace android
diff --git a/libs/gui/ScreenCaptureResults.cpp b/libs/gui/ScreenCaptureResults.cpp
new file mode 100644
index 0000000..e91f74f
--- /dev/null
+++ b/libs/gui/ScreenCaptureResults.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2021 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 <gui/ScreenCaptureResults.h>
+
+#include <private/gui/ParcelUtils.h>
+
+namespace android::gui {
+
+status_t ScreenCaptureResults::writeToParcel(android::Parcel* parcel) const {
+ if (buffer != nullptr) {
+ SAFE_PARCEL(parcel->writeBool, true);
+ SAFE_PARCEL(parcel->write, *buffer);
+ } else {
+ SAFE_PARCEL(parcel->writeBool, false);
+ }
+
+ if (fence != Fence::NO_FENCE) {
+ SAFE_PARCEL(parcel->writeBool, true);
+ SAFE_PARCEL(parcel->write, *fence);
+ } else {
+ SAFE_PARCEL(parcel->writeBool, false);
+ }
+
+ SAFE_PARCEL(parcel->writeBool, capturedSecureLayers);
+ SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(capturedDataspace));
+ SAFE_PARCEL(parcel->writeInt32, result);
+ return NO_ERROR;
+}
+
+status_t ScreenCaptureResults::readFromParcel(const android::Parcel* parcel) {
+ bool hasGraphicBuffer;
+ SAFE_PARCEL(parcel->readBool, &hasGraphicBuffer);
+ if (hasGraphicBuffer) {
+ buffer = new GraphicBuffer();
+ SAFE_PARCEL(parcel->read, *buffer);
+ }
+
+ bool hasFence;
+ SAFE_PARCEL(parcel->readBool, &hasFence);
+ if (hasFence) {
+ fence = new Fence();
+ SAFE_PARCEL(parcel->read, *fence);
+ }
+
+ SAFE_PARCEL(parcel->readBool, &capturedSecureLayers);
+ uint32_t dataspace = 0;
+ SAFE_PARCEL(parcel->readUint32, &dataspace);
+ capturedDataspace = static_cast<ui::Dataspace>(dataspace);
+ SAFE_PARCEL(parcel->readInt32, &result);
+ return NO_ERROR;
+}
+
+} // namespace android::gui
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index d6f9e63..2fc9d47 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -34,9 +34,9 @@
#include <utils/NativeHandle.h>
#include <ui/DisplayStatInfo.h>
+#include <ui/DynamicDisplayInfo.h>
#include <ui/Fence.h>
#include <ui/GraphicBuffer.h>
-#include <ui/HdrCapabilities.h>
#include <ui/Region.h>
#include <gui/BufferItem.h>
@@ -48,7 +48,6 @@
namespace android {
-using ui::ColorMode;
using ui::Dataspace;
namespace {
@@ -63,7 +62,8 @@
} // namespace
-Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp)
+Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp,
+ const sp<IBinder>& surfaceControlHandle)
: mGraphicBufferProducer(bufferProducer),
mCrop(Rect::EMPTY_RECT),
mBufferAge(0),
@@ -111,6 +111,7 @@
mProducerControlledByApp = controlledByApp;
mSwapIntervalZero = false;
mMaxBufferCount = NUM_BUFFER_SLOTS;
+ mSurfaceControlHandle = surfaceControlHandle;
}
Surface::~Surface() {
@@ -359,15 +360,12 @@
return NAME_NOT_FOUND;
}
- HdrCapabilities hdrCapabilities;
- status_t err =
- composerService()->getHdrCapabilities(display, &hdrCapabilities);
-
- if (err)
+ ui::DynamicDisplayInfo info;
+ if (status_t err = composerService()->getDynamicDisplayInfo(display, &info); err != NO_ERROR) {
return err;
+ }
- *supported = !hdrCapabilities.getSupportedHdrTypes().empty();
-
+ *supported = !info.hdrCapabilities.getSupportedHdrTypes().empty();
return NO_ERROR;
}
@@ -616,29 +614,31 @@
std::mutex mMutex;
};
+void Surface::getDequeueBufferInputLocked(
+ IGraphicBufferProducer::DequeueBufferInput* dequeueInput) {
+ LOG_ALWAYS_FATAL_IF(dequeueInput == nullptr, "input is null");
+
+ dequeueInput->width = mReqWidth ? mReqWidth : mUserWidth;
+ dequeueInput->height = mReqHeight ? mReqHeight : mUserHeight;
+
+ dequeueInput->format = mReqFormat;
+ dequeueInput->usage = mReqUsage;
+
+ dequeueInput->getTimestamps = mEnableFrameTimestamps;
+}
+
int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
ATRACE_CALL();
ALOGV("Surface::dequeueBuffer");
- uint32_t reqWidth;
- uint32_t reqHeight;
- PixelFormat reqFormat;
- uint64_t reqUsage;
- bool enableFrameTimestamps;
-
+ IGraphicBufferProducer::DequeueBufferInput dqInput;
{
Mutex::Autolock lock(mMutex);
if (mReportRemovedBuffers) {
mRemovedBuffers.clear();
}
- reqWidth = mReqWidth ? mReqWidth : mUserWidth;
- reqHeight = mReqHeight ? mReqHeight : mUserHeight;
-
- reqFormat = mReqFormat;
- reqUsage = mReqUsage;
-
- enableFrameTimestamps = mEnableFrameTimestamps;
+ getDequeueBufferInputLocked(&dqInput);
if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot !=
BufferItem::INVALID_BUFFER_SLOT) {
@@ -656,16 +656,17 @@
nsecs_t startTime = systemTime();
FrameEventHistoryDelta frameTimestamps;
- status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth, reqHeight,
- reqFormat, reqUsage, &mBufferAge,
- enableFrameTimestamps ? &frameTimestamps
- : nullptr);
+ status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, dqInput.width,
+ dqInput.height, dqInput.format,
+ dqInput.usage, &mBufferAge,
+ dqInput.getTimestamps ?
+ &frameTimestamps : nullptr);
mLastDequeueDuration = systemTime() - startTime;
if (result < 0) {
ALOGV("dequeueBuffer: IGraphicBufferProducer::dequeueBuffer"
"(%d, %d, %d, %#" PRIx64 ") failed: %d",
- reqWidth, reqHeight, reqFormat, reqUsage, result);
+ dqInput.width, dqInput.height, dqInput.format, dqInput.usage, result);
return result;
}
@@ -694,7 +695,7 @@
freeAllBuffers();
}
- if (enableFrameTimestamps) {
+ if (dqInput.getTimestamps) {
mFrameEventHistory->applyDelta(frameTimestamps);
}
@@ -737,6 +738,176 @@
return OK;
}
+int Surface::dequeueBuffers(std::vector<BatchBuffer>* buffers) {
+ using DequeueBufferInput = IGraphicBufferProducer::DequeueBufferInput;
+ using DequeueBufferOutput = IGraphicBufferProducer::DequeueBufferOutput;
+ using CancelBufferInput = IGraphicBufferProducer::CancelBufferInput;
+ using RequestBufferOutput = IGraphicBufferProducer::RequestBufferOutput;
+
+ ATRACE_CALL();
+ ALOGV("Surface::dequeueBuffers");
+
+ if (buffers->size() == 0) {
+ ALOGE("%s: must dequeue at least 1 buffer!", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ if (mSharedBufferMode) {
+ ALOGE("%s: batch operation is not supported in shared buffer mode!",
+ __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ size_t numBufferRequested = buffers->size();
+ DequeueBufferInput input;
+
+ {
+ Mutex::Autolock lock(mMutex);
+ if (mReportRemovedBuffers) {
+ mRemovedBuffers.clear();
+ }
+
+ getDequeueBufferInputLocked(&input);
+ } // Drop the lock so that we can still touch the Surface while blocking in IGBP::dequeueBuffers
+
+ std::vector<DequeueBufferInput> dequeueInput(numBufferRequested, input);
+ std::vector<DequeueBufferOutput> dequeueOutput;
+
+ nsecs_t startTime = systemTime();
+
+ status_t result = mGraphicBufferProducer->dequeueBuffers(dequeueInput, &dequeueOutput);
+
+ mLastDequeueDuration = systemTime() - startTime;
+
+ if (result < 0) {
+ ALOGV("%s: IGraphicBufferProducer::dequeueBuffers"
+ "(%d, %d, %d, %#" PRIx64 ") failed: %d",
+ __FUNCTION__, input.width, input.height, input.format, input.usage, result);
+ return result;
+ }
+
+ std::vector<CancelBufferInput> cancelBufferInputs(numBufferRequested);
+ std::vector<status_t> cancelBufferOutputs;
+ for (size_t i = 0; i < numBufferRequested; i++) {
+ cancelBufferInputs[i].slot = dequeueOutput[i].slot;
+ cancelBufferInputs[i].fence = dequeueOutput[i].fence;
+ }
+
+ for (const auto& output : dequeueOutput) {
+ if (output.result < 0) {
+ mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs);
+ ALOGV("%s: IGraphicBufferProducer::dequeueBuffers"
+ "(%d, %d, %d, %#" PRIx64 ") failed: %d",
+ __FUNCTION__, input.width, input.height, input.format, input.usage,
+ output.result);
+ return output.result;
+ }
+
+ if (output.slot < 0 || output.slot >= NUM_BUFFER_SLOTS) {
+ mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs);
+ ALOGE("%s: IGraphicBufferProducer returned invalid slot number %d",
+ __FUNCTION__, output.slot);
+ android_errorWriteLog(0x534e4554, "36991414"); // SafetyNet logging
+ return FAILED_TRANSACTION;
+ }
+
+ if (input.getTimestamps && !output.timestamps.has_value()) {
+ mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs);
+ ALOGE("%s: no frame timestamp returns!", __FUNCTION__);
+ return FAILED_TRANSACTION;
+ }
+
+ // this should never happen
+ ALOGE_IF(output.fence == nullptr,
+ "%s: received null Fence! slot=%d", __FUNCTION__, output.slot);
+ }
+
+ Mutex::Autolock lock(mMutex);
+
+ // Write this while holding the mutex
+ mLastDequeueStartTime = startTime;
+
+ std::vector<int32_t> requestBufferSlots;
+ requestBufferSlots.reserve(numBufferRequested);
+ // handle release all buffers and request buffers
+ for (const auto& output : dequeueOutput) {
+ if (output.result & IGraphicBufferProducer::RELEASE_ALL_BUFFERS) {
+ ALOGV("%s: RELEASE_ALL_BUFFERS during batch operation", __FUNCTION__);
+ freeAllBuffers();
+ break;
+ }
+ }
+
+ for (const auto& output : dequeueOutput) {
+ // Collect slots that needs requesting buffer
+ sp<GraphicBuffer>& gbuf(mSlots[output.slot].buffer);
+ if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == nullptr) {
+ if (mReportRemovedBuffers && (gbuf != nullptr)) {
+ mRemovedBuffers.push_back(gbuf);
+ }
+ requestBufferSlots.push_back(output.slot);
+ }
+ }
+
+ // Batch request Buffer
+ std::vector<RequestBufferOutput> reqBufferOutput;
+ if (requestBufferSlots.size() > 0) {
+ result = mGraphicBufferProducer->requestBuffers(requestBufferSlots, &reqBufferOutput);
+ if (result != NO_ERROR) {
+ ALOGE("%s: IGraphicBufferProducer::requestBuffers failed: %d",
+ __FUNCTION__, result);
+ mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs);
+ return result;
+ }
+
+ // Check if we have any single failure
+ for (size_t i = 0; i < requestBufferSlots.size(); i++) {
+ if (reqBufferOutput[i].result != OK) {
+ ALOGE("%s: IGraphicBufferProducer::requestBuffers failed at %zu-th buffer, slot %d",
+ __FUNCTION__, i, requestBufferSlots[i]);
+ mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs);
+ return reqBufferOutput[i].result;
+ }
+ }
+
+ // Fill request buffer results to mSlots
+ for (size_t i = 0; i < requestBufferSlots.size(); i++) {
+ mSlots[requestBufferSlots[i]].buffer = reqBufferOutput[i].buffer;
+ }
+ }
+
+ for (size_t batchIdx = 0; batchIdx < numBufferRequested; batchIdx++) {
+ const auto& output = dequeueOutput[batchIdx];
+ int slot = output.slot;
+ sp<GraphicBuffer>& gbuf(mSlots[slot].buffer);
+
+ if (CC_UNLIKELY(atrace_is_tag_enabled(ATRACE_TAG_GRAPHICS))) {
+ static FenceMonitor hwcReleaseThread("HWC release");
+ hwcReleaseThread.queueFence(output.fence);
+ }
+
+ if (input.getTimestamps) {
+ mFrameEventHistory->applyDelta(output.timestamps.value());
+ }
+
+ if (output.fence->isValid()) {
+ buffers->at(batchIdx).fenceFd = output.fence->dup();
+ if (buffers->at(batchIdx).fenceFd == -1) {
+ ALOGE("%s: error duping fence: %d", __FUNCTION__, errno);
+ // dup() should never fail; something is badly wrong. Soldier on
+ // and hope for the best; the worst that should happen is some
+ // visible corruption that lasts until the next frame.
+ }
+ } else {
+ buffers->at(batchIdx).fenceFd = -1;
+ }
+
+ buffers->at(batchIdx).buffer = gbuf.get();
+ mDequeuedSlots.insert(slot);
+ }
+ return OK;
+}
+
int Surface::cancelBuffer(android_native_buffer_t* buffer,
int fenceFd) {
ATRACE_CALL();
@@ -767,15 +938,65 @@
return OK;
}
+int Surface::cancelBuffers(const std::vector<BatchBuffer>& buffers) {
+ using CancelBufferInput = IGraphicBufferProducer::CancelBufferInput;
+ ATRACE_CALL();
+ ALOGV("Surface::cancelBuffers");
+
+ if (mSharedBufferMode) {
+ ALOGE("%s: batch operation is not supported in shared buffer mode!",
+ __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ size_t numBuffers = buffers.size();
+ std::vector<CancelBufferInput> cancelBufferInputs(numBuffers);
+ std::vector<status_t> cancelBufferOutputs;
+ size_t numBuffersCancelled = 0;
+ int badSlotResult = 0;
+ for (size_t i = 0; i < numBuffers; i++) {
+ int slot = getSlotFromBufferLocked(buffers[i].buffer);
+ int fenceFd = buffers[i].fenceFd;
+ if (slot < 0) {
+ if (fenceFd >= 0) {
+ close(fenceFd);
+ }
+ ALOGE("%s: cannot find slot number for cancelled buffer", __FUNCTION__);
+ badSlotResult = slot;
+ } else {
+ sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
+ cancelBufferInputs[numBuffersCancelled].slot = slot;
+ cancelBufferInputs[numBuffersCancelled++].fence = fence;
+ }
+ }
+ cancelBufferInputs.resize(numBuffersCancelled);
+ mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs);
+
+
+ for (size_t i = 0; i < numBuffersCancelled; i++) {
+ mDequeuedSlots.erase(cancelBufferInputs[i].slot);
+ }
+
+ if (badSlotResult != 0) {
+ return badSlotResult;
+ }
+ return OK;
+}
+
int Surface::getSlotFromBufferLocked(
android_native_buffer_t* buffer) const {
+ if (buffer == nullptr) {
+ ALOGE("%s: input buffer is null!", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
if (mSlots[i].buffer != nullptr &&
mSlots[i].buffer->handle == buffer->handle) {
return i;
}
}
- ALOGE("getSlotFromBufferLocked: unknown buffer: %p", buffer->handle);
+ ALOGE("%s: unknown buffer: %p", __FUNCTION__, buffer->handle);
return BAD_VALUE;
}
@@ -785,42 +1006,22 @@
return OK;
}
-int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
- ATRACE_CALL();
- ALOGV("Surface::queueBuffer");
- Mutex::Autolock lock(mMutex);
- int64_t timestamp;
+void Surface::getQueueBufferInputLocked(android_native_buffer_t* buffer, int fenceFd,
+ nsecs_t timestamp, IGraphicBufferProducer::QueueBufferInput* out) {
bool isAutoTimestamp = false;
- if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) {
+ if (timestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) {
timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
isAutoTimestamp = true;
ALOGV("Surface::queueBuffer making up timestamp: %.2f ms",
timestamp / 1000000.0);
- } else {
- timestamp = mTimestamp;
}
- int i = getSlotFromBufferLocked(buffer);
- if (i < 0) {
- if (fenceFd >= 0) {
- close(fenceFd);
- }
- return i;
- }
- if (mSharedBufferSlot == i && mSharedBufferHasBeenQueued) {
- if (fenceFd >= 0) {
- close(fenceFd);
- }
- return OK;
- }
-
// Make sure the crop rectangle is entirely inside the buffer.
Rect crop(Rect::EMPTY_RECT);
mCrop.intersect(Rect(buffer->width, buffer->height), &crop);
sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
- IGraphicBufferProducer::QueueBufferOutput output;
IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp,
static_cast<android_dataspace>(mDataSpace), crop, mScalingMode,
mTransform ^ mStickyTransform, fence, mStickyTransform,
@@ -891,15 +1092,12 @@
input.setSurfaceDamage(flippedRegion);
}
+ *out = input;
+}
- nsecs_t now = systemTime();
- status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);
- mLastQueueDuration = systemTime() - now;
- if (err != OK) {
- ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
- }
-
- mDequeuedSlots.erase(i);
+void Surface::onBufferQueuedLocked(int slot, sp<Fence> fence,
+ const IGraphicBufferProducer::QueueBufferOutput& output) {
+ mDequeuedSlots.erase(slot);
if (mEnableFrameTimestamps) {
mFrameEventHistory->applyDelta(output.frameTimestamps);
@@ -933,7 +1131,7 @@
mDirtyRegion = Region::INVALID_REGION;
}
- if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot == i) {
+ if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot == slot) {
mSharedBufferHasBeenQueued = true;
}
@@ -943,6 +1141,89 @@
static FenceMonitor gpuCompletionThread("GPU completion");
gpuCompletionThread.queueFence(fence);
}
+}
+
+int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
+ ATRACE_CALL();
+ ALOGV("Surface::queueBuffer");
+ Mutex::Autolock lock(mMutex);
+
+ int i = getSlotFromBufferLocked(buffer);
+ if (i < 0) {
+ if (fenceFd >= 0) {
+ close(fenceFd);
+ }
+ return i;
+ }
+ if (mSharedBufferSlot == i && mSharedBufferHasBeenQueued) {
+ if (fenceFd >= 0) {
+ close(fenceFd);
+ }
+ return OK;
+ }
+
+ IGraphicBufferProducer::QueueBufferOutput output;
+ IGraphicBufferProducer::QueueBufferInput input;
+ getQueueBufferInputLocked(buffer, fenceFd, mTimestamp, &input);
+ sp<Fence> fence = input.fence;
+
+ nsecs_t now = systemTime();
+ status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);
+ mLastQueueDuration = systemTime() - now;
+ if (err != OK) {
+ ALOGE("queueBuffer: error queuing buffer, %d", err);
+ }
+
+ onBufferQueuedLocked(i, fence, output);
+ return err;
+}
+
+int Surface::queueBuffers(const std::vector<BatchQueuedBuffer>& buffers) {
+ ATRACE_CALL();
+ ALOGV("Surface::queueBuffers");
+ Mutex::Autolock lock(mMutex);
+
+ if (mSharedBufferMode) {
+ ALOGE("%s: batched operation is not supported in shared buffer mode", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ size_t numBuffers = buffers.size();
+ std::vector<IGraphicBufferProducer::QueueBufferInput> queueBufferInputs(numBuffers);
+ std::vector<IGraphicBufferProducer::QueueBufferOutput> queueBufferOutputs;
+ std::vector<int> bufferSlots(numBuffers, -1);
+ std::vector<sp<Fence>> bufferFences(numBuffers);
+
+ for (size_t batchIdx = 0; batchIdx < numBuffers; batchIdx++) {
+ int i = getSlotFromBufferLocked(buffers[batchIdx].buffer);
+ if (i < 0) {
+ if (buffers[batchIdx].fenceFd >= 0) {
+ close(buffers[batchIdx].fenceFd);
+ }
+ return i;
+ }
+ bufferSlots[batchIdx] = i;
+
+ IGraphicBufferProducer::QueueBufferInput input;
+ getQueueBufferInputLocked(
+ buffers[batchIdx].buffer, buffers[batchIdx].fenceFd, buffers[batchIdx].timestamp,
+ &input);
+ bufferFences[batchIdx] = input.fence;
+ queueBufferInputs[batchIdx] = input;
+ }
+
+ nsecs_t now = systemTime();
+ status_t err = mGraphicBufferProducer->queueBuffers(queueBufferInputs, &queueBufferOutputs);
+ mLastQueueDuration = systemTime() - now;
+ if (err != OK) {
+ ALOGE("%s: error queuing buffer, %d", __FUNCTION__, err);
+ }
+
+
+ for (size_t batchIdx = 0; batchIdx < numBuffers; batchIdx++) {
+ onBufferQueuedLocked(bufferSlots[batchIdx], bufferFences[batchIdx],
+ queueBufferOutputs[batchIdx]);
+ }
return err;
}
@@ -983,6 +1264,10 @@
}
break;
case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: {
+ status_t err = mGraphicBufferProducer->query(what, value);
+ if (err == NO_ERROR) {
+ return NO_ERROR;
+ }
if (composerService()->authenticateSurfaceTexture(
mGraphicBufferProducer)) {
*value = 1;
@@ -1207,6 +1492,12 @@
case NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER:
res = dispatchGetLastQueuedBuffer(args);
break;
+ case NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO:
+ res = dispatchSetFrameTimelineInfo(args);
+ break;
+ case NATIVE_WINDOW_GET_EXTRA_BUFFER_COUNT:
+ res = dispatchGetExtraBufferCount(args);
+ break;
default:
res = NAME_NOT_FOUND;
break;
@@ -1438,7 +1729,8 @@
int Surface::dispatchSetFrameRate(va_list args) {
float frameRate = static_cast<float>(va_arg(args, double));
int8_t compatibility = static_cast<int8_t>(va_arg(args, int));
- return setFrameRate(frameRate, compatibility);
+ int8_t changeFrameRateStrategy = static_cast<int8_t>(va_arg(args, int));
+ return setFrameRate(frameRate, compatibility, changeFrameRateStrategy);
}
int Surface::dispatchAddCancelInterceptor(va_list args) {
@@ -1499,7 +1791,7 @@
int result = mGraphicBufferProducer->getLastQueuedBuffer(&graphicBuffer, &spFence, matrix);
if (graphicBuffer != nullptr) {
- *buffer = reinterpret_cast<AHardwareBuffer*>(graphicBuffer.get());
+ *buffer = graphicBuffer->toAHardwareBuffer();
AHardwareBuffer_acquire(*buffer);
} else {
*buffer = nullptr;
@@ -1513,6 +1805,23 @@
return result;
}
+int Surface::dispatchSetFrameTimelineInfo(va_list args) {
+ ATRACE_CALL();
+ auto frameTimelineVsyncId = static_cast<int64_t>(va_arg(args, int64_t));
+ auto inputEventId = static_cast<int32_t>(va_arg(args, int32_t));
+
+ ALOGV("Surface::%s", __func__);
+ return setFrameTimelineInfo({frameTimelineVsyncId, inputEventId});
+}
+
+int Surface::dispatchGetExtraBufferCount(va_list args) {
+ ATRACE_CALL();
+ auto extraBuffers = static_cast<int*>(va_arg(args, int*));
+
+ ALOGV("Surface::dispatchGetExtraBufferCount");
+ return getExtraBufferCount(extraBuffers);
+}
+
bool Surface::transformToDisplayInverse() {
return (mTransform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) ==
NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
@@ -2266,15 +2575,26 @@
mSurfaceListener->onBuffersDiscarded(discardedBufs);
}
-status_t Surface::setFrameRate(float frameRate, int8_t compatibility) {
+status_t Surface::setFrameRate(float frameRate, int8_t compatibility,
+ int8_t changeFrameRateStrategy) {
ATRACE_CALL();
ALOGV("Surface::setFrameRate");
- if (!ValidateFrameRate(frameRate, compatibility, "Surface::setFrameRate")) {
+ if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy,
+ "Surface::setFrameRate")) {
return BAD_VALUE;
}
- return composerService()->setFrameRate(mGraphicBufferProducer, frameRate, compatibility);
+ return composerService()->setFrameRate(mGraphicBufferProducer, frameRate, compatibility,
+ changeFrameRateStrategy);
+}
+
+status_t Surface::setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) {
+ return composerService()->setFrameTimelineInfo(mGraphicBufferProducer, frameTimelineInfo);
+}
+
+status_t Surface::getExtraBufferCount(int* extraBuffers) const {
+ return composerService()->getExtraBufferCount(extraBuffers);
}
}; // namespace android
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 83bc069..5db0eae 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -39,7 +39,9 @@
#include <gui/LayerState.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
-#include <ui/DisplayConfig.h>
+#include <private/gui/ParcelUtils.h>
+#include <ui/DisplayMode.h>
+#include <ui/DynamicDisplayInfo.h>
#ifndef NO_INPUT
#include <input/InputWindow.h>
@@ -125,6 +127,9 @@
return DefaultComposerClient::getComposerClient();
}
+JankDataListener::~JankDataListener() {
+}
+
// ---------------------------------------------------------------------------
// TransactionCompletedListener does not use ANDROID_SINGLETON_STATIC_INSTANCE because it needs
@@ -134,7 +139,7 @@
// 0 is an invalid callback id
TransactionCompletedListener::TransactionCompletedListener() : mCallbackIdCounter(1) {}
-CallbackId TransactionCompletedListener::getNextIdLocked() {
+int64_t TransactionCompletedListener::getNextIdLocked() {
return mCallbackIdCounter++;
}
@@ -158,13 +163,13 @@
CallbackId TransactionCompletedListener::addCallbackFunction(
const TransactionCompletedCallback& callbackFunction,
const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>&
- surfaceControls) {
+ surfaceControls,
+ CallbackId::Type callbackType) {
std::lock_guard<std::mutex> lock(mMutex);
startListeningLocked();
- CallbackId callbackId = getNextIdLocked();
+ CallbackId callbackId(getNextIdLocked(), callbackType);
mCallbacks[callbackId].callbackFunction = callbackFunction;
-
auto& callbackSurfaceControls = mCallbacks[callbackId].surfaceControls;
for (const auto& surfaceControl : surfaceControls) {
@@ -174,9 +179,56 @@
return callbackId;
}
+void TransactionCompletedListener::addJankListener(const sp<JankDataListener>& listener,
+ sp<SurfaceControl> surfaceControl) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mJankListeners.insert({surfaceControl->getHandle(), listener});
+}
+
+void TransactionCompletedListener::removeJankListener(const sp<JankDataListener>& listener) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ for (auto it = mJankListeners.begin(); it != mJankListeners.end();) {
+ if (it->second == listener) {
+ it = mJankListeners.erase(it);
+ } else {
+ it++;
+ }
+ }
+}
+
+void TransactionCompletedListener::setReleaseBufferCallback(uint64_t graphicBufferId,
+ ReleaseBufferCallback listener) {
+ std::scoped_lock<std::mutex> lock(mMutex);
+ mReleaseBufferCallbacks[graphicBufferId] = listener;
+}
+
+void TransactionCompletedListener::removeReleaseBufferCallback(uint64_t graphicBufferId) {
+ std::scoped_lock<std::mutex> lock(mMutex);
+ mReleaseBufferCallbacks.erase(graphicBufferId);
+}
+
+void TransactionCompletedListener::addSurfaceStatsListener(void* context, void* cookie,
+ sp<SurfaceControl> surfaceControl, SurfaceStatsCallback listener) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mSurfaceStatsListeners.insert({surfaceControl->getHandle(),
+ SurfaceStatsCallbackEntry(context, cookie, listener)});
+}
+
+void TransactionCompletedListener::removeSurfaceStatsListener(void* context, void* cookie) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ for (auto it = mSurfaceStatsListeners.begin(); it != mSurfaceStatsListeners.end();) {
+ auto [itContext, itCookie, itListener] = it->second;
+ if (itContext == context && itCookie == cookie) {
+ it = mSurfaceStatsListeners.erase(it);
+ } else {
+ it++;
+ }
+ }
+}
+
void TransactionCompletedListener::addSurfaceControlToCallbacks(
const sp<SurfaceControl>& surfaceControl,
- const std::unordered_set<CallbackId>& callbackIds) {
+ const std::unordered_set<CallbackId, CallbackIdHash>& callbackIds) {
std::lock_guard<std::mutex> lock(mMutex);
for (auto callbackId : callbackIds) {
@@ -188,7 +240,9 @@
}
void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
- std::unordered_map<CallbackId, CallbackTranslation> callbacksMap;
+ std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap;
+ std::multimap<sp<IBinder>, sp<JankDataListener>> jankListenersMap;
+ std::multimap<sp<IBinder>, SurfaceStatsCallbackEntry> surfaceListeners;
{
std::lock_guard<std::mutex> lock(mMutex);
@@ -204,6 +258,8 @@
* sp<SurfaceControl> that could possibly exist for the callbacks.
*/
callbacksMap = mCallbacks;
+ jankListenersMap = mJankListeners;
+ surfaceListeners = mSurfaceStatsListeners;
for (const auto& transactionStats : listenerStats.transactionStats) {
for (auto& callbackId : transactionStats.callbackIds) {
mCallbacks.erase(callbackId);
@@ -211,7 +267,36 @@
}
}
for (const auto& transactionStats : listenerStats.transactionStats) {
+ // handle on commit callbacks
for (auto callbackId : transactionStats.callbackIds) {
+ if (callbackId.type != CallbackId::Type::ON_COMMIT) {
+ continue;
+ }
+ auto& [callbackFunction, callbackSurfaceControls] = callbacksMap[callbackId];
+ if (!callbackFunction) {
+ ALOGE("cannot call null callback function, skipping");
+ continue;
+ }
+ std::vector<SurfaceControlStats> surfaceControlStats;
+ for (const auto& surfaceStats : transactionStats.surfaceStats) {
+ surfaceControlStats
+ .emplace_back(callbacksMap[callbackId]
+ .surfaceControls[surfaceStats.surfaceControl],
+ transactionStats.latchTime, surfaceStats.acquireTime,
+ transactionStats.presentFence,
+ surfaceStats.previousReleaseFence, surfaceStats.transformHint,
+ surfaceStats.eventStats);
+ }
+
+ callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
+ surfaceControlStats);
+ }
+
+ // handle on complete callbacks
+ for (auto callbackId : transactionStats.callbackIds) {
+ if (callbackId.type != CallbackId::Type::ON_COMPLETE) {
+ continue;
+ }
auto& [callbackFunction, callbackSurfaceControls] = callbacksMap[callbackId];
if (!callbackFunction) {
ALOGE("cannot call null callback function, skipping");
@@ -231,14 +316,71 @@
.surfaceControls[surfaceStats.surfaceControl]
->setTransformHint(surfaceStats.transformHint);
}
+ // If there is buffer id set, we look up any pending client release buffer callbacks
+ // and call them. This is a performance optimization when we have a transaction
+ // callback and a release buffer callback happening at the same time to avoid an
+ // additional ipc call from the server.
+ if (surfaceStats.previousBufferId) {
+ ReleaseBufferCallback callback;
+ {
+ std::scoped_lock<std::mutex> lock(mMutex);
+ callback = popReleaseBufferCallbackLocked(surfaceStats.previousBufferId);
+ }
+ if (callback) {
+ callback(surfaceStats.previousBufferId,
+ surfaceStats.previousReleaseFence
+ ? surfaceStats.previousReleaseFence
+ : Fence::NO_FENCE);
+ }
+ }
}
callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
surfaceControlStats);
}
+ for (const auto& surfaceStats : transactionStats.surfaceStats) {
+ auto listenerRange = surfaceListeners.equal_range(surfaceStats.surfaceControl);
+ for (auto it = listenerRange.first; it != listenerRange.second; it++) {
+ auto entry = it->second;
+ entry.callback(entry.context, transactionStats.latchTime,
+ transactionStats.presentFence, surfaceStats);
+ }
+
+ if (surfaceStats.jankData.empty()) continue;
+ auto jankRange = jankListenersMap.equal_range(surfaceStats.surfaceControl);
+ for (auto it = jankRange.first; it != jankRange.second; it++) {
+ it->second->onJankDataAvailable(surfaceStats.jankData);
+ }
+ }
}
}
+void TransactionCompletedListener::onReleaseBuffer(uint64_t graphicBufferId,
+ sp<Fence> releaseFence) {
+ ReleaseBufferCallback callback;
+ {
+ std::scoped_lock<std::mutex> lock(mMutex);
+ callback = popReleaseBufferCallbackLocked(graphicBufferId);
+ }
+ if (!callback) {
+ ALOGE("Could not call release buffer callback, buffer not found %" PRIu64, graphicBufferId);
+ return;
+ }
+ callback(graphicBufferId, releaseFence);
+}
+
+ReleaseBufferCallback TransactionCompletedListener::popReleaseBufferCallbackLocked(
+ uint64_t graphicBufferId) {
+ ReleaseBufferCallback callback;
+ auto itr = mReleaseBufferCallbacks.find(graphicBufferId);
+ if (itr == mReleaseBufferCallbacks.end()) {
+ return nullptr;
+ }
+ callback = itr->second;
+ mReleaseBufferCallbacks.erase(itr);
+ return callback;
+}
+
// ---------------------------------------------------------------------------
void removeDeadBufferCallback(void* /*context*/, uint64_t graphicBufferId);
@@ -348,15 +490,26 @@
// ---------------------------------------------------------------------------
+// Initialize transaction id counter used to generate transaction ids
+// Transactions will start counting at 1, 0 is used for invalid transactions
+std::atomic<uint32_t> SurfaceComposerClient::Transaction::idCounter = 1;
+
+SurfaceComposerClient::Transaction::Transaction() {
+ mId = generateId();
+}
+
SurfaceComposerClient::Transaction::Transaction(const Transaction& other)
- : mForceSynchronous(other.mForceSynchronous),
+ : mId(other.mId),
+ mForceSynchronous(other.mForceSynchronous),
mTransactionNestCount(other.mTransactionNestCount),
mAnimation(other.mAnimation),
- mEarlyWakeup(other.mEarlyWakeup),
- mExplicitEarlyWakeupStart(other.mExplicitEarlyWakeupStart),
- mExplicitEarlyWakeupEnd(other.mExplicitEarlyWakeupEnd),
+ mEarlyWakeupStart(other.mEarlyWakeupStart),
+ mEarlyWakeupEnd(other.mEarlyWakeupEnd),
mContainsBuffer(other.mContainsBuffer),
- mDesiredPresentTime(other.mDesiredPresentTime) {
+ mDesiredPresentTime(other.mDesiredPresentTime),
+ mIsAutoTimestamp(other.mIsAutoTimestamp),
+ mFrameTimelineInfo(other.mFrameTimelineInfo),
+ mApplyToken(other.mApplyToken) {
mDisplayStates = other.mDisplayStates;
mComposerStates = other.mComposerStates;
mInputWindowCommands = other.mInputWindowCommands;
@@ -372,16 +525,24 @@
return nullptr;
}
+int64_t SurfaceComposerClient::Transaction::generateId() {
+ return (((int64_t)getpid()) << 32) | idCounter++;
+}
+
status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel) {
const uint32_t forceSynchronous = parcel->readUint32();
const uint32_t transactionNestCount = parcel->readUint32();
const bool animation = parcel->readBool();
- const bool earlyWakeup = parcel->readBool();
- const bool explicitEarlyWakeupStart = parcel->readBool();
- const bool explicitEarlyWakeupEnd = parcel->readBool();
+ const bool earlyWakeupStart = parcel->readBool();
+ const bool earlyWakeupEnd = parcel->readBool();
const bool containsBuffer = parcel->readBool();
const int64_t desiredPresentTime = parcel->readInt64();
+ const bool isAutoTimestamp = parcel->readBool();
+ FrameTimelineInfo frameTimelineInfo;
+ SAFE_PARCEL(frameTimelineInfo.read, *parcel);
+ sp<IBinder> applyToken;
+ parcel->readNullableStrongBinder(&applyToken);
size_t count = static_cast<size_t>(parcel->readUint32());
if (count > parcel->dataSize()) {
return BAD_VALUE;
@@ -410,7 +571,9 @@
return BAD_VALUE;
}
for (size_t j = 0; j < numCallbackIds; j++) {
- listenerCallbacks[listener].callbackIds.insert(parcel->readInt64());
+ CallbackId id;
+ parcel->readParcelable(&id);
+ listenerCallbacks[listener].callbackIds.insert(id);
}
size_t numSurfaces = parcel->readUint32();
if (numSurfaces > parcel->dataSize()) {
@@ -418,7 +581,7 @@
}
for (size_t j = 0; j < numSurfaces; j++) {
sp<SurfaceControl> surface;
- surface = SurfaceControl::readFromParcel(parcel);
+ SAFE_PARCEL(SurfaceControl::readFromParcel, *parcel, &surface);
listenerCallbacks[listener].surfaceControls.insert(surface);
}
}
@@ -430,12 +593,14 @@
std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> composerStates;
composerStates.reserve(count);
for (size_t i = 0; i < count; i++) {
- sp<IBinder> surfaceControlHandle = parcel->readStrongBinder();
+ sp<IBinder> surfaceControlHandle;
+ SAFE_PARCEL(parcel->readStrongBinder, &surfaceControlHandle);
ComposerState composerState;
if (composerState.read(*parcel) == BAD_VALUE) {
return BAD_VALUE;
}
+
composerStates[surfaceControlHandle] = composerState;
}
@@ -446,15 +611,17 @@
mForceSynchronous = forceSynchronous;
mTransactionNestCount = transactionNestCount;
mAnimation = animation;
- mEarlyWakeup = earlyWakeup;
- mExplicitEarlyWakeupStart = explicitEarlyWakeupStart;
- mExplicitEarlyWakeupEnd = explicitEarlyWakeupEnd;
+ mEarlyWakeupStart = earlyWakeupStart;
+ mEarlyWakeupEnd = earlyWakeupEnd;
mContainsBuffer = containsBuffer;
mDesiredPresentTime = desiredPresentTime;
+ mIsAutoTimestamp = isAutoTimestamp;
+ mFrameTimelineInfo = frameTimelineInfo;
mDisplayStates = displayStates;
mListenerCallbacks = listenerCallbacks;
mComposerStates = composerStates;
mInputWindowCommands = inputWindowCommands;
+ mApplyToken = applyToken;
return NO_ERROR;
}
@@ -475,11 +642,13 @@
parcel->writeUint32(mForceSynchronous);
parcel->writeUint32(mTransactionNestCount);
parcel->writeBool(mAnimation);
- parcel->writeBool(mEarlyWakeup);
- parcel->writeBool(mExplicitEarlyWakeupStart);
- parcel->writeBool(mExplicitEarlyWakeupEnd);
+ parcel->writeBool(mEarlyWakeupStart);
+ parcel->writeBool(mEarlyWakeupEnd);
parcel->writeBool(mContainsBuffer);
parcel->writeInt64(mDesiredPresentTime);
+ parcel->writeBool(mIsAutoTimestamp);
+ SAFE_PARCEL(mFrameTimelineInfo.write, *parcel);
+ parcel->writeStrongBinder(mApplyToken);
parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size()));
for (auto const& displayState : mDisplayStates) {
displayState.write(*parcel);
@@ -490,17 +659,17 @@
parcel->writeStrongBinder(ITransactionCompletedListener::asBinder(listener));
parcel->writeUint32(static_cast<uint32_t>(callbackInfo.callbackIds.size()));
for (auto callbackId : callbackInfo.callbackIds) {
- parcel->writeInt64(callbackId);
+ parcel->writeParcelable(callbackId);
}
parcel->writeUint32(static_cast<uint32_t>(callbackInfo.surfaceControls.size()));
for (auto surfaceControl : callbackInfo.surfaceControls) {
- surfaceControl->writeToParcel(parcel);
+ SAFE_PARCEL(surfaceControl->writeToParcel, *parcel);
}
}
parcel->writeUint32(static_cast<uint32_t>(mComposerStates.size()));
- for (auto const& [surfaceHandle, composerState] : mComposerStates) {
- parcel->writeStrongBinder(surfaceHandle);
+ for (auto const& [handle, composerState] : mComposerStates) {
+ SAFE_PARCEL(parcel->writeStrongBinder, handle);
composerState.write(*parcel);
}
@@ -509,11 +678,11 @@
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Transaction&& other) {
- for (auto const& [surfaceHandle, composerState] : other.mComposerStates) {
- if (mComposerStates.count(surfaceHandle) == 0) {
- mComposerStates[surfaceHandle] = composerState;
+ for (auto const& [handle, composerState] : other.mComposerStates) {
+ if (mComposerStates.count(handle) == 0) {
+ mComposerStates[handle] = composerState;
} else {
- mComposerStates[surfaceHandle].state.merge(composerState.state);
+ mComposerStates[handle].state.merge(composerState.state);
}
}
@@ -552,9 +721,12 @@
mInputWindowCommands.merge(other.mInputWindowCommands);
mContainsBuffer |= other.mContainsBuffer;
- mEarlyWakeup = mEarlyWakeup || other.mEarlyWakeup;
- mExplicitEarlyWakeupStart = mExplicitEarlyWakeupStart || other.mExplicitEarlyWakeupStart;
- mExplicitEarlyWakeupEnd = mExplicitEarlyWakeupEnd || other.mExplicitEarlyWakeupEnd;
+ mEarlyWakeupStart = mEarlyWakeupStart || other.mEarlyWakeupStart;
+ mEarlyWakeupEnd = mEarlyWakeupEnd || other.mEarlyWakeupEnd;
+ mApplyToken = other.mApplyToken;
+
+ mFrameTimelineInfo.merge(other.mFrameTimelineInfo);
+
other.clear();
return *this;
}
@@ -568,10 +740,12 @@
mForceSynchronous = 0;
mTransactionNestCount = 0;
mAnimation = false;
- mEarlyWakeup = false;
- mExplicitEarlyWakeupStart = false;
- mExplicitEarlyWakeupEnd = false;
- mDesiredPresentTime = -1;
+ mEarlyWakeupStart = false;
+ mEarlyWakeupEnd = false;
+ mDesiredPresentTime = 0;
+ mIsAutoTimestamp = true;
+ mFrameTimelineInfo.clear();
+ mApplyToken = nullptr;
}
void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
@@ -582,7 +756,8 @@
uncacheBuffer.id = cacheId;
sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
- sf->setTransactionState({}, {}, 0, applyToken, {}, -1, uncacheBuffer, false, {});
+ sf->setTransactionState(FrameTimelineInfo{}, {}, {}, 0, applyToken, {}, systemTime(), true,
+ uncacheBuffer, false, {}, 0 /* Undefined transactionId */);
}
void SurfaceComposerClient::Transaction::cacheBuffers() {
@@ -592,7 +767,7 @@
size_t count = 0;
for (auto& [handle, cs] : mComposerStates) {
- layer_state_t* s = getLayerState(handle);
+ layer_state_t* s = &(mComposerStates[handle].state);
if (!(s->what & layer_state_t::eBufferChanged)) {
continue;
} else if (s->what & layer_state_t::eCachedBufferChanged) {
@@ -665,8 +840,6 @@
}
}
- mListenerCallbacks.clear();
-
cacheBuffers();
Vector<ComposerState> composerStates;
@@ -679,10 +852,7 @@
composerStates.add(kv.second);
}
- mComposerStates.clear();
-
- displayStates = mDisplayStates;
- mDisplayStates.clear();
+ displayStates = std::move(mDisplayStates);
if (mForceSynchronous) {
flags |= ISurfaceComposer::eSynchronous;
@@ -690,31 +860,29 @@
if (mAnimation) {
flags |= ISurfaceComposer::eAnimation;
}
- if (mEarlyWakeup) {
- flags |= ISurfaceComposer::eEarlyWakeup;
- }
- // If both mExplicitEarlyWakeupStart and mExplicitEarlyWakeupEnd are set
+ // If both mEarlyWakeupStart and mEarlyWakeupEnd are set
// it is equivalent for none
- if (mExplicitEarlyWakeupStart && !mExplicitEarlyWakeupEnd) {
- flags |= ISurfaceComposer::eExplicitEarlyWakeupStart;
+ if (mEarlyWakeupStart && !mEarlyWakeupEnd) {
+ flags |= ISurfaceComposer::eEarlyWakeupStart;
}
- if (mExplicitEarlyWakeupEnd && !mExplicitEarlyWakeupStart) {
- flags |= ISurfaceComposer::eExplicitEarlyWakeupEnd;
+ if (mEarlyWakeupEnd && !mEarlyWakeupStart) {
+ flags |= ISurfaceComposer::eEarlyWakeupEnd;
}
- mForceSynchronous = false;
- mAnimation = false;
- mEarlyWakeup = false;
- mExplicitEarlyWakeupStart = false;
- mExplicitEarlyWakeupEnd = false;
+ sp<IBinder> applyToken = mApplyToken
+ ? mApplyToken
+ : IInterface::asBinder(TransactionCompletedListener::getIInstance());
- sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
- sf->setTransactionState(composerStates, displayStates, flags, applyToken, mInputWindowCommands,
- mDesiredPresentTime,
+ sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags, applyToken,
+ mInputWindowCommands, mDesiredPresentTime, mIsAutoTimestamp,
{} /*uncacheBuffer - only set in doUncacheBufferTransaction*/,
- hasListenerCallbacks, listenerCallbacks);
- mInputWindowCommands.clear();
+ hasListenerCallbacks, listenerCallbacks, mId);
+ mId = generateId();
+
+ // Clear the current states and flags
+ clear();
+
mStatus = NO_ERROR;
return NO_ERROR;
}
@@ -750,23 +918,24 @@
mAnimation = true;
}
-void SurfaceComposerClient::Transaction::setEarlyWakeup() {
- mEarlyWakeup = true;
+void SurfaceComposerClient::Transaction::setEarlyWakeupStart() {
+ mEarlyWakeupStart = true;
}
-void SurfaceComposerClient::Transaction::setExplicitEarlyWakeupStart() {
- mExplicitEarlyWakeupStart = true;
+void SurfaceComposerClient::Transaction::setEarlyWakeupEnd() {
+ mEarlyWakeupEnd = true;
}
-void SurfaceComposerClient::Transaction::setExplicitEarlyWakeupEnd() {
- mExplicitEarlyWakeupEnd = true;
-}
+layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<SurfaceControl>& sc) {
+ auto handle = sc->getLayerStateHandle();
-layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<IBinder>& handle) {
if (mComposerStates.count(handle) == 0) {
// we don't have it, add an initialized layer_state to our list
ComposerState s;
+
s.state.surface = handle;
+ s.state.layerId = sc->getLayerId();
+
mComposerStates[handle] = s;
}
@@ -837,8 +1006,8 @@
return *this;
}
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setRelativeLayer(const sp<SurfaceControl>& sc, const sp<IBinder>& relativeTo,
- int32_t z) {
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setRelativeLayer(
+ const sp<SurfaceControl>& sc, const sp<SurfaceControl>& relativeTo, int32_t z) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
@@ -846,7 +1015,7 @@
}
s->what |= layer_state_t::eRelativeLayerChanged;
s->what &= ~layer_state_t::eLayerChanged;
- s->relativeLayerHandle = relativeTo;
+ s->relativeLayerSurfaceControl = relativeTo;
s->z = z;
registerSurfaceControlForCallback(sc);
@@ -861,9 +1030,9 @@
mStatus = BAD_INDEX;
return *this;
}
- if ((mask & layer_state_t::eLayerOpaque) ||
- (mask & layer_state_t::eLayerHidden) ||
- (mask & layer_state_t::eLayerSecure)) {
+ if ((mask & layer_state_t::eLayerOpaque) || (mask & layer_state_t::eLayerHidden) ||
+ (mask & layer_state_t::eLayerSecure) || (mask & layer_state_t::eLayerSkipScreenshot) ||
+ (mask & layer_state_t::eEnableBackpressure)) {
s->what |= layer_state_t::eFlagsChanged;
}
s->flags &= ~mask;
@@ -952,15 +1121,15 @@
return *this;
}
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop_legacy(
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop(
const sp<SurfaceControl>& sc, const Rect& crop) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
return *this;
}
- s->what |= layer_state_t::eCropChanged_legacy;
- s->crop_legacy = crop;
+ s->what |= layer_state_t::eCropChanged;
+ s->crop = crop;
registerSurfaceControlForCallback(sc);
return *this;
@@ -990,65 +1159,30 @@
return *this;
}
-SurfaceComposerClient::Transaction&
-SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(const sp<SurfaceControl>& sc,
- const sp<IBinder>& handle,
- uint64_t frameNumber) {
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBlurRegions(
+ const sp<SurfaceControl>& sc, const std::vector<BlurRegion>& blurRegions) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
return *this;
}
- s->what |= layer_state_t::eDeferTransaction_legacy;
- s->barrierHandle_legacy = handle;
- s->frameNumber_legacy = frameNumber;
-
- registerSurfaceControlForCallback(sc);
- return *this;
-}
-
-SurfaceComposerClient::Transaction&
-SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(const sp<SurfaceControl>& sc,
- const sp<Surface>& barrierSurface,
- uint64_t frameNumber) {
- layer_state_t* s = getLayerState(sc);
- if (!s) {
- mStatus = BAD_INDEX;
- return *this;
- }
- s->what |= layer_state_t::eDeferTransaction_legacy;
- s->barrierGbp_legacy = barrierSurface->getIGraphicBufferProducer();
- s->frameNumber_legacy = frameNumber;
-
- registerSurfaceControlForCallback(sc);
- return *this;
-}
-
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::reparentChildren(
- const sp<SurfaceControl>& sc,
- const sp<IBinder>& newParentHandle) {
- layer_state_t* s = getLayerState(sc);
- if (!s) {
- mStatus = BAD_INDEX;
- return *this;
- }
- s->what |= layer_state_t::eReparentChildren;
- s->reparentHandle = newParentHandle;
-
- registerSurfaceControlForCallback(sc);
+ s->what |= layer_state_t::eBlurRegionsChanged;
+ s->blurRegions = blurRegions;
return *this;
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::reparent(
- const sp<SurfaceControl>& sc,
- const sp<IBinder>& newParentHandle) {
+ const sp<SurfaceControl>& sc, const sp<SurfaceControl>& newParent) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
return *this;
}
+ if (SurfaceControl::isSameSurface(sc, newParent)) {
+ return *this;
+ }
s->what |= layer_state_t::eReparent;
- s->parentHandleForChild = newParentHandle;
+ s->parentSurfaceControlForChild = newParent ? newParent->getParentingLayer() : nullptr;
registerSurfaceControlForCallback(sc);
return *this;
@@ -1115,43 +1249,21 @@
return *this;
}
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop(
- const sp<SurfaceControl>& sc, const Rect& crop) {
- layer_state_t* s = getLayerState(sc);
- if (!s) {
- mStatus = BAD_INDEX;
- return *this;
- }
- s->what |= layer_state_t::eCropChanged;
- s->crop = crop;
-
- registerSurfaceControlForCallback(sc);
- return *this;
-}
-
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrame(
- const sp<SurfaceControl>& sc, const Rect& frame) {
- layer_state_t* s = getLayerState(sc);
- if (!s) {
- mStatus = BAD_INDEX;
- return *this;
- }
- s->what |= layer_state_t::eFrameChanged;
- s->frame = frame;
-
- registerSurfaceControlForCallback(sc);
- return *this;
-}
-
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffer(
- const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer) {
+ const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
+ ReleaseBufferCallback callback) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
return *this;
}
+ removeReleaseBufferCallback(s);
s->what |= layer_state_t::eBufferChanged;
s->buffer = buffer;
+ if (mIsAutoTimestamp) {
+ mDesiredPresentTime = systemTime();
+ }
+ setReleaseBufferCallback(s, callback);
registerSurfaceControlForCallback(sc);
@@ -1159,6 +1271,34 @@
return *this;
}
+void SurfaceComposerClient::Transaction::removeReleaseBufferCallback(layer_state_t* s) {
+ if (!s->releaseBufferListener) {
+ return;
+ }
+
+ s->what &= ~static_cast<uint64_t>(layer_state_t::eReleaseBufferListenerChanged);
+ s->releaseBufferListener = nullptr;
+ TransactionCompletedListener::getInstance()->removeReleaseBufferCallback(s->buffer->getId());
+}
+
+void SurfaceComposerClient::Transaction::setReleaseBufferCallback(layer_state_t* s,
+ ReleaseBufferCallback callback) {
+ if (!callback) {
+ return;
+ }
+
+ if (!s->buffer) {
+ ALOGW("Transaction::setReleaseBufferCallback"
+ "ignored trying to set a callback on a null buffer.");
+ return;
+ }
+
+ s->what |= layer_state_t::eReleaseBufferListenerChanged;
+ s->releaseBufferListener = TransactionCompletedListener::getIInstance();
+ auto listener = TransactionCompletedListener::getInstance();
+ listener->setReleaseBufferCallback(s->buffer->getId(), callback);
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAcquireFence(
const sp<SurfaceControl>& sc, const sp<Fence>& fence) {
layer_state_t* s = getLayerState(sc);
@@ -1246,6 +1386,7 @@
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDesiredPresentTime(
nsecs_t desiredPresentTime) {
mDesiredPresentTime = desiredPresentTime;
+ mIsAutoTimestamp = false;
return *this;
}
@@ -1279,9 +1420,9 @@
return *this;
}
-SurfaceComposerClient::Transaction&
-SurfaceComposerClient::Transaction::addTransactionCompletedCallback(
- TransactionCompletedCallbackTakesContext callback, void* callbackContext) {
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::addTransactionCallback(
+ TransactionCompletedCallbackTakesContext callback, void* callbackContext,
+ CallbackId::Type callbackType) {
auto listener = TransactionCompletedListener::getInstance();
auto callbackWithContext = std::bind(callback, callbackContext, std::placeholders::_1,
@@ -1289,13 +1430,26 @@
const auto& surfaceControls =
mListenerCallbacks[TransactionCompletedListener::getIInstance()].surfaceControls;
- CallbackId callbackId = listener->addCallbackFunction(callbackWithContext, surfaceControls);
+ CallbackId callbackId =
+ listener->addCallbackFunction(callbackWithContext, surfaceControls, callbackType);
mListenerCallbacks[TransactionCompletedListener::getIInstance()].callbackIds.emplace(
callbackId);
return *this;
}
+SurfaceComposerClient::Transaction&
+SurfaceComposerClient::Transaction::addTransactionCompletedCallback(
+ TransactionCompletedCallbackTakesContext callback, void* callbackContext) {
+ return addTransactionCallback(callback, callbackContext, CallbackId::Type::ON_COMPLETE);
+}
+
+SurfaceComposerClient::Transaction&
+SurfaceComposerClient::Transaction::addTransactionCommittedCallback(
+ TransactionCompletedCallbackTakesContext callback, void* callbackContext) {
+ return addTransactionCallback(callback, callbackContext, CallbackId::Type::ON_COMMIT);
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::notifyProducerDisconnect(
const sp<SurfaceControl>& sc) {
layer_state_t* s = getLayerState(sc);
@@ -1308,45 +1462,17 @@
return *this;
}
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::detachChildren(
- const sp<SurfaceControl>& sc) {
- layer_state_t* s = getLayerState(sc);
- if (!s) {
- mStatus = BAD_INDEX;
- return *this;
- }
- s->what |= layer_state_t::eDetachChildren;
-
- registerSurfaceControlForCallback(sc);
- return *this;
-}
-
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setOverrideScalingMode(
- const sp<SurfaceControl>& sc, int32_t overrideScalingMode) {
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameNumber(
+ const sp<SurfaceControl>& sc, uint64_t frameNumber) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
return *this;
}
- switch (overrideScalingMode) {
- case NATIVE_WINDOW_SCALING_MODE_FREEZE:
- case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
- case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
- case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP:
- case -1:
- break;
- default:
- ALOGE("unknown scaling mode: %d",
- overrideScalingMode);
- mStatus = BAD_VALUE;
- return *this;
- }
+ s->what |= layer_state_t::eFrameNumberChanged;
+ s->frameNumber = frameNumber;
- s->what |= layer_state_t::eOverrideScalingModeChanged;
- s->overrideScalingMode = overrideScalingMode;
-
- registerSurfaceControlForCallback(sc);
return *this;
}
@@ -1359,11 +1485,17 @@
mStatus = BAD_INDEX;
return *this;
}
- s->inputInfo = info;
+ s->inputHandle = new InputWindowHandle(info);
s->what |= layer_state_t::eInputInfoChanged;
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFocusedWindow(
+ const FocusRequest& request) {
+ mInputWindowCommands.focusRequests.push_back(request);
+ return *this;
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::syncInputWindows() {
mInputWindowCommands.syncInputWindows = true;
return *this;
@@ -1387,7 +1519,7 @@
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setGeometry(
const sp<SurfaceControl>& sc, const Rect& source, const Rect& dst, int transform) {
- setCrop_legacy(sc, source);
+ setCrop(sc, source);
int x = dst.left;
int y = dst.top;
@@ -1452,19 +1584,25 @@
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameRate(
- const sp<SurfaceControl>& sc, float frameRate, int8_t compatibility) {
+ const sp<SurfaceControl>& sc, float frameRate, int8_t compatibility,
+ int8_t changeFrameRateStrategy) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
return *this;
}
- if (!ValidateFrameRate(frameRate, compatibility, "Transaction::setFrameRate")) {
+ // Allow privileged values as well here, those will be ignored by SF if
+ // the caller is not privileged
+ if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy,
+ "Transaction::setFrameRate",
+ /*privileged=*/true)) {
mStatus = BAD_VALUE;
return *this;
}
s->what |= layer_state_t::eFrameRateChanged;
s->frameRate = frameRate;
s->frameRateCompatibility = compatibility;
+ s->changeFrameRateStrategy = changeFrameRateStrategy;
return *this;
}
@@ -1484,6 +1622,48 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameTimelineInfo(
+ const FrameTimelineInfo& frameTimelineInfo) {
+ mFrameTimelineInfo.merge(frameTimelineInfo);
+ return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAutoRefresh(
+ const sp<SurfaceControl>& sc, bool autoRefresh) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::eAutoRefreshChanged;
+ s->autoRefresh = autoRefresh;
+ return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setApplyToken(
+ const sp<IBinder>& applyToken) {
+ mApplyToken = applyToken;
+ return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setStretchEffect(
+ const sp<SurfaceControl>& sc, float left, float top, float right, float bottom, float vecX,
+ float vecY, float maxAmount) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::eStretchChanged;
+ s->stretchEffect = StretchEffect{.area = {left, top, right, bottom},
+ .vectorX = vecX,
+ .vectorY = vecY,
+ .maxAmount = maxAmount};
+ return *this;
+}
+
// ---------------------------------------------------------------------------
DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
@@ -1530,8 +1710,8 @@
const Rect& displayRect) {
DisplayState& s(getDisplayState(token));
s.orientation = orientation;
- s.viewport = layerStackRect;
- s.frame = displayRect;
+ s.layerStackSpaceRect = layerStackRect;
+ s.orientedDisplaySpaceRect = displayRect;
s.what |= DisplayState::eDisplayProjectionChanged;
mForceSynchronous = true; // TODO: do we actually still need this?
}
@@ -1599,11 +1779,11 @@
sp<SurfaceControl> SurfaceComposerClient::createSurface(const String8& name, uint32_t w, uint32_t h,
PixelFormat format, uint32_t flags,
- SurfaceControl* parent,
+ const sp<IBinder>& parentHandle,
LayerMetadata metadata,
uint32_t* outTransformHint) {
sp<SurfaceControl> s;
- createSurfaceChecked(name, w, h, format, &s, flags, parent, std::move(metadata),
+ createSurfaceChecked(name, w, h, format, &s, flags, parentHandle, std::move(metadata),
outTransformHint);
return s;
}
@@ -1622,14 +1802,16 @@
sp<IGraphicBufferProducer> gbp;
uint32_t transformHint = 0;
+ int32_t id = -1;
err = mClient->createWithSurfaceParent(name, w, h, format, flags, parentGbp,
- std::move(metadata), &handle, &gbp, &transformHint);
+ std::move(metadata), &handle, &gbp, &id,
+ &transformHint);
if (outTransformHint) {
*outTransformHint = transformHint;
}
ALOGE_IF(err, "SurfaceComposerClient::createWithSurfaceParent error %s", strerror(-err));
if (err == NO_ERROR) {
- return new SurfaceControl(this, handle, gbp, transformHint);
+ return new SurfaceControl(this, handle, gbp, id, transformHint);
}
}
return nullptr;
@@ -1638,29 +1820,28 @@
status_t SurfaceComposerClient::createSurfaceChecked(const String8& name, uint32_t w, uint32_t h,
PixelFormat format,
sp<SurfaceControl>* outSurface, uint32_t flags,
- SurfaceControl* parent, LayerMetadata metadata,
+ const sp<IBinder>& parentHandle,
+ LayerMetadata metadata,
uint32_t* outTransformHint) {
sp<SurfaceControl> sur;
status_t err = mStatus;
if (mStatus == NO_ERROR) {
sp<IBinder> handle;
- sp<IBinder> parentHandle;
sp<IGraphicBufferProducer> gbp;
- if (parent != nullptr) {
- parentHandle = parent->getHandle();
- }
-
uint32_t transformHint = 0;
+ int32_t id = -1;
err = mClient->createSurface(name, w, h, format, flags, parentHandle, std::move(metadata),
- &handle, &gbp, &transformHint);
+ &handle, &gbp, &id, &transformHint);
+
if (outTransformHint) {
*outTransformHint = transformHint;
}
ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
if (err == NO_ERROR) {
- *outSurface = new SurfaceControl(this, handle, gbp, transformHint);
+ *outSurface =
+ new SurfaceControl(this, handle, gbp, id, w, h, format, transformHint, flags);
}
}
return err;
@@ -1673,9 +1854,10 @@
sp<IBinder> handle;
sp<IBinder> mirrorFromHandle = mirrorFromSurface->getHandle();
- status_t err = mClient->mirrorSurface(mirrorFromHandle, &handle);
+ int32_t layer_id = -1;
+ status_t err = mClient->mirrorSurface(mirrorFromHandle, &handle, &layer_id);
if (err == NO_ERROR) {
- return new SurfaceControl(this, handle, nullptr, true /* owned */);
+ return new SurfaceControl(this, handle, nullptr, layer_id, true /* owned */);
}
return nullptr;
}
@@ -1712,64 +1894,54 @@
return ComposerService::getComposerService()->getDisplayState(display, state);
}
-status_t SurfaceComposerClient::getDisplayInfo(const sp<IBinder>& display, DisplayInfo* info) {
- return ComposerService::getComposerService()->getDisplayInfo(display, info);
+status_t SurfaceComposerClient::getStaticDisplayInfo(const sp<IBinder>& display,
+ ui::StaticDisplayInfo* info) {
+ return ComposerService::getComposerService()->getStaticDisplayInfo(display, info);
}
-status_t SurfaceComposerClient::getDisplayConfigs(const sp<IBinder>& display,
- Vector<DisplayConfig>* configs) {
- return ComposerService::getComposerService()->getDisplayConfigs(display, configs);
+status_t SurfaceComposerClient::getDynamicDisplayInfo(const sp<IBinder>& display,
+ ui::DynamicDisplayInfo* info) {
+ return ComposerService::getComposerService()->getDynamicDisplayInfo(display, info);
}
-status_t SurfaceComposerClient::getActiveDisplayConfig(const sp<IBinder>& display,
- DisplayConfig* config) {
- Vector<DisplayConfig> configs;
- status_t result = getDisplayConfigs(display, &configs);
+status_t SurfaceComposerClient::getActiveDisplayMode(const sp<IBinder>& display,
+ ui::DisplayMode* mode) {
+ ui::DynamicDisplayInfo info;
+ status_t result = getDynamicDisplayInfo(display, &info);
if (result != NO_ERROR) {
return result;
}
- int activeId = getActiveConfig(display);
- if (activeId < 0) {
- ALOGE("No active configuration found");
- return NAME_NOT_FOUND;
+ if (const auto activeMode = info.getActiveDisplayMode()) {
+ *mode = *activeMode;
+ return NO_ERROR;
}
- *config = configs[static_cast<size_t>(activeId)];
- return NO_ERROR;
+ ALOGE("Active display mode not found.");
+ return NAME_NOT_FOUND;
}
-int SurfaceComposerClient::getActiveConfig(const sp<IBinder>& display) {
- return ComposerService::getComposerService()->getActiveConfig(display);
-}
-
-status_t SurfaceComposerClient::setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
- int32_t defaultConfig,
- float primaryRefreshRateMin,
- float primaryRefreshRateMax,
- float appRequestRefreshRateMin,
- float appRequestRefreshRateMax) {
+status_t SurfaceComposerClient::setDesiredDisplayModeSpecs(
+ const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode, bool allowGroupSwitching,
+ float primaryRefreshRateMin, float primaryRefreshRateMax, float appRequestRefreshRateMin,
+ float appRequestRefreshRateMax) {
return ComposerService::getComposerService()
- ->setDesiredDisplayConfigSpecs(displayToken, defaultConfig, primaryRefreshRateMin,
- primaryRefreshRateMax, appRequestRefreshRateMin,
- appRequestRefreshRateMax);
+ ->setDesiredDisplayModeSpecs(displayToken, defaultMode, allowGroupSwitching,
+ primaryRefreshRateMin, primaryRefreshRateMax,
+ appRequestRefreshRateMin, appRequestRefreshRateMax);
}
-status_t SurfaceComposerClient::getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
- int32_t* outDefaultConfig,
- float* outPrimaryRefreshRateMin,
- float* outPrimaryRefreshRateMax,
- float* outAppRequestRefreshRateMin,
- float* outAppRequestRefreshRateMax) {
+status_t SurfaceComposerClient::getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
+ ui::DisplayModeId* outDefaultMode,
+ bool* outAllowGroupSwitching,
+ float* outPrimaryRefreshRateMin,
+ float* outPrimaryRefreshRateMax,
+ float* outAppRequestRefreshRateMin,
+ float* outAppRequestRefreshRateMax) {
return ComposerService::getComposerService()
- ->getDesiredDisplayConfigSpecs(displayToken, outDefaultConfig, outPrimaryRefreshRateMin,
- outPrimaryRefreshRateMax, outAppRequestRefreshRateMin,
- outAppRequestRefreshRateMax);
-}
-
-status_t SurfaceComposerClient::getDisplayColorModes(const sp<IBinder>& display,
- Vector<ColorMode>* outColorModes) {
- return ComposerService::getComposerService()->getDisplayColorModes(display, outColorModes);
+ ->getDesiredDisplayModeSpecs(displayToken, outDefaultMode, outAllowGroupSwitching,
+ outPrimaryRefreshRateMin, outPrimaryRefreshRateMax,
+ outAppRequestRefreshRateMin, outAppRequestRefreshRateMax);
}
status_t SurfaceComposerClient::getDisplayNativePrimaries(const sp<IBinder>& display,
@@ -1777,31 +1949,15 @@
return ComposerService::getComposerService()->getDisplayNativePrimaries(display, outPrimaries);
}
-ColorMode SurfaceComposerClient::getActiveColorMode(const sp<IBinder>& display) {
- return ComposerService::getComposerService()->getActiveColorMode(display);
-}
-
status_t SurfaceComposerClient::setActiveColorMode(const sp<IBinder>& display,
ColorMode colorMode) {
return ComposerService::getComposerService()->setActiveColorMode(display, colorMode);
}
-bool SurfaceComposerClient::getAutoLowLatencyModeSupport(const sp<IBinder>& display) {
- bool supported = false;
- ComposerService::getComposerService()->getAutoLowLatencyModeSupport(display, &supported);
- return supported;
-}
-
void SurfaceComposerClient::setAutoLowLatencyMode(const sp<IBinder>& display, bool on) {
ComposerService::getComposerService()->setAutoLowLatencyMode(display, on);
}
-bool SurfaceComposerClient::getGameContentTypeSupport(const sp<IBinder>& display) {
- bool supported = false;
- ComposerService::getComposerService()->getGameContentTypeSupport(display, &supported);
- return supported;
-}
-
void SurfaceComposerClient::setGameContentType(const sp<IBinder>& display, bool on) {
ComposerService::getComposerService()->setGameContentType(display, on);
}
@@ -1833,10 +1989,14 @@
return ComposerService::getComposerService()->getAnimationFrameStats(outStats);
}
-status_t SurfaceComposerClient::getHdrCapabilities(const sp<IBinder>& display,
- HdrCapabilities* outCapabilities) {
- return ComposerService::getComposerService()->getHdrCapabilities(display,
- outCapabilities);
+status_t SurfaceComposerClient::overrideHdrTypes(const sp<IBinder>& display,
+ const std::vector<ui::Hdr>& hdrTypes) {
+ return ComposerService::getComposerService()->overrideHdrTypes(display, hdrTypes);
+}
+
+status_t SurfaceComposerClient::onPullAtom(const int32_t atomId, std::string* outData,
+ bool* success) {
+ return ComposerService::getComposerService()->onPullAtom(atomId, outData, success);
}
status_t SurfaceComposerClient::getDisplayedContentSamplingAttributes(const sp<IBinder>& display,
@@ -1882,6 +2042,15 @@
return ComposerService::getComposerService()->removeRegionSamplingListener(listener);
}
+status_t SurfaceComposerClient::addFpsListener(int32_t taskId,
+ const sp<gui::IFpsListener>& listener) {
+ return ComposerService::getComposerService()->addFpsListener(taskId, listener);
+}
+
+status_t SurfaceComposerClient::removeFpsListener(const sp<gui::IFpsListener>& listener) {
+ return ComposerService::getComposerService()->removeFpsListener(listener);
+}
+
bool SurfaceComposerClient::getDisplayBrightnessSupport(const sp<IBinder>& displayToken) {
bool support = false;
ComposerService::getComposerService()->getDisplayBrightnessSupport(displayToken, &support);
@@ -1889,12 +2058,23 @@
}
status_t SurfaceComposerClient::setDisplayBrightness(const sp<IBinder>& displayToken,
- float brightness) {
+ const gui::DisplayBrightness& brightness) {
return ComposerService::getComposerService()->setDisplayBrightness(displayToken, brightness);
}
-status_t SurfaceComposerClient::notifyPowerHint(int32_t hintId) {
- return ComposerService::getComposerService()->notifyPowerHint(hintId);
+status_t SurfaceComposerClient::addHdrLayerInfoListener(
+ const sp<IBinder>& displayToken, const sp<gui::IHdrLayerInfoListener>& listener) {
+ return ComposerService::getComposerService()->addHdrLayerInfoListener(displayToken, listener);
+}
+
+status_t SurfaceComposerClient::removeHdrLayerInfoListener(
+ const sp<IBinder>& displayToken, const sp<gui::IHdrLayerInfoListener>& listener) {
+ return ComposerService::getComposerService()->removeHdrLayerInfoListener(displayToken,
+ listener);
+}
+
+status_t SurfaceComposerClient::notifyPowerBoost(int32_t boostId) {
+ return ComposerService::getComposerService()->notifyPowerBoost(boostId);
}
status_t SurfaceComposerClient::setGlobalShadowSettings(const half4& ambientColor,
@@ -1905,61 +2085,34 @@
lightRadius);
}
+int SurfaceComposerClient::getGPUContextPriority() {
+ return ComposerService::getComposerService()->getGPUContextPriority();
+}
+
// ----------------------------------------------------------------------------
-status_t ScreenshotClient::capture(const sp<IBinder>& display, ui::Dataspace reqDataSpace,
- ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
- uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
- ui::Rotation rotation, bool captureSecureLayers,
- sp<GraphicBuffer>* outBuffer, bool& outCapturedSecureLayers) {
+status_t ScreenshotClient::captureDisplay(const DisplayCaptureArgs& captureArgs,
+ const sp<IScreenCaptureListener>& captureListener) {
sp<ISurfaceComposer> s(ComposerService::getComposerService());
if (s == nullptr) return NO_INIT;
- status_t ret = s->captureScreen(display, outBuffer, outCapturedSecureLayers, reqDataSpace,
- reqPixelFormat, sourceCrop, reqWidth, reqHeight,
- useIdentityTransform, rotation, captureSecureLayers);
- if (ret != NO_ERROR) {
- return ret;
- }
- return ret;
+
+ return s->captureDisplay(captureArgs, captureListener);
}
-status_t ScreenshotClient::capture(const sp<IBinder>& display, ui::Dataspace reqDataSpace,
- ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
- uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
- ui::Rotation rotation, sp<GraphicBuffer>* outBuffer) {
- bool ignored;
- return capture(display, reqDataSpace, reqPixelFormat, sourceCrop, reqWidth, reqHeight,
- useIdentityTransform, rotation, false, outBuffer, ignored);
-}
-
-status_t ScreenshotClient::capture(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace,
- sp<GraphicBuffer>* outBuffer) {
+status_t ScreenshotClient::captureDisplay(uint64_t displayOrLayerStack,
+ const sp<IScreenCaptureListener>& captureListener) {
sp<ISurfaceComposer> s(ComposerService::getComposerService());
if (s == nullptr) return NO_INIT;
- return s->captureScreen(displayOrLayerStack, outDataspace, outBuffer);
+
+ return s->captureDisplay(displayOrLayerStack, captureListener);
}
-status_t ScreenshotClient::captureLayers(const sp<IBinder>& layerHandle, ui::Dataspace reqDataSpace,
- ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
- float frameScale, sp<GraphicBuffer>* outBuffer) {
+status_t ScreenshotClient::captureLayers(const LayerCaptureArgs& captureArgs,
+ const sp<IScreenCaptureListener>& captureListener) {
sp<ISurfaceComposer> s(ComposerService::getComposerService());
if (s == nullptr) return NO_INIT;
- status_t ret = s->captureLayers(layerHandle, outBuffer, reqDataSpace, reqPixelFormat,
- sourceCrop, {}, frameScale, false /* childrenOnly */);
- return ret;
-}
-status_t ScreenshotClient::captureChildLayers(
- const sp<IBinder>& layerHandle, ui::Dataspace reqDataSpace, ui::PixelFormat reqPixelFormat,
- const Rect& sourceCrop,
- const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& excludeHandles,
- float frameScale, sp<GraphicBuffer>* outBuffer) {
- sp<ISurfaceComposer> s(ComposerService::getComposerService());
- if (s == nullptr) return NO_INIT;
- status_t ret =
- s->captureLayers(layerHandle, outBuffer, reqDataSpace, reqPixelFormat, sourceCrop,
- excludeHandles, frameScale, true /* childrenOnly */);
- return ret;
+ return s->captureLayers(captureArgs, captureListener);
}
} // namespace android
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index a332a1f..37750fa 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -24,20 +24,23 @@
#include <android/native_window.h>
#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
#include <utils/Log.h>
#include <utils/threads.h>
#include <binder/IPCThreadState.h>
-#include <ui/DisplayInfo.h>
#include <ui/GraphicBuffer.h>
#include <ui/Rect.h>
+#include <ui/StaticDisplayInfo.h>
#include <gui/BufferQueueCore.h>
+#include <gui/BLASTBufferQueue.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/SurfaceControl.h>
+#include <private/gui/ParcelUtils.h>
namespace android {
@@ -46,18 +49,28 @@
// ============================================================================
SurfaceControl::SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle,
- const sp<IGraphicBufferProducer>& gbp,
- uint32_t transform)
+ const sp<IGraphicBufferProducer>& gbp, int32_t layerId,
+ uint32_t w, uint32_t h, PixelFormat format, uint32_t transform,
+ uint32_t flags)
: mClient(client),
mHandle(handle),
mGraphicBufferProducer(gbp),
- mTransformHint(transform) {}
+ mLayerId(layerId),
+ mTransformHint(transform),
+ mWidth(w),
+ mHeight(h),
+ mFormat(format),
+ mCreateFlags(flags) {}
SurfaceControl::SurfaceControl(const sp<SurfaceControl>& other) {
mClient = other->mClient;
mHandle = other->mHandle;
mGraphicBufferProducer = other->mGraphicBufferProducer;
mTransformHint = other->mTransformHint;
+ mLayerId = other->mLayerId;
+ mWidth = other->mWidth;
+ mHeight = other->mHeight;
+ mCreateFlags = other->mCreateFlags;
}
SurfaceControl::~SurfaceControl()
@@ -66,13 +79,13 @@
// happen without delay, since these resources are quite heavy.
mClient.clear();
mHandle.clear();
- mGraphicBufferProducer.clear();
+ mBbq.clear();
IPCThreadState::self()->flushCommands();
}
void SurfaceControl::disconnect() {
- if (mGraphicBufferProducer != nullptr) {
- mGraphicBufferProducer->disconnect(
+ if (getIGraphicBufferProducer() != nullptr) {
+ getIGraphicBufferProducer()->disconnect(
BufferQueueCore::CURRENTLY_CONNECTED_API);
}
}
@@ -114,21 +127,28 @@
{
sp<IGraphicBufferProducer> bp;
if (control != nullptr) {
- bp = control->mGraphicBufferProducer;
+ bp = control->getIGraphicBufferProducer();
}
return parcel->writeStrongBinder(IInterface::asBinder(bp));
}
-sp<Surface> SurfaceControl::generateSurfaceLocked() const
+sp<Surface> SurfaceControl::generateSurfaceLocked()
{
+ uint32_t ignore;
+ auto flags = mCreateFlags & (ISurfaceComposerClient::eCursorWindow |
+ ISurfaceComposerClient::eOpaque);
+ mBbqChild = mClient->createSurface(String8("bbq-wrapper"), 0, 0, mFormat,
+ flags, mHandle, {}, &ignore);
+ mBbq = new BLASTBufferQueue("bbq-adapter", mBbqChild, mWidth, mHeight, mFormat);
+
// This surface is always consumed by SurfaceFlinger, so the
// producerControlledByApp value doesn't matter; using false.
- mSurfaceData = new Surface(mGraphicBufferProducer, false);
+ mSurfaceData = mBbq->getSurface(true);
return mSurfaceData;
}
-sp<Surface> SurfaceControl::getSurface() const
+sp<Surface> SurfaceControl::getSurface()
{
Mutex::Autolock _l(mLock);
if (mSurfaceData == nullptr) {
@@ -137,21 +157,42 @@
return mSurfaceData;
}
-sp<Surface> SurfaceControl::createSurface() const
+sp<Surface> SurfaceControl::createSurface()
{
- Mutex::Autolock _l(mLock);
- return generateSurfaceLocked();
+ return getSurface();
}
-sp<IBinder> SurfaceControl::getHandle() const
+void SurfaceControl::updateDefaultBufferSize(uint32_t width, uint32_t height) {
+ Mutex::Autolock _l(mLock);
+ mWidth = width; mHeight = height;
+ if (mBbq) {
+ mBbq->update(this, width, height, mFormat);
+ }
+
+}
+
+sp<IBinder> SurfaceControl::getLayerStateHandle() const
{
return mHandle;
}
-sp<IGraphicBufferProducer> SurfaceControl::getIGraphicBufferProducer() const
+sp<IBinder> SurfaceControl::getHandle() const {
+ if (mBbqChild != nullptr) {
+ return mBbqChild->getHandle();
+ }
+ return getLayerStateHandle();
+}
+
+int32_t SurfaceControl::getLayerId() const {
+ return mLayerId;
+}
+
+sp<IGraphicBufferProducer> SurfaceControl::getIGraphicBufferProducer()
{
+ getSurface();
Mutex::Autolock _l(mLock);
- return mGraphicBufferProducer;
+
+ return mBbq->getIGraphicBufferProducer();
}
sp<SurfaceComposerClient> SurfaceControl::getClient() const
@@ -169,31 +210,74 @@
mTransformHint = hint;
}
-void SurfaceControl::writeToParcel(Parcel* parcel)
-{
- parcel->writeStrongBinder(ISurfaceComposerClient::asBinder(mClient->getClient()));
- parcel->writeStrongBinder(mHandle);
- parcel->writeStrongBinder(IGraphicBufferProducer::asBinder(mGraphicBufferProducer));
- parcel->writeUint32(mTransformHint);
+status_t SurfaceControl::writeToParcel(Parcel& parcel) {
+ SAFE_PARCEL(parcel.writeStrongBinder, ISurfaceComposerClient::asBinder(mClient->getClient()));
+ SAFE_PARCEL(parcel.writeStrongBinder, mHandle);
+ SAFE_PARCEL(parcel.writeInt32, mLayerId);
+ SAFE_PARCEL(parcel.writeUint32, mTransformHint);
+ SAFE_PARCEL(parcel.writeUint32, mWidth);
+ SAFE_PARCEL(parcel.writeUint32, mHeight);
+ SAFE_PARCEL(parcel.writeUint32, mFormat);
+
+ return NO_ERROR;
}
-sp<SurfaceControl> SurfaceControl::readFromParcel(const Parcel* parcel) {
- sp<IBinder> client = parcel->readStrongBinder();
- sp<IBinder> handle = parcel->readStrongBinder();
- if (client == nullptr || handle == nullptr)
- {
- ALOGE("Invalid parcel");
- return nullptr;
- }
- sp<IBinder> gbp;
- parcel->readNullableStrongBinder(&gbp);
+status_t SurfaceControl::readFromParcel(const Parcel& parcel,
+ sp<SurfaceControl>* outSurfaceControl) {
+ sp<IBinder> client;
+ sp<IBinder> handle;
+ int32_t layerId;
+ uint32_t transformHint;
+ uint32_t width;
+ uint32_t height;
+ uint32_t format;
- uint32_t transformHint = parcel->readUint32();
+ SAFE_PARCEL(parcel.readStrongBinder, &client);
+ SAFE_PARCEL(parcel.readStrongBinder, &handle);
+ SAFE_PARCEL(parcel.readInt32, &layerId);
+ SAFE_PARCEL(parcel.readUint32, &transformHint);
+ SAFE_PARCEL(parcel.readUint32, &width);
+ SAFE_PARCEL(parcel.readUint32, &height);
+ SAFE_PARCEL(parcel.readUint32, &format);
+
// We aren't the original owner of the surface.
- return new SurfaceControl(new SurfaceComposerClient(
- interface_cast<ISurfaceComposerClient>(client)),
- handle.get(), interface_cast<IGraphicBufferProducer>(gbp),
+ *outSurfaceControl =
+ new SurfaceControl(new SurfaceComposerClient(
+ interface_cast<ISurfaceComposerClient>(client)),
+ handle.get(), nullptr, layerId,
+ width, height, format,
transformHint);
+
+ return NO_ERROR;
+}
+
+status_t SurfaceControl::readNullableFromParcel(const Parcel& parcel,
+ sp<SurfaceControl>* outSurfaceControl) {
+ bool isNotNull;
+ SAFE_PARCEL(parcel.readBool, &isNotNull);
+ if (isNotNull) {
+ SAFE_PARCEL(SurfaceControl::readFromParcel, parcel, outSurfaceControl);
+ }
+
+ return NO_ERROR;
+}
+
+status_t SurfaceControl::writeNullableToParcel(Parcel& parcel,
+ const sp<SurfaceControl>& surfaceControl) {
+ auto isNotNull = surfaceControl != nullptr;
+ SAFE_PARCEL(parcel.writeBool, isNotNull);
+ if (isNotNull) {
+ SAFE_PARCEL(surfaceControl->writeToParcel, parcel);
+ }
+
+ return NO_ERROR;
+}
+
+sp<SurfaceControl> SurfaceControl::getParentingLayer() {
+ if (mBbqChild != nullptr) {
+ return mBbqChild;
+ }
+ return this;
}
// ----------------------------------------------------------------------------
diff --git a/libs/gui/SyncFeatures.cpp b/libs/gui/SyncFeatures.cpp
index fcae05c..1a8fc1a 100644
--- a/libs/gui/SyncFeatures.cpp
+++ b/libs/gui/SyncFeatures.cpp
@@ -27,8 +27,6 @@
#include <private/gui/SyncFeatures.h>
-extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
-
namespace android {
ANDROID_SINGLETON_STATIC_INSTANCE(SyncFeatures);
@@ -40,8 +38,8 @@
EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
// This can only be called after EGL has been initialized; otherwise the
// check below will abort.
- const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS);
- LOG_ALWAYS_FATAL_IF(exts == nullptr, "eglQueryStringImplementationANDROID failed");
+ const char* exts = eglQueryString(dpy, EGL_EXTENSIONS);
+ LOG_ALWAYS_FATAL_IF(exts == nullptr, "eglQueryString failed");
if (strstr(exts, "EGL_ANDROID_native_fence_sync")) {
// This makes GLConsumer use the EGL_ANDROID_native_fence_sync
// extension to create Android native fences to signal when all
@@ -73,15 +71,7 @@
return mHasNativeFenceSync;
}
bool SyncFeatures::useFenceSync() const {
-#ifdef DONT_USE_FENCE_SYNC
- // on some devices it's better to not use EGL_KHR_fence_sync
- // even if they have it
- return false;
-#else
- // currently we shall only attempt to use EGL_KHR_fence_sync if
- // USE_FENCE_SYNC is set in our makefile
return !mHasNativeFenceSync && mHasFenceSync;
-#endif
}
bool SyncFeatures::useWaitSync() const {
return (useNativeFenceSync() || useFenceSync()) && mHasWaitSync;
diff --git a/libs/gui/TransactionTracing.cpp b/libs/gui/TransactionTracing.cpp
new file mode 100644
index 0000000..eedc3df
--- /dev/null
+++ b/libs/gui/TransactionTracing.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2020 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 "gui/TransactionTracing.h"
+#include "gui/ISurfaceComposer.h"
+
+#include <private/gui/ComposerService.h>
+
+namespace android {
+
+sp<TransactionTraceListener> TransactionTraceListener::sInstance = nullptr;
+std::mutex TransactionTraceListener::sMutex;
+
+TransactionTraceListener::TransactionTraceListener() {}
+
+sp<TransactionTraceListener> TransactionTraceListener::getInstance() {
+ const std::lock_guard<std::mutex> lock(sMutex);
+
+ if (sInstance == nullptr) {
+ sInstance = new TransactionTraceListener;
+
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ sf->addTransactionTraceListener(sInstance);
+ }
+
+ return sInstance;
+}
+
+binder::Status TransactionTraceListener::onToggled(bool enabled) {
+ ALOGD("TransactionTraceListener: onToggled listener called");
+ mTracingEnabled = enabled;
+
+ return binder::Status::ok();
+}
+
+bool TransactionTraceListener::isTracingEnabled() {
+ return mTracingEnabled;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/aidl/android/gui/DisplayBrightness.aidl b/libs/gui/aidl/android/gui/DisplayBrightness.aidl
new file mode 100644
index 0000000..bdb8c63
--- /dev/null
+++ b/libs/gui/aidl/android/gui/DisplayBrightness.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2021 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 android.gui;
+
+/** @hide */
+parcelable DisplayBrightness {
+ // Range 0-1f, the desired sdr white point brightness
+ float sdrWhitePoint = 0f;
+
+ // The SDR white point in nits. -1 if unknown
+ float sdrWhitePointNits = -1f;
+
+ // Range 0-1f, the desired brightness of the display itself. -1f to turn the backlight off
+ float displayBrightness = 0f;
+
+ // The desired brightness of the display in nits. -1 if unknown
+ float displayBrightnessNits = -1f;
+}
\ No newline at end of file
diff --git a/libs/ui/UiConfig.cpp b/libs/gui/aidl/android/gui/IFpsListener.aidl
similarity index 67%
copy from libs/ui/UiConfig.cpp
copy to libs/gui/aidl/android/gui/IFpsListener.aidl
index 0ac863d..63d333c 100644
--- a/libs/ui/UiConfig.cpp
+++ b/libs/gui/aidl/android/gui/IFpsListener.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright 2021 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.
@@ -14,15 +14,11 @@
* limitations under the License.
*/
-#include <ui/UiConfig.h>
+package android.gui;
-namespace android {
+/** @hide */
+oneway interface IFpsListener {
-void appendUiConfigString(std::string& configStr) {
- static const char* config =
- " [libui]";
- configStr.append(config);
-}
-
-
-}; // namespace android
+ // Reports the most recent recorded fps for the tree rooted at this layer
+ void onFpsReported(float fps);
+}
\ No newline at end of file
diff --git a/libs/gui/aidl/android/gui/IHdrLayerInfoListener.aidl b/libs/gui/aidl/android/gui/IHdrLayerInfoListener.aidl
new file mode 100644
index 0000000..fc809c4
--- /dev/null
+++ b/libs/gui/aidl/android/gui/IHdrLayerInfoListener.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2021 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 android.gui;
+
+/** @hide */
+oneway interface IHdrLayerInfoListener {
+ // Callback with the total number of HDR layers, the dimensions of the largest layer,
+ // and a placeholder flags
+ // TODO (b/182312559): Define the flags (likely need an indicator that a UDFPS layer is present)
+ void onHdrLayerInfoChanged(int numberOfHdrLayers, int maxW, int maxH, int flags);
+}
\ No newline at end of file
diff --git a/libs/ui/UiConfig.cpp b/libs/gui/aidl/android/gui/IScreenCaptureListener.aidl
similarity index 67%
copy from libs/ui/UiConfig.cpp
copy to libs/gui/aidl/android/gui/IScreenCaptureListener.aidl
index 0ac863d..e0f66cd 100644
--- a/libs/ui/UiConfig.cpp
+++ b/libs/gui/aidl/android/gui/IScreenCaptureListener.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright 2021 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.
@@ -14,15 +14,11 @@
* limitations under the License.
*/
-#include <ui/UiConfig.h>
+package android.gui;
-namespace android {
+import android.gui.ScreenCaptureResults;
-void appendUiConfigString(std::string& configStr) {
- static const char* config =
- " [libui]";
- configStr.append(config);
-}
-
-
-}; // namespace android
+/** @hide */
+oneway interface IScreenCaptureListener {
+ void onScreenCaptureCompleted(in ScreenCaptureResults captureResults);
+}
\ No newline at end of file
diff --git a/libs/gui/aidl/android/gui/ITransactionTraceListener.aidl b/libs/gui/aidl/android/gui/ITransactionTraceListener.aidl
new file mode 100644
index 0000000..5cd12fd
--- /dev/null
+++ b/libs/gui/aidl/android/gui/ITransactionTraceListener.aidl
@@ -0,0 +1,6 @@
+package android.gui;
+
+/** @hide */
+interface ITransactionTraceListener {
+ void onToggled(boolean enabled);
+}
\ No newline at end of file
diff --git a/libs/ui/UiConfig.cpp b/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl
similarity index 67%
copy from libs/ui/UiConfig.cpp
copy to libs/gui/aidl/android/gui/ScreenCaptureResults.aidl
index 0ac863d..9908edd 100644
--- a/libs/ui/UiConfig.cpp
+++ b/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright 2021 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.
@@ -14,15 +14,6 @@
* limitations under the License.
*/
-#include <ui/UiConfig.h>
+package android.gui;
-namespace android {
-
-void appendUiConfigString(std::string& configStr) {
- static const char* config =
- " [libui]";
- configStr.append(config);
-}
-
-
-}; // namespace android
+parcelable ScreenCaptureResults cpp_header "gui/ScreenCaptureResults.h";
\ No newline at end of file
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 2320771..139dbb7 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -28,9 +28,11 @@
#include <system/window.h>
#include <thread>
+#include <queue>
namespace android {
+class BLASTBufferQueue;
class BufferItemConsumer;
class BLASTBufferItemConsumer : public BufferItemConsumer {
@@ -39,51 +41,69 @@
int bufferCount, bool controlledByApp)
: BufferItemConsumer(consumer, consumerUsage, bufferCount, controlledByApp),
mCurrentlyConnected(false),
- mPreviouslyConnected(false) {}
+ mPreviouslyConnected(false),
+ mBLASTBufferQueue(nullptr) {}
void onDisconnect() override;
void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
- FrameEventHistoryDelta* outDelta) override
- REQUIRES(mFrameEventHistoryMutex);
+ FrameEventHistoryDelta* outDelta) override REQUIRES(mMutex);
void updateFrameTimestamps(uint64_t frameNumber, nsecs_t refreshStartTime,
const sp<Fence>& gpuCompositionDoneFence,
const sp<Fence>& presentFence, const sp<Fence>& prevReleaseFence,
CompositorTiming compositorTiming, nsecs_t latchTime,
- nsecs_t dequeueReadyTime) REQUIRES(mFrameEventHistoryMutex);
+ nsecs_t dequeueReadyTime) REQUIRES(mMutex);
void getConnectionEvents(uint64_t frameNumber, bool* needsDisconnect);
+ void setBlastBufferQueue(BLASTBufferQueue* blastbufferqueue) REQUIRES(mMutex);
+
+protected:
+ void onSidebandStreamChanged() override REQUIRES(mMutex);
private:
uint64_t mCurrentFrameNumber = 0;
- Mutex mFrameEventHistoryMutex;
- ConsumerFrameEventHistory mFrameEventHistory GUARDED_BY(mFrameEventHistoryMutex);
- std::queue<uint64_t> mDisconnectEvents GUARDED_BY(mFrameEventHistoryMutex);
- bool mCurrentlyConnected GUARDED_BY(mFrameEventHistoryMutex);
- bool mPreviouslyConnected GUARDED_BY(mFrameEventHistoryMutex);
+ Mutex mMutex;
+ ConsumerFrameEventHistory mFrameEventHistory GUARDED_BY(mMutex);
+ std::queue<uint64_t> mDisconnectEvents GUARDED_BY(mMutex);
+ bool mCurrentlyConnected GUARDED_BY(mMutex);
+ bool mPreviouslyConnected GUARDED_BY(mMutex);
+ BLASTBufferQueue* mBLASTBufferQueue GUARDED_BY(mMutex);
};
class BLASTBufferQueue
: public ConsumerBase::FrameAvailableListener, public BufferItemConsumer::BufferFreedListener
{
public:
- BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, int height,
- bool enableTripleBuffering = true);
+ BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, int width,
+ int height, int32_t format);
sp<IGraphicBufferProducer> getIGraphicBufferProducer() const {
return mProducer;
}
+ sp<Surface> getSurface(bool includeSurfaceControlHandle);
void onBufferFreed(const wp<GraphicBuffer>&/* graphicBuffer*/) override { /* TODO */ }
- void onFrameReplaced(const BufferItem& item) override {onFrameAvailable(item);}
+ void onFrameReplaced(const BufferItem& item) override;
void onFrameAvailable(const BufferItem& item) override;
+ void onFrameDequeued(const uint64_t) override;
+ void onFrameCancelled(const uint64_t) override;
void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
const std::vector<SurfaceControlStats>& stats);
+ void releaseBufferCallback(uint64_t graphicBufferId, const sp<Fence>& releaseFence);
void setNextTransaction(SurfaceComposerClient::Transaction *t);
+ void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber);
+ void setTransactionCompleteCallback(uint64_t frameNumber,
+ std::function<void(int64_t)>&& transactionCompleteCallback);
- void update(const sp<SurfaceControl>& surface, int width, int height);
+ void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, int32_t format);
+ void flushShadowQueue() {}
- virtual ~BLASTBufferQueue() = default;
+ status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless);
+ status_t setFrameTimelineInfo(const FrameTimelineInfo& info);
+
+ void setSidebandStream(const sp<NativeHandle>& stream);
+
+ virtual ~BLASTBufferQueue();
private:
friend class BLASTBufferQueueHelper;
@@ -91,10 +111,17 @@
// can't be copied
BLASTBufferQueue& operator = (const BLASTBufferQueue& rhs);
BLASTBufferQueue(const BLASTBufferQueue& rhs);
+ void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
+ sp<IGraphicBufferConsumer>* outConsumer);
void processNextBufferLocked(bool useNextTransaction) REQUIRES(mMutex);
- Rect computeCrop(const BufferItem& item);
+ Rect computeCrop(const BufferItem& item) REQUIRES(mMutex);
+ // Return true if we need to reject the buffer based on the scaling mode and the buffer size.
+ bool rejectBuffer(const BufferItem& item) REQUIRES(mMutex);
+ bool maxBuffersAcquired(bool includeExtraAcquire) const REQUIRES(mMutex);
+ static PixelFormat convertBufferFormat(PixelFormat& format);
+ std::string mName;
sp<SurfaceControl> mSurfaceControl;
std::mutex mMutex;
@@ -107,16 +134,46 @@
int32_t mNumFrameAvailable GUARDED_BY(mMutex);
int32_t mNumAcquired GUARDED_BY(mMutex);
- struct PendingReleaseItem {
- BufferItem item;
- sp<Fence> releaseFence;
+ // Keep a reference to the submitted buffers so we can release when surfaceflinger drops the
+ // buffer or the buffer has been presented and a new buffer is ready to be presented.
+ std::unordered_map<uint64_t /* bufferId */, BufferItem> mSubmitted GUARDED_BY(mMutex);
+
+ ui::Size mSize GUARDED_BY(mMutex);
+ ui::Size mRequestedSize GUARDED_BY(mMutex);
+ int32_t mFormat GUARDED_BY(mMutex);
+
+ struct BufferInfo {
+ bool hasBuffer = false;
+ uint32_t width;
+ uint32_t height;
+ uint32_t transform;
+ // This is used to check if we should update the blast layer size immediately or wait until
+ // we get the next buffer. This will support scenarios where the layer can change sizes
+ // and the buffer will scale to fit the new size.
+ uint32_t scalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
+ Rect crop;
+
+ void update(bool hasBuffer, uint32_t width, uint32_t height, uint32_t transform,
+ uint32_t scalingMode, const Rect& crop) {
+ this->hasBuffer = hasBuffer;
+ this->width = width;
+ this->height = height;
+ this->transform = transform;
+ this->scalingMode = scalingMode;
+ if (!crop.isEmpty()) {
+ this->crop = crop;
+ } else {
+ this->crop = Rect(width, height);
+ }
+ }
};
- std::queue<const BufferItem> mSubmitted GUARDED_BY(mMutex);
- PendingReleaseItem mPendingReleaseItem GUARDED_BY(mMutex);
-
- int mWidth GUARDED_BY(mMutex);
- int mHeight GUARDED_BY(mMutex);
+ // Last acquired buffer's info. This is used to calculate the correct scale when size change is
+ // requested. We need to use the old buffer's info to determine what scale we need to apply to
+ // ensure the correct size.
+ BufferInfo mLastBufferInfo GUARDED_BY(mMutex);
+ void setMatrix(SurfaceComposerClient::Transaction* t, const BufferInfo& bufferInfo)
+ REQUIRES(mMutex);
uint32_t mTransformHint GUARDED_BY(mMutex);
@@ -125,6 +182,37 @@
sp<BLASTBufferItemConsumer> mBufferItemConsumer;
SurfaceComposerClient::Transaction* mNextTransaction GUARDED_BY(mMutex);
+ std::vector<std::tuple<uint64_t /* framenumber */, SurfaceComposerClient::Transaction>>
+ mPendingTransactions GUARDED_BY(mMutex);
+
+ // Last requested auto refresh state set by the producer. The state indicates that the consumer
+ // should acquire the next frame as soon as it can and not wait for a frame to become available.
+ // This is only relevant for shared buffer mode.
+ bool mAutoRefresh GUARDED_BY(mMutex) = false;
+
+ std::queue<FrameTimelineInfo> mNextFrameTimelineInfoQueue GUARDED_BY(mMutex);
+
+ // Tracks the last acquired frame number
+ uint64_t mLastAcquiredFrameNumber GUARDED_BY(mMutex) = 0;
+
+ std::function<void(int64_t)> mTransactionCompleteCallback GUARDED_BY(mMutex) = nullptr;
+ uint64_t mTransactionCompleteFrameNumber GUARDED_BY(mMutex){0};
+
+ // Queues up transactions using this token in SurfaceFlinger. This prevents queued up
+ // transactions from other parts of the client from blocking this transaction.
+ const sp<IBinder> mApplyToken GUARDED_BY(mMutex) = new BBinder();
+
+ // Guards access to mDequeueTimestamps since we cannot hold to mMutex in onFrameDequeued or
+ // we will deadlock.
+ std::mutex mTimestampMutex;
+ // Tracks buffer dequeue times by the client. This info is sent to SurfaceFlinger which uses
+ // it for debugging purposes.
+ std::unordered_map<uint64_t /* bufferId */, nsecs_t> mDequeueTimestamps
+ GUARDED_BY(mTimestampMutex);
+
+ // Keep track of SurfaceControls that have submitted a transaction and BBQ is waiting on a
+ // callback for them.
+ std::queue<sp<SurfaceControl>> mSurfaceControlsWithPendingCallback GUARDED_BY(mMutex);
};
} // namespace android
diff --git a/libs/gui/include/gui/BufferQueueConsumer.h b/libs/gui/include/gui/BufferQueueConsumer.h
index 7db69ec..6aa801a 100644
--- a/libs/gui/include/gui/BufferQueueConsumer.h
+++ b/libs/gui/include/gui/BufferQueueConsumer.h
@@ -174,6 +174,10 @@
// Value used to determine if present time is valid.
constexpr static int MAX_REASONABLE_NSEC = 1'000'000'000ULL; // 1 second
+ // This allows the consumer to acquire an additional buffer if that buffer is not droppable and
+ // will eventually be released or acquired by the consumer.
+ void setAllowExtraAcquire(bool /* allow */);
+
private:
sp<BufferQueueCore> mCore;
diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h
index 557c28b..8d0828d 100644
--- a/libs/gui/include/gui/BufferQueueCore.h
+++ b/libs/gui/include/gui/BufferQueueCore.h
@@ -354,6 +354,9 @@
// mTransformHintInUse is to cache the mTransformHint used by the producer.
uint32_t mTransformHintInUse;
+ // This allows the consumer to acquire an additional buffer if that buffer is not droppable and
+ // will eventually be released or acquired by the consumer.
+ bool mAllowExtraAcquire = false;
}; // class BufferQueueCore
} // namespace android
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index eb5b004..4ade240 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -19,14 +19,28 @@
#include <utils/Looper.h>
namespace android {
+using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
+
+struct VsyncEventData {
+ // The Vsync Id corresponsing to this vsync event. This will be used to
+ // populate ISurfaceComposer::setFrameTimelineVsync and
+ // SurfaceComposerClient::setFrameTimelineVsync
+ int64_t id = FrameTimelineInfo::INVALID_VSYNC_ID;
+
+ // The deadline in CLOCK_MONOTONIC that the app needs to complete its
+ // frame by (both on the CPU and the GPU)
+ int64_t deadlineTimestamp = std::numeric_limits<int64_t>::max();
+
+ // The current frame interval in ns when this frame was scheduled.
+ int64_t frameInterval = 0;
+};
class DisplayEventDispatcher : public LooperCallback {
public:
explicit DisplayEventDispatcher(
const sp<Looper>& looper,
ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp,
- ISurfaceComposer::ConfigChanged configChanged =
- ISurfaceComposer::eConfigChangedSuppress);
+ ISurfaceComposer::EventRegistrationFlags eventRegistration = {});
status_t initialize();
void dispose();
@@ -43,16 +57,22 @@
DisplayEventReceiver mReceiver;
bool mWaitingForVsync;
- virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) = 0;
+ std::vector<FrameRateOverride> mFrameRateOverrides;
+
+ virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
+ VsyncEventData vsyncEventData) = 0;
virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId,
bool connected) = 0;
- virtual void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
- int32_t configId, nsecs_t vsyncPeriod) = 0;
+ virtual void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId,
+ nsecs_t vsyncPeriod) = 0;
// AChoreographer-specific hook for processing null-events so that looper
// can be properly poked.
virtual void dispatchNullEvent(nsecs_t timestamp, PhysicalDisplayId displayId) = 0;
+ virtual void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId,
+ std::vector<FrameRateOverride> overrides) = 0;
+
bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId,
- uint32_t* outCount);
+ uint32_t* outCount, VsyncEventData* outVsyncEventData);
};
} // namespace android
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index 0e10d1a..0dffbde 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -52,37 +52,53 @@
enum {
DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'),
DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'),
- DISPLAY_EVENT_CONFIG_CHANGED = fourcc('c', 'o', 'n', 'f'),
+ DISPLAY_EVENT_MODE_CHANGE = fourcc('m', 'o', 'd', 'e'),
DISPLAY_EVENT_NULL = fourcc('n', 'u', 'l', 'l'),
+ DISPLAY_EVENT_FRAME_RATE_OVERRIDE = fourcc('r', 'a', 't', 'e'),
+ DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH = fourcc('f', 'l', 's', 'h'),
};
struct Event {
+ // We add __attribute__((aligned(8))) for nsecs_t fields because
+ // we need to make sure all fields are aligned the same with x86
+ // and x64 (long long has different default alignment):
+ //
+ // https://en.wikipedia.org/wiki/Data_structure_alignment
struct Header {
uint32_t type;
- PhysicalDisplayId displayId;
+ PhysicalDisplayId displayId __attribute__((aligned(8)));
nsecs_t timestamp __attribute__((aligned(8)));
};
struct VSync {
uint32_t count;
- nsecs_t expectedVSyncTimestamp;
+ nsecs_t expectedVSyncTimestamp __attribute__((aligned(8)));
+ nsecs_t deadlineTimestamp __attribute__((aligned(8)));
+ nsecs_t frameInterval __attribute__((aligned(8)));
+ int64_t vsyncId;
};
struct Hotplug {
bool connected;
};
- struct Config {
- int32_t configId;
- nsecs_t vsyncPeriod;
+ struct ModeChange {
+ int32_t modeId;
+ nsecs_t vsyncPeriod __attribute__((aligned(8)));
+ };
+
+ struct FrameRateOverride {
+ uid_t uid __attribute__((aligned(8)));
+ float frameRateHz __attribute__((aligned(8)));
};
Header header;
union {
VSync vsync;
Hotplug hotplug;
- Config config;
+ ModeChange modeChange;
+ FrameRateOverride frameRateOverride;
};
};
@@ -91,13 +107,12 @@
* DisplayEventReceiver creates and registers an event connection with
* SurfaceFlinger. VSync events are disabled by default. Call setVSyncRate
* or requestNextVsync to receive them.
- * To receive Config Changed events specify this in the constructor.
- * Other events start being delivered immediately.
+ * To receive ModeChanged and/or FrameRateOverrides events specify this in
+ * the constructor. Other events start being delivered immediately.
*/
explicit DisplayEventReceiver(
ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp,
- ISurfaceComposer::ConfigChanged configChanged =
- ISurfaceComposer::eConfigChangedSuppress);
+ ISurfaceComposer::EventRegistrationFlags eventRegistration = {});
/*
* ~DisplayEventReceiver severs the connection with SurfaceFlinger, new events
diff --git a/libs/gui/include/gui/FrameTimelineInfo.h b/libs/gui/include/gui/FrameTimelineInfo.h
new file mode 100644
index 0000000..a23c202
--- /dev/null
+++ b/libs/gui/include/gui/FrameTimelineInfo.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include <binder/Parcel.h>
+
+namespace android {
+
+struct FrameTimelineInfo {
+ // Needs to be in sync with android.graphics.FrameInfo.INVALID_VSYNC_ID in java
+ static constexpr int64_t INVALID_VSYNC_ID = -1;
+
+ // The vsync id that was used to start the transaction
+ int64_t vsyncId = INVALID_VSYNC_ID;
+
+ // The id of the input event that caused this buffer
+ // Default is android::os::IInputConstants::INVALID_INPUT_EVENT_ID = 0
+ // We copy the value of the input event ID instead of including the header, because libgui
+ // header libraries containing FrameTimelineInfo must be available to vendors, but libinput is
+ // not directly vendor available.
+ int32_t inputEventId = 0;
+
+ status_t write(Parcel& output) const;
+ status_t read(const Parcel& input);
+
+ void merge(const FrameTimelineInfo& other);
+ void clear();
+};
+
+} // namespace android
diff --git a/libs/gui/include/gui/GuiConfig.h b/libs/gui/include/gui/GuiConfig.h
deleted file mode 100644
index 7aa5432..0000000
--- a/libs/gui/include/gui/GuiConfig.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2012 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.
- */
-
-#ifndef ANDROID_GUI_CONFIG_H
-#define ANDROID_GUI_CONFIG_H
-
-#include <string>
-
-namespace android {
-
-// Append the libgui configuration details to configStr.
-void appendGuiConfigString(std::string& configStr);
-
-}; // namespace android
-
-#endif /*ANDROID_GUI_CONFIG_H*/
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index 45e0a13..c3b9262 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -38,6 +38,9 @@
#include <android/hardware/graphics/bufferqueue/1.0/IGraphicBufferProducer.h>
#include <android/hardware/graphics/bufferqueue/2.0/IGraphicBufferProducer.h>
+#include <optional>
+#include <vector>
+
namespace android {
// ----------------------------------------------------------------------------
@@ -289,8 +292,9 @@
const sp<GraphicBuffer>& buffer) = 0;
struct QueueBufferInput : public Flattenable<QueueBufferInput> {
- friend class Flattenable<QueueBufferInput>;
- explicit inline QueueBufferInput(const Parcel& parcel);
+ explicit inline QueueBufferInput(const Parcel& parcel) {
+ parcel.read(*this);
+ }
// timestamp - a monotonically increasing value in nanoseconds
// isAutoTimestamp - if the timestamp was synthesized at queue time
@@ -304,21 +308,29 @@
// camera mode).
// getFrameTimestamps - whether or not the latest frame timestamps
// should be retrieved from the consumer.
+ // slot - the slot index to queue. This is used only by queueBuffers().
+ // queueBuffer() ignores this value and uses the argument `slot`
+ // instead.
inline QueueBufferInput(int64_t _timestamp, bool _isAutoTimestamp,
android_dataspace _dataSpace, const Rect& _crop,
int _scalingMode, uint32_t _transform, const sp<Fence>& _fence,
- uint32_t _sticky = 0, bool _getFrameTimestamps = false)
+ uint32_t _sticky = 0, bool _getFrameTimestamps = false,
+ int _slot = -1)
: timestamp(_timestamp), isAutoTimestamp(_isAutoTimestamp),
dataSpace(_dataSpace), crop(_crop), scalingMode(_scalingMode),
- transform(_transform), stickyTransform(_sticky), fence(_fence),
- surfaceDamage(), getFrameTimestamps(_getFrameTimestamps) { }
+ transform(_transform), stickyTransform(_sticky),
+ fence(_fence), surfaceDamage(),
+ getFrameTimestamps(_getFrameTimestamps), slot(_slot) { }
+
+ QueueBufferInput() = default;
inline void deflate(int64_t* outTimestamp, bool* outIsAutoTimestamp,
android_dataspace* outDataSpace,
Rect* outCrop, int* outScalingMode,
uint32_t* outTransform, sp<Fence>* outFence,
uint32_t* outStickyTransform = nullptr,
- bool* outGetFrameTimestamps = nullptr) const {
+ bool* outGetFrameTimestamps = nullptr,
+ int* outSlot = nullptr) const {
*outTimestamp = timestamp;
*outIsAutoTimestamp = bool(isAutoTimestamp);
*outDataSpace = dataSpace;
@@ -332,6 +344,9 @@
if (outGetFrameTimestamps) {
*outGetFrameTimestamps = getFrameTimestamps;
}
+ if (outSlot) {
+ *outSlot = slot;
+ }
}
// Flattenable protocol
@@ -357,6 +372,7 @@
sp<Fence> fence;
Region surfaceDamage;
bool getFrameTimestamps{false};
+ int slot{-1};
HdrMetadata hdrMetadata;
};
@@ -385,6 +401,7 @@
FrameEventHistoryDelta frameTimestamps;
bool bufferReplaced{false};
int maxBufferCount{0};
+ status_t result{NO_ERROR};
};
// queueBuffer indicates that the client has finished filling in the
@@ -404,6 +421,10 @@
// Upon success, the output will be filled with meaningful values
// (refer to the documentation below).
//
+ // Note: QueueBufferInput::slot was added to QueueBufferInput to be used by
+ // queueBuffers(), the batched version of queueBuffer(). The non-batched
+ // method (queueBuffer()) uses `slot` and ignores `input.slot`.
+ //
// Return of a value other than NO_ERROR means an error has occurred:
// * NO_INIT - the buffer queue has been abandoned or the producer is not
// connected.
@@ -639,6 +660,147 @@
// the width and height used for dequeueBuffer will be additionally swapped.
virtual status_t setAutoPrerotation(bool autoPrerotation);
+ struct RequestBufferOutput : public Flattenable<RequestBufferOutput> {
+ RequestBufferOutput() = default;
+
+ // Flattenable protocol
+ static constexpr size_t minFlattenedSize();
+ size_t getFlattenedSize() const;
+ size_t getFdCount() const;
+ status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
+ status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
+
+ status_t result;
+ sp<GraphicBuffer> buffer;
+ };
+
+ // Batched version of requestBuffer().
+ // This method behaves like a sequence of requestBuffer() calls.
+ // The return value of the batched method will only be about the
+ // transaction. For a local call, the return value will always be NO_ERROR.
+ virtual status_t requestBuffers(
+ const std::vector<int32_t>& slots,
+ std::vector<RequestBufferOutput>* outputs);
+
+ struct DequeueBufferInput : public LightFlattenable<DequeueBufferInput> {
+ DequeueBufferInput() = default;
+
+ // LightFlattenable protocol
+ inline bool isFixedSize() const { return true; }
+ size_t getFlattenedSize() const;
+ status_t flatten(void* buffer, size_t size) const;
+ status_t unflatten(void const* buffer, size_t size);
+
+ uint32_t width;
+ uint32_t height;
+ PixelFormat format;
+ uint64_t usage;
+ bool getTimestamps;
+ };
+
+ struct DequeueBufferOutput : public Flattenable<DequeueBufferOutput> {
+ DequeueBufferOutput() = default;
+
+ // Flattenable protocol
+ static constexpr size_t minFlattenedSize();
+ size_t getFlattenedSize() const;
+ size_t getFdCount() const;
+ status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
+ status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
+
+ status_t result;
+ int slot = -1;
+ sp<Fence> fence = Fence::NO_FENCE;
+ uint64_t bufferAge;
+ std::optional<FrameEventHistoryDelta> timestamps;
+ };
+
+ // Batched version of dequeueBuffer().
+ // This method behaves like a sequence of dequeueBuffer() calls.
+ // The return value of the batched method will only be about the
+ // transaction. For a local call, the return value will always be NO_ERROR.
+ virtual status_t dequeueBuffers(
+ const std::vector<DequeueBufferInput>& inputs,
+ std::vector<DequeueBufferOutput>* outputs);
+
+ // Batched version of detachBuffer().
+ // This method behaves like a sequence of detachBuffer() calls.
+ // The return value of the batched method will only be about the
+ // transaction. For a local call, the return value will always be NO_ERROR.
+ virtual status_t detachBuffers(const std::vector<int32_t>& slots,
+ std::vector<status_t>* results);
+
+
+ struct AttachBufferOutput : public LightFlattenable<AttachBufferOutput> {
+ AttachBufferOutput() = default;
+
+ // LightFlattenable protocol
+ inline bool isFixedSize() const { return true; }
+ size_t getFlattenedSize() const;
+ status_t flatten(void* buffer, size_t size) const;
+ status_t unflatten(void const* buffer, size_t size);
+
+ status_t result;
+ int slot;
+ };
+ // Batched version of attachBuffer().
+ // This method behaves like a sequence of attachBuffer() calls.
+ // The return value of the batched method will only be about the
+ // transaction. For a local call, the return value will always be NO_ERROR.
+ virtual status_t attachBuffers(
+ const std::vector<sp<GraphicBuffer>>& buffers,
+ std::vector<AttachBufferOutput>* outputs);
+
+ // Batched version of queueBuffer().
+ // This method behaves like a sequence of queueBuffer() calls.
+ // The return value of the batched method will only be about the
+ // transaction. For a local call, the return value will always be NO_ERROR.
+ //
+ // Note: QueueBufferInput::slot was added to QueueBufferInput to include the
+ // `slot` input argument of the non-batched method queueBuffer().
+ virtual status_t queueBuffers(const std::vector<QueueBufferInput>& inputs,
+ std::vector<QueueBufferOutput>* outputs);
+
+ struct CancelBufferInput : public Flattenable<CancelBufferInput> {
+ CancelBufferInput() = default;
+
+ // Flattenable protocol
+ static constexpr size_t minFlattenedSize();
+ size_t getFlattenedSize() const;
+ size_t getFdCount() const;
+ status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
+ status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
+
+ int slot;
+ sp<Fence> fence;
+ };
+ // Batched version of cancelBuffer().
+ // This method behaves like a sequence of cancelBuffer() calls.
+ // The return value of the batched method will only be about the
+ // transaction. For a local call, the return value will always be NO_ERROR.
+ virtual status_t cancelBuffers(
+ const std::vector<CancelBufferInput>& inputs,
+ std::vector<status_t>* results);
+
+ struct QueryOutput : public LightFlattenable<QueryOutput> {
+ QueryOutput() = default;
+
+ // LightFlattenable protocol
+ inline bool isFixedSize() const { return true; }
+ size_t getFlattenedSize() const;
+ status_t flatten(void* buffer, size_t size) const;
+ status_t unflatten(void const* buffer, size_t size);
+
+ status_t result;
+ int64_t value;
+ };
+ // Batched version of query().
+ // This method behaves like a sequence of query() calls.
+ // The return value of the batched method will only be about the
+ // transaction. For a local call, the return value will always be NO_ERROR.
+ virtual status_t query(const std::vector<int32_t> inputs,
+ std::vector<QueryOutput>* outputs);
+
#ifndef NO_BINDER
// Static method exports any IGraphicBufferProducer object to a parcel. It
// handles null producer as well.
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 8d3160a..cb04689 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -16,25 +16,28 @@
#pragma once
-#include <stdint.h>
-#include <sys/types.h>
-
+#include <android/gui/DisplayBrightness.h>
+#include <android/gui/IFpsListener.h>
+#include <android/gui/IHdrLayerInfoListener.h>
+#include <android/gui/IScreenCaptureListener.h>
+#include <android/gui/ITransactionTraceListener.h>
#include <binder/IBinder.h>
#include <binder/IInterface.h>
-
+#include <gui/FrameTimelineInfo.h>
#include <gui/ITransactionCompletedListener.h>
-
+#include <input/Flags.h>
#include <math/vec4.h>
-
+#include <stdint.h>
+#include <sys/types.h>
#include <ui/ConfigStoreTypes.h>
+#include <ui/DisplayId.h>
+#include <ui/DisplayMode.h>
#include <ui/DisplayedFrameStats.h>
#include <ui/FrameStats.h>
#include <ui/GraphicBuffer.h>
#include <ui/GraphicTypes.h>
-#include <ui/PhysicalDisplayId.h>
#include <ui/PixelFormat.h>
#include <ui/Rotation.h>
-
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>
@@ -48,11 +51,11 @@
struct client_cache_t;
struct ComposerState;
-struct DisplayConfig;
-struct DisplayInfo;
+struct DisplayCaptureArgs;
struct DisplayStatInfo;
struct DisplayState;
struct InputWindowCommands;
+struct LayerCaptureArgs;
class LayerDebugInfo;
class HdrCapabilities;
class IDisplayEventConnection;
@@ -62,9 +65,14 @@
class Rect;
enum class FrameEvent;
+using gui::IScreenCaptureListener;
+
namespace ui {
+struct DisplayMode;
struct DisplayState;
+struct DynamicDisplayInfo;
+struct StaticDisplayInfo;
} // namespace ui
@@ -83,18 +91,15 @@
eSynchronous = 0x01,
eAnimation = 0x02,
- // DEPRECATED - use eExplicitEarlyWakeup[Start|End]
- eEarlyWakeup = 0x04,
-
// Explicit indication that this transaction and others to follow will likely result in a
// lot of layers being composed, and thus, SurfaceFlinger should wake-up earlier to avoid
// missing frame deadlines. In this case SurfaceFlinger will wake up at
// (sf vsync offset - debug.sf.early_phase_offset_ns). SurfaceFlinger will continue to be
- // in the early configuration until it receives eExplicitEarlyWakeupEnd. These flags are
+ // in the early configuration until it receives eEarlyWakeupEnd. These flags are
// expected to be used by WindowManager only and are guarded by
// android.permission.ACCESS_SURFACE_FLINGER
- eExplicitEarlyWakeupStart = 0x08,
- eExplicitEarlyWakeupEnd = 0x10,
+ eEarlyWakeupStart = 0x08,
+ eEarlyWakeupEnd = 0x10,
};
enum VsyncSource {
@@ -102,7 +107,12 @@
eVsyncSourceSurfaceFlinger = 1
};
- enum ConfigChanged { eConfigChangedSuppress = 0, eConfigChangedDispatch = 1 };
+ enum class EventRegistration {
+ modeChanged = 1 << 0,
+ frameRateOverride = 1 << 1,
+ };
+
+ using EventRegistrationFlags = Flags<EventRegistration>;
/*
* Create a connection with SurfaceFlinger.
@@ -112,7 +122,7 @@
/* return an IDisplayEventConnection */
virtual sp<IDisplayEventConnection> createDisplayEventConnection(
VsyncSource vsyncSource = eVsyncSourceApp,
- ConfigChanged configChanged = eConfigChangedSuppress) = 0;
+ EventRegistrationFlags eventRegistration = {}) = 0;
/* create a virtual display
* requires ACCESS_SURFACE_FLINGER permission.
@@ -147,13 +157,12 @@
}
/* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */
- virtual void setTransactionState(const Vector<ComposerState>& state,
- const Vector<DisplayState>& displays, uint32_t flags,
- const sp<IBinder>& applyToken,
- const InputWindowCommands& inputWindowCommands,
- int64_t desiredPresentTime,
- const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
- const std::vector<ListenerCallbacks>& listenerCallbacks) = 0;
+ virtual status_t setTransactionState(
+ const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& state,
+ const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
+ bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+ const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) = 0;
/* signal that we're done booting.
* Requires ACCESS_SURFACE_FLINGER permission
@@ -189,56 +198,32 @@
virtual status_t getDisplayState(const sp<IBinder>& display, ui::DisplayState*) = 0;
/**
- * Get immutable information about given physical display.
+ * Gets immutable information about given physical display.
*/
- virtual status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo*) = 0;
+ virtual status_t getStaticDisplayInfo(const sp<IBinder>& display, ui::StaticDisplayInfo*) = 0;
/**
- * Get configurations supported by given physical display.
+ * Gets dynamic information about given physical display.
*/
- virtual status_t getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayConfig>*) = 0;
+ virtual status_t getDynamicDisplayInfo(const sp<IBinder>& display, ui::DynamicDisplayInfo*) = 0;
- /**
- * Get the index into configurations returned by getDisplayConfigs,
- * corresponding to the active configuration.
- */
- virtual int getActiveConfig(const sp<IBinder>& display) = 0;
-
- virtual status_t getDisplayColorModes(const sp<IBinder>& display,
- Vector<ui::ColorMode>* outColorModes) = 0;
virtual status_t getDisplayNativePrimaries(const sp<IBinder>& display,
ui::DisplayPrimaries& primaries) = 0;
- virtual ui::ColorMode getActiveColorMode(const sp<IBinder>& display) = 0;
virtual status_t setActiveColorMode(const sp<IBinder>& display,
ui::ColorMode colorMode) = 0;
/**
- * Returns true if the connected display reports support for HDMI 2.1 Auto
- * Low Latency Mode.
- * For more information, see the HDMI 2.1 specification.
- */
- virtual status_t getAutoLowLatencyModeSupport(const sp<IBinder>& display,
- bool* outSupport) const = 0;
-
- /**
* Switches Auto Low Latency Mode on/off on the connected display, if it is
- * available. This should only be called if #getAutoLowLatencyMode returns
- * true.
+ * available. This should only be called if the display supports Auto Low
+ * Latency Mode as reported in #getDynamicDisplayInfo.
* For more information, see the HDMI 2.1 specification.
*/
virtual void setAutoLowLatencyMode(const sp<IBinder>& display, bool on) = 0;
/**
- * Returns true if the connected display reports support for Game Content Type.
- * For more information, see the HDMI 1.4 specification.
- */
- virtual status_t getGameContentTypeSupport(const sp<IBinder>& display,
- bool* outSupport) const = 0;
-
- /**
* This will start sending infoframes to the connected display with
- * ContentType=Game (if on=true). This will switch the disply to Game mode.
- * This should only be called if #getGameContentTypeSupport returns true.
+ * ContentType=Game (if on=true). This should only be called if the display
+ * Game Content Type as reported in #getDynamicDisplayInfo.
* For more information, see the HDMI 1.4 specification.
*/
virtual void setGameContentType(const sp<IBinder>& display, bool on) = 0;
@@ -246,65 +231,17 @@
/**
* Capture the specified screen. This requires READ_FRAME_BUFFER
* permission. This function will fail if there is a secure window on
- * screen.
+ * screen and DisplayCaptureArgs.captureSecureLayers is false.
*
* This function can capture a subregion (the source crop) of the screen.
* The subregion can be optionally rotated. It will also be scaled to
* match the size of the output buffer.
- *
- * reqDataspace and reqPixelFormat specify the data space and pixel format
- * of the buffer. The caller should pick the data space and pixel format
- * that it can consume.
- *
- * sourceCrop is the crop on the logical display.
- *
- * reqWidth and reqHeight specifies the size of the buffer. When either
- * of them is 0, they are set to the size of the logical display viewport.
- *
- * When useIdentityTransform is true, layer transformations are disabled.
- *
- * rotation specifies the rotation of the source crop (and the pixels in
- * it) around its center.
*/
- virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
- bool& outCapturedSecureLayers, ui::Dataspace reqDataspace,
- ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
- uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
- ui::Rotation rotation = ui::ROTATION_0,
- bool captureSecureLayers = false) = 0;
- /**
- * Capture the specified screen. This requires READ_FRAME_BUFFER
- * permission. This function will fail if there is a secure window on
- * screen.
- *
- * This function can capture a subregion (the source crop) of the screen
- * into an sRGB buffer with RGBA_8888 pixel format.
- * The subregion can be optionally rotated. It will also be scaled to
- * match the size of the output buffer.
- *
- * At the moment, sourceCrop is ignored and is always set to the visible
- * region (projected display viewport) of the screen.
- *
- * reqWidth and reqHeight specifies the size of the buffer. When either
- * of them is 0, they are set to the size of the logical display viewport.
- *
- * When useIdentityTransform is true, layer transformations are disabled.
- *
- * rotation specifies the rotation of the source crop (and the pixels in
- * it) around its center.
- */
- virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
- const Rect& sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- bool useIdentityTransform,
- ui::Rotation rotation = ui::ROTATION_0) {
- bool outIgnored;
- return captureScreen(display, outBuffer, outIgnored, ui::Dataspace::V0_SRGB,
- ui::PixelFormat::RGBA_8888, sourceCrop, reqWidth, reqHeight,
- useIdentityTransform, rotation);
- }
+ virtual status_t captureDisplay(const DisplayCaptureArgs& args,
+ const sp<IScreenCaptureListener>& captureListener) = 0;
- virtual status_t captureScreen(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace,
- sp<GraphicBuffer>* outBuffer) = 0;
+ virtual status_t captureDisplay(uint64_t displayOrLayerStack,
+ const sp<IScreenCaptureListener>& captureListener) = 0;
template <class AA>
struct SpHash {
@@ -313,27 +250,11 @@
/**
* Capture a subtree of the layer hierarchy, potentially ignoring the root node.
- *
- * reqDataspace and reqPixelFormat specify the data space and pixel format
- * of the buffer. The caller should pick the data space and pixel format
- * that it can consume.
+ * This requires READ_FRAME_BUFFER permission. This function will fail if there
+ * is a secure window on screen
*/
- virtual status_t captureLayers(
- const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer,
- ui::Dataspace reqDataspace, ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
- const std::unordered_set<sp<IBinder>, SpHash<IBinder>>& excludeHandles,
- float frameScale = 1.0, bool childrenOnly = false) = 0;
-
- /**
- * Capture a subtree of the layer hierarchy into an sRGB buffer with RGBA_8888 pixel format,
- * potentially ignoring the root node.
- */
- status_t captureLayers(const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer,
- const Rect& sourceCrop, float frameScale = 1.0,
- bool childrenOnly = false) {
- return captureLayers(layerHandleBinder, outBuffer, ui::Dataspace::V0_SRGB,
- ui::PixelFormat::RGBA_8888, sourceCrop, {}, frameScale, childrenOnly);
- }
+ virtual status_t captureLayers(const LayerCaptureArgs& args,
+ const sp<IScreenCaptureListener>& captureListener) = 0;
/* Clears the frame statistics for animations.
*
@@ -347,12 +268,18 @@
*/
virtual status_t getAnimationFrameStats(FrameStats* outStats) const = 0;
- /* Gets the supported HDR capabilities of the given display.
+ /* Overrides the supported HDR modes for the given display device.
*
* Requires the ACCESS_SURFACE_FLINGER permission.
*/
- virtual status_t getHdrCapabilities(const sp<IBinder>& display,
- HdrCapabilities* outCapabilities) const = 0;
+ virtual status_t overrideHdrTypes(const sp<IBinder>& display,
+ const std::vector<ui::Hdr>& hdrTypes) = 0;
+
+ /* Pulls surfaceflinger atoms global stats and layer stats to pipe to statsd.
+ *
+ * Requires the calling uid be from system server.
+ */
+ virtual status_t onPullAtom(const int32_t atomId, std::string* outData, bool* success) = 0;
virtual status_t enableVSyncInjections(bool enable) = 0;
@@ -434,36 +361,51 @@
*/
virtual status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) = 0;
+ /* Registers a listener that streams fps updates from SurfaceFlinger.
+ *
+ * The listener will stream fps updates for the layer tree rooted at the layer denoted by the
+ * task ID, i.e., the layer must have the task ID as part of its layer metadata with key
+ * METADATA_TASK_ID. If there is no such layer, then no fps is expected to be reported.
+ *
+ * Multiple listeners may be supported.
+ *
+ * Requires the READ_FRAME_BUFFER permission.
+ */
+ virtual status_t addFpsListener(int32_t taskId, const sp<gui::IFpsListener>& listener) = 0;
+ /*
+ * Removes a listener that was streaming fps updates from SurfaceFlinger.
+ */
+ virtual status_t removeFpsListener(const sp<gui::IFpsListener>& listener) = 0;
+
/* Sets the refresh rate boundaries for the display.
*
* The primary refresh rate range represents display manager's general guidance on the display
- * configs we'll consider when switching refresh rates. Unless we get an explicit signal from an
+ * modes we'll consider when switching refresh rates. Unless we get an explicit signal from an
* app, we should stay within this range.
*
- * The app request refresh rate range allows us to consider more display configs when switching
+ * The app request refresh rate range allows us to consider more display modes when switching
* refresh rates. Although we should generally stay within the primary range, specific
* considerations, such as layer frame rate settings specified via the setFrameRate() api, may
* cause us to go outside the primary range. We never go outside the app request range. The app
* request range will be greater than or equal to the primary refresh rate range, never smaller.
*
- * defaultConfig is used to narrow the list of display configs SurfaceFlinger will consider
- * switching between. Only configs with a config group and resolution matching defaultConfig
- * will be considered for switching. The defaultConfig index corresponds to the list of configs
- * returned from getDisplayConfigs().
+ * defaultMode is used to narrow the list of display modes SurfaceFlinger will consider
+ * switching between. Only modes with a mode group and resolution matching defaultMode
+ * will be considered for switching. The defaultMode corresponds to an ID of mode in the list
+ * of supported modes returned from getDynamicDisplayInfo().
*/
- virtual status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
- int32_t defaultConfig,
- float primaryRefreshRateMin,
- float primaryRefreshRateMax,
- float appRequestRefreshRateMin,
- float appRequestRefreshRateMax) = 0;
+ virtual status_t setDesiredDisplayModeSpecs(
+ const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode,
+ bool allowGroupSwitching, float primaryRefreshRateMin, float primaryRefreshRateMax,
+ float appRequestRefreshRateMin, float appRequestRefreshRateMax) = 0;
- virtual status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
- int32_t* outDefaultConfig,
- float* outPrimaryRefreshRateMin,
- float* outPrimaryRefreshRateMax,
- float* outAppRequestRefreshRateMin,
- float* outAppRequestRefreshRateMax) = 0;
+ virtual status_t getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
+ ui::DisplayModeId* outDefaultMode,
+ bool* outAllowGroupSwitching,
+ float* outPrimaryRefreshRateMin,
+ float* outPrimaryRefreshRateMax,
+ float* outAppRequestRefreshRateMin,
+ float* outAppRequestRefreshRateMax) = 0;
/*
* Gets whether brightness operations are supported on a display.
*
@@ -485,25 +427,44 @@
* displayToken
* The token of the display whose brightness is set.
* brightness
- * A number between 0.0f (minimum brightness) and 1.0 (maximum brightness), or -1.0f to
- * turn the backlight off.
+ * The DisplayBrightness info to set on the desired display.
*
* Returns NO_ERROR upon success. Otherwise,
* NAME_NOT_FOUND if the display is invalid, or
* BAD_VALUE if the brightness is invalid, or
* INVALID_OPERATION if brightness operations are not supported.
*/
- virtual status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) = 0;
+ virtual status_t setDisplayBrightness(const sp<IBinder>& displayToken,
+ const gui::DisplayBrightness& brightness) = 0;
/*
- * Sends a power hint to the composer. This function is asynchronous.
+ * Adds a listener that receives HDR layer information. This is used in combination
+ * with setDisplayBrightness to adjust the display brightness depending on factors such
+ * as whether or not HDR is in use.
*
- * hintId
- * hint id according to android::hardware::power::V1_0::PowerHint
+ * Returns NO_ERROR upon success or NAME_NOT_FOUND if the display is invalid.
+ */
+ virtual status_t addHdrLayerInfoListener(const sp<IBinder>& displayToken,
+ const sp<gui::IHdrLayerInfoListener>& listener) = 0;
+ /*
+ * Removes a listener that was added with addHdrLayerInfoListener.
+ *
+ * Returns NO_ERROR upon success, NAME_NOT_FOUND if the display is invalid, and BAD_VALUE if
+ * the listener wasn't registered.
+ *
+ */
+ virtual status_t removeHdrLayerInfoListener(const sp<IBinder>& displayToken,
+ const sp<gui::IHdrLayerInfoListener>& listener) = 0;
+
+ /*
+ * Sends a power boost to the composer. This function is asynchronous.
+ *
+ * boostId
+ * boost id according to android::hardware::power::Boost
*
* Returns NO_ERROR upon success.
*/
- virtual status_t notifyPowerHint(int32_t hintId) = 0;
+ virtual status_t notifyPowerBoost(int32_t boostId) = 0;
/*
* Sets the global configuration for all the shadows drawn by SurfaceFlinger. Shadow follows
@@ -531,7 +492,7 @@
* Sets the intended frame rate for a surface. See ANativeWindow_setFrameRate() for more info.
*/
virtual status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
- int8_t compatibility) = 0;
+ int8_t compatibility, int8_t changeFrameRateStrategy) = 0;
/*
* Acquire a frame rate flexibility token from SurfaceFlinger. While this token is acquired,
@@ -540,6 +501,42 @@
* for tests. Release the token by releasing the returned IBinder reference.
*/
virtual status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) = 0;
+
+ /*
+ * Sets the frame timeline vsync info received from choreographer that corresponds to next
+ * buffer submitted on that surface.
+ */
+ virtual status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface,
+ const FrameTimelineInfo& frameTimelineInfo) = 0;
+
+ /*
+ * Adds a TransactionTraceListener to listen for transaction tracing state updates.
+ */
+ virtual status_t addTransactionTraceListener(
+ const sp<gui::ITransactionTraceListener>& listener) = 0;
+
+ /**
+ * Gets priority of the RenderEngine in SurfaceFlinger.
+ */
+ virtual int getGPUContextPriority() = 0;
+
+ /**
+ * Gets the extra buffers a client would need to allocate if it passes
+ * the Choreographer#getVsyncId with its buffers.
+ *
+ * When Choreographer#getVsyncId is passed to SurfaceFlinger, it is used
+ * as an indication of when to latch the buffer. SurfaceFlinger will make
+ * sure that it will give the app at least the time configured as the
+ * 'appDuration' before trying to latch the buffer.
+ *
+ * The total buffers needed for a given configuration is basically the
+ * numbers of vsyncs a single buffer is used across the stack. For the default
+ * configuration a buffer is held ~1 vsync by the app, ~1 vsync by SurfaceFlinger
+ * and 1 vsync by the display. The extra buffers are calculated as the
+ * number of additional buffers on top of the 3 buffers already allocated
+ * by the app.
+ */
+ virtual status_t getExtraBufferCount(int* extraBuffers) const = 0;
};
// ----------------------------------------------------------------------------
@@ -551,7 +548,7 @@
// Java by ActivityManagerService.
BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION,
CREATE_CONNECTION,
- GET_DISPLAY_INFO,
+ GET_STATIC_DISPLAY_INFO,
CREATE_DISPLAY_EVENT_CONNECTION,
CREATE_DISPLAY,
DESTROY_DISPLAY,
@@ -559,18 +556,18 @@
SET_TRANSACTION_STATE,
AUTHENTICATE_SURFACE,
GET_SUPPORTED_FRAME_TIMESTAMPS,
- GET_DISPLAY_CONFIGS,
- GET_ACTIVE_CONFIG,
+ GET_DISPLAY_MODES, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead.
+ GET_ACTIVE_DISPLAY_MODE, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead.
GET_DISPLAY_STATE,
- CAPTURE_SCREEN,
+ CAPTURE_DISPLAY,
CAPTURE_LAYERS,
CLEAR_ANIMATION_FRAME_STATS,
GET_ANIMATION_FRAME_STATS,
SET_POWER_MODE,
GET_DISPLAY_STATS,
- GET_HDR_CAPABILITIES,
- GET_DISPLAY_COLOR_MODES,
- GET_ACTIVE_COLOR_MODE,
+ GET_HDR_CAPABILITIES, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead.
+ GET_DISPLAY_COLOR_MODES, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead.
+ GET_ACTIVE_COLOR_MODE, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead.
SET_ACTIVE_COLOR_MODE,
ENABLE_VSYNC_INJECTIONS,
INJECT_VSYNC,
@@ -586,19 +583,30 @@
GET_PHYSICAL_DISPLAY_IDS,
ADD_REGION_SAMPLING_LISTENER,
REMOVE_REGION_SAMPLING_LISTENER,
- SET_DESIRED_DISPLAY_CONFIG_SPECS,
- GET_DESIRED_DISPLAY_CONFIG_SPECS,
+ SET_DESIRED_DISPLAY_MODE_SPECS,
+ GET_DESIRED_DISPLAY_MODE_SPECS,
GET_DISPLAY_BRIGHTNESS_SUPPORT,
SET_DISPLAY_BRIGHTNESS,
- CAPTURE_SCREEN_BY_ID,
- NOTIFY_POWER_HINT,
+ CAPTURE_DISPLAY_BY_ID,
+ NOTIFY_POWER_BOOST,
SET_GLOBAL_SHADOW_SETTINGS,
- GET_AUTO_LOW_LATENCY_MODE_SUPPORT,
+ GET_AUTO_LOW_LATENCY_MODE_SUPPORT, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead.
SET_AUTO_LOW_LATENCY_MODE,
- GET_GAME_CONTENT_TYPE_SUPPORT,
+ GET_GAME_CONTENT_TYPE_SUPPORT, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead.
SET_GAME_CONTENT_TYPE,
SET_FRAME_RATE,
ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN,
+ SET_FRAME_TIMELINE_INFO,
+ ADD_TRANSACTION_TRACE_LISTENER,
+ GET_GPU_CONTEXT_PRIORITY,
+ GET_EXTRA_BUFFER_COUNT,
+ GET_DYNAMIC_DISPLAY_INFO,
+ ADD_FPS_LISTENER,
+ REMOVE_FPS_LISTENER,
+ OVERRIDE_HDR_TYPES,
+ ADD_HDR_LAYER_INFO_LISTENER,
+ REMOVE_HDR_LAYER_INFO_LISTENER,
+ ON_PULL_ATOM,
// Always append new enum to the end.
};
diff --git a/libs/gui/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h
index 3afbabf..9e9e191 100644
--- a/libs/gui/include/gui/ISurfaceComposerClient.h
+++ b/libs/gui/include/gui/ISurfaceComposerClient.h
@@ -36,6 +36,7 @@
enum { // (keep in sync with SurfaceControl.java)
eHidden = 0x00000004,
eDestroyBackbuffer = 0x00000020,
+ eSkipScreenshot = 0x00000040,
eSecure = 0x00000080,
eNonPremultiplied = 0x00000100,
eOpaque = 0x00000400,
@@ -51,13 +52,15 @@
eFXSurfaceMask = 0x000F0000,
};
+ // TODO(b/172002646): Clean up the Surface Creation Arguments
/*
* Requires ACCESS_SURFACE_FLINGER permission
*/
virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
uint32_t flags, const sp<IBinder>& parent,
LayerMetadata metadata, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp, uint32_t* outTransformHint) = 0;
+ sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
+ uint32_t* outTransformHint) = 0;
/*
* Requires ACCESS_SURFACE_FLINGER permission
@@ -66,7 +69,7 @@
PixelFormat format, uint32_t flags,
const sp<IGraphicBufferProducer>& parent,
LayerMetadata metadata, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp,
+ sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
uint32_t* outTransformHint) = 0;
/*
@@ -79,7 +82,8 @@
*/
virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const = 0;
- virtual status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle) = 0;
+ virtual status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle,
+ int32_t* outLayerId) = 0;
};
class BnSurfaceComposerClient : public SafeBnInterface<ISurfaceComposerClient> {
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index c58634b..2d71194 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -16,6 +16,8 @@
#pragma once
+#include "JankInfo.h"
+
#include <binder/IInterface.h>
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
@@ -34,7 +36,22 @@
class ITransactionCompletedListener;
class ListenerCallbacks;
-using CallbackId = int64_t;
+class CallbackId : public Parcelable {
+public:
+ int64_t id;
+ enum class Type : int32_t { ON_COMPLETE, ON_COMMIT } type;
+
+ CallbackId() {}
+ CallbackId(int64_t id, Type type) : id(id), type(type) {}
+ status_t writeToParcel(Parcel* output) const override;
+ status_t readFromParcel(const Parcel* input) override;
+
+ bool operator==(const CallbackId& rhs) const { return id == rhs.id && type == rhs.type; }
+};
+
+struct CallbackIdHash {
+ std::size_t operator()(const CallbackId& key) const { return std::hash<int64_t>()(key.id); }
+};
class FrameEventHistoryStats : public Parcelable {
public:
@@ -57,6 +74,26 @@
nsecs_t dequeueReadyTime;
};
+/**
+ * Jank information representing SurfaceFlinger's jank classification about frames for a specific
+ * surface.
+ */
+class JankData : public Parcelable {
+public:
+ status_t writeToParcel(Parcel* output) const override;
+ status_t readFromParcel(const Parcel* input) override;
+
+ JankData();
+ JankData(int64_t frameVsyncId, int32_t jankType)
+ : frameVsyncId(frameVsyncId), jankType(jankType) {}
+
+ // Identifier for the frame submitted with Transaction.setFrameTimelineVsyncId
+ int64_t frameVsyncId;
+
+ // Bitmask of janks that occurred
+ int32_t jankType;
+};
+
class SurfaceStats : public Parcelable {
public:
status_t writeToParcel(Parcel* output) const override;
@@ -64,18 +101,23 @@
SurfaceStats() = default;
SurfaceStats(const sp<IBinder>& sc, nsecs_t time, const sp<Fence>& prevReleaseFence,
- uint32_t hint, FrameEventHistoryStats frameEventStats)
+ uint32_t hint, FrameEventHistoryStats frameEventStats,
+ std::vector<JankData> jankData, uint64_t previousBufferId)
: surfaceControl(sc),
acquireTime(time),
previousReleaseFence(prevReleaseFence),
transformHint(hint),
- eventStats(frameEventStats) {}
+ eventStats(frameEventStats),
+ jankData(std::move(jankData)),
+ previousBufferId(previousBufferId) {}
sp<IBinder> surfaceControl;
nsecs_t acquireTime = -1;
sp<Fence> previousReleaseFence;
uint32_t transformHint = 0;
FrameEventHistoryStats eventStats;
+ std::vector<JankData> jankData;
+ uint64_t previousBufferId;
};
class TransactionStats : public Parcelable {
@@ -85,7 +127,7 @@
TransactionStats() = default;
TransactionStats(const std::vector<CallbackId>& ids) : callbackIds(ids) {}
- TransactionStats(const std::unordered_set<CallbackId>& ids)
+ TransactionStats(const std::unordered_set<CallbackId, CallbackIdHash>& ids)
: callbackIds(ids.begin(), ids.end()) {}
TransactionStats(const std::vector<CallbackId>& ids, nsecs_t latch, const sp<Fence>& present,
const std::vector<SurfaceStats>& surfaces)
@@ -102,8 +144,9 @@
status_t writeToParcel(Parcel* output) const override;
status_t readFromParcel(const Parcel* input) override;
- static ListenerStats createEmpty(const sp<IBinder>& listener,
- const std::unordered_set<CallbackId>& callbackIds);
+ static ListenerStats createEmpty(
+ const sp<IBinder>& listener,
+ const std::unordered_set<CallbackId, CallbackIdHash>& callbackIds);
sp<IBinder> listener;
std::vector<TransactionStats> transactionStats;
@@ -114,6 +157,8 @@
DECLARE_META_INTERFACE(TransactionCompletedListener)
virtual void onTransactionCompleted(ListenerStats stats) = 0;
+
+ virtual void onReleaseBuffer(uint64_t graphicBufferId, sp<Fence> releaseFence) = 0;
};
class BnTransactionCompletedListener : public SafeBnInterface<ITransactionCompletedListener> {
@@ -127,7 +172,8 @@
class ListenerCallbacks {
public:
- ListenerCallbacks(const sp<IBinder>& listener, const std::unordered_set<CallbackId>& callbacks)
+ ListenerCallbacks(const sp<IBinder>& listener,
+ const std::unordered_set<CallbackId, CallbackIdHash>& callbacks)
: transactionCompletedListener(listener),
callbackIds(callbacks.begin(), callbacks.end()) {}
@@ -141,9 +187,12 @@
if (callbackIds.empty()) {
return rhs.callbackIds.empty();
}
- return callbackIds.front() == rhs.callbackIds.front();
+ return callbackIds.front().id == rhs.callbackIds.front().id;
}
+ // Returns a new ListenerCallbacks filtered by type
+ ListenerCallbacks filter(CallbackId::Type type) const;
+
sp<IBinder> transactionCompletedListener;
std::vector<CallbackId> callbackIds;
};
@@ -162,7 +211,7 @@
// same members. It is sufficient to just check the first CallbackId in the vectors. If
// they match, they are the same. If they do not match, they are not the same.
std::size_t operator()(const std::vector<CallbackId>& callbackIds) const {
- return std::hash<CallbackId>{}((callbackIds.empty()) ? 0 : callbackIds.front());
+ return std::hash<int64_t>{}((callbackIds.empty()) ? 0 : callbackIds.front().id);
}
};
diff --git a/libs/gui/include/gui/JankInfo.h b/libs/gui/include/gui/JankInfo.h
new file mode 100644
index 0000000..ce9716f
--- /dev/null
+++ b/libs/gui/include/gui/JankInfo.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+namespace android {
+
+// Jank information tracked by SurfaceFlinger(SF) for perfetto tracing and telemetry.
+enum JankType {
+ // No Jank
+ None = 0x0,
+ // Jank that occurs in the layers below SurfaceFlinger
+ DisplayHAL = 0x1,
+ // SF took too long on the CPU
+ SurfaceFlingerCpuDeadlineMissed = 0x2,
+ // SF took too long on the GPU
+ SurfaceFlingerGpuDeadlineMissed = 0x4,
+ // Either App or GPU took too long on the frame
+ AppDeadlineMissed = 0x8,
+ // Vsync predictions have drifted beyond the threshold from the actual HWVsync
+ PredictionError = 0x10,
+ // Janks caused due to the time SF was scheduled to work on the frame
+ // Example: SF woke up too early and latched a buffer resulting in an early present
+ SurfaceFlingerScheduling = 0x20,
+ // A buffer is said to be stuffed if it was expected to be presented on a vsync but was
+ // presented later because the previous buffer was presented in its expected vsync. This
+ // usually happens if there is an unexpectedly long frame causing the rest of the buffers
+ // to enter a stuffed state.
+ BufferStuffing = 0x40,
+ // Jank due to unknown reasons.
+ Unknown = 0x80,
+ // SF is said to be stuffed if the previous frame ran longer than expected resulting in the case
+ // where the previous frame was presented in the current frame's expected vsync. This pushes the
+ // current frame to the next vsync. The behavior is similar to BufferStuffing.
+ SurfaceFlingerStuffing = 0x100,
+};
+
+} // namespace android
diff --git a/libs/gui/include/gui/LayerDebugInfo.h b/libs/gui/include/gui/LayerDebugInfo.h
index 66a7b4d..8b7d32c 100644
--- a/libs/gui/include/gui/LayerDebugInfo.h
+++ b/libs/gui/include/gui/LayerDebugInfo.h
@@ -20,6 +20,7 @@
#include <ui/PixelFormat.h>
#include <ui/Region.h>
+#include <ui/StretchEffect.h>
#include <string>
#include <math/vec4.h>
@@ -66,6 +67,7 @@
bool mRefreshPending = false;
bool mIsOpaque = false;
bool mContentDirty = false;
+ StretchEffect mStretchEffect = {};
};
std::string to_string(const LayerDebugInfo& info);
diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h
index d58e019..41982c2 100644
--- a/libs/gui/include/gui/LayerMetadata.h
+++ b/libs/gui/include/gui/LayerMetadata.h
@@ -27,6 +27,9 @@
METADATA_WINDOW_TYPE = 2,
METADATA_TASK_ID = 3,
METADATA_MOUSE_CURSOR = 4,
+ METADATA_ACCESSIBILITY_ID = 5,
+ METADATA_OWNER_PID = 6,
+ METADATA_DEQUEUE_TIME = 7
};
struct LayerMetadata : public Parcelable {
@@ -49,6 +52,8 @@
bool has(uint32_t key) const;
int32_t getInt32(uint32_t key, int32_t fallback) const;
void setInt32(uint32_t key, int32_t value);
+ std::optional<int64_t> getInt64(uint32_t key) const;
+ void setInt64(uint32_t key, int64_t value);
std::string itemToString(uint32_t key, const char* separator) const;
};
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index e60f677..b4f62f2 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_SF_LAYER_STATE_H
#define ANDROID_SF_LAYER_STATE_H
+
#include <stdint.h>
#include <sys/types.h>
@@ -26,15 +27,20 @@
#include <math/mat4.h>
#ifndef NO_INPUT
+#include <android/FocusRequest.h>
#include <input/InputWindow.h>
#endif
+#include <gui/ISurfaceComposer.h>
#include <gui/LayerMetadata.h>
+#include <gui/SurfaceControl.h>
#include <math/vec3.h>
+#include <ui/BlurRegion.h>
#include <ui/GraphicTypes.h>
#include <ui/Rect.h>
#include <ui/Region.h>
#include <ui/Rotation.h>
+#include <ui/StretchEffect.h>
#include <ui/Transform.h>
#include <utils/Errors.h>
@@ -57,9 +63,14 @@
*/
struct layer_state_t {
enum {
- eLayerHidden = 0x01, // SURFACE_HIDDEN in SurfaceControl.java
- eLayerOpaque = 0x02, // SURFACE_OPAQUE
- eLayerSecure = 0x80, // SECURE
+ eLayerHidden = 0x01, // SURFACE_HIDDEN in SurfaceControl.java
+ eLayerOpaque = 0x02, // SURFACE_OPAQUE
+ eLayerSkipScreenshot = 0x40, // SKIP_SCREENSHOT
+ eLayerSecure = 0x80, // SECURE
+ // Queue up BufferStateLayer buffers instead of dropping the oldest buffer when this flag is
+ // set. This blocks the client until all the buffers have been presented. If the buffers
+ // have presentation timestamps, then we may drop buffers.
+ eEnableBackpressure = 0x100, // ENABLE_BACKPRESSURE
};
enum {
@@ -71,12 +82,10 @@
eTransparentRegionChanged = 0x00000020,
eFlagsChanged = 0x00000040,
eLayerStackChanged = 0x00000080,
- eCropChanged_legacy = 0x00000100,
- eDeferTransaction_legacy = 0x00000200,
- eOverrideScalingModeChanged = 0x00000400,
+ eReleaseBufferListenerChanged = 0x00000400,
eShadowRadiusChanged = 0x00000800,
- eReparentChildren = 0x00001000,
- eDetachChildren = 0x00002000,
+ eLayerCreated = 0x00001000,
+ /* was eDetachChildren, now available 0x00002000, */
eRelativeLayerChanged = 0x00004000,
eReparent = 0x00008000,
eColorChanged = 0x00010000,
@@ -95,7 +104,7 @@
eHasListenerCallbacksChanged = 0x20000000,
eInputInfoChanged = 0x40000000,
eCornerRadiusChanged = 0x80000000,
- eFrameChanged = 0x1'00000000,
+ /* was eFrameChanged, now available 0x1'00000000, */
eCachedBufferChanged = 0x2'00000000,
eBackgroundColorChanged = 0x4'00000000,
eMetadataChanged = 0x8'00000000,
@@ -105,57 +114,29 @@
eBackgroundBlurRadiusChanged = 0x80'00000000,
eProducerDisconnect = 0x100'00000000,
eFixedTransformHintChanged = 0x200'00000000,
+ eFrameNumberChanged = 0x400'00000000,
+ eBlurRegionsChanged = 0x800'00000000,
+ eAutoRefreshChanged = 0x1000'00000000,
+ eStretchChanged = 0x2000'00000000,
};
- layer_state_t()
- : what(0),
- x(0),
- y(0),
- z(0),
- w(0),
- h(0),
- layerStack(0),
- alpha(0),
- flags(0),
- mask(0),
- reserved(0),
- crop_legacy(Rect::INVALID_RECT),
- cornerRadius(0.0f),
- backgroundBlurRadius(0),
- frameNumber_legacy(0),
- overrideScalingMode(-1),
- transform(0),
- transformToDisplayInverse(false),
- crop(Rect::INVALID_RECT),
- frame(Rect::INVALID_RECT),
- dataspace(ui::Dataspace::UNKNOWN),
- surfaceDamageRegion(),
- api(-1),
- colorTransform(mat4()),
- bgColorAlpha(0),
- bgColorDataspace(ui::Dataspace::UNKNOWN),
- colorSpaceAgnostic(false),
- shadowRadius(0.0f),
- frameRateSelectionPriority(-1),
- frameRate(0.0f),
- frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT),
- fixedTransformHint(ui::Transform::ROT_INVALID) {
- matrix.dsdx = matrix.dtdy = 1.0f;
- matrix.dsdy = matrix.dtdx = 0.0f;
- hdrMetadata.validTypes = 0;
- }
+ layer_state_t();
void merge(const layer_state_t& other);
status_t write(Parcel& output) const;
status_t read(const Parcel& input);
+ bool hasBufferChanges() const;
struct matrix22_t {
float dsdx{0};
float dtdx{0};
float dtdy{0};
float dsdy{0};
+ status_t write(Parcel& output) const;
+ status_t read(const Parcel& input);
};
sp<IBinder> surface;
+ int32_t layerId;
uint64_t what;
float x;
float y;
@@ -164,23 +145,17 @@
uint32_t h;
uint32_t layerStack;
float alpha;
- uint8_t flags;
- uint8_t mask;
+ uint32_t flags;
+ uint32_t mask;
uint8_t reserved;
matrix22_t matrix;
- Rect crop_legacy;
float cornerRadius;
uint32_t backgroundBlurRadius;
- sp<IBinder> barrierHandle_legacy;
- sp<IBinder> reparentHandle;
- uint64_t frameNumber_legacy;
- int32_t overrideScalingMode;
+ sp<SurfaceControl> reparentSurfaceControl;
- sp<IGraphicBufferProducer> barrierGbp_legacy;
+ sp<SurfaceControl> relativeLayerSurfaceControl;
- sp<IBinder> relativeLayerHandle;
-
- sp<IBinder> parentHandleForChild;
+ sp<SurfaceControl> parentSurfaceControlForChild;
half3 color;
@@ -190,7 +165,7 @@
uint32_t transform;
bool transformToDisplayInverse;
Rect crop;
- Rect frame;
+ Rect orientedDisplaySpaceRect;
sp<GraphicBuffer> buffer;
sp<Fence> acquireFence;
ui::Dataspace dataspace;
@@ -199,9 +174,10 @@
int32_t api;
sp<NativeHandle> sidebandStream;
mat4 colorTransform;
+ std::vector<BlurRegion> blurRegions;
#ifndef NO_INPUT
- InputWindowInfo inputInfo;
+ sp<InputWindowHandle> inputHandle = new InputWindowHandle();
#endif
client_cache_t cachedBuffer;
@@ -228,6 +204,7 @@
// Layer frame rate and compatibility. See ANativeWindow_setFrameRate().
float frameRate;
int8_t frameRateCompatibility;
+ int8_t changeFrameRateStrategy;
// Set by window manager indicating the layer and all its children are
// in a different orientation than the display. The hint suggests that
@@ -237,6 +214,23 @@
// a buffer of a different size. -1 means the transform hint is not set,
// otherwise the value will be a valid ui::Rotation.
ui::Transform::RotationFlags fixedTransformHint;
+
+ // Used by BlastBufferQueue to forward the framenumber generated by the
+ // graphics producer.
+ uint64_t frameNumber;
+
+ // Indicates that the consumer should acquire the next frame as soon as it
+ // can and not wait for a frame to become available. This is only relevant
+ // in shared buffer mode.
+ bool autoRefresh;
+
+ // Stretch effect to be applied to this layer
+ StretchEffect stretchEffect;
+
+ // Listens to when the buffer is safe to be released. This is used for blast
+ // layers only. The callback includes a release fence as well as the graphic
+ // buffer id to identify the buffer.
+ sp<ITransactionCompletedListener> releaseBufferListener;
};
struct ComposerState {
@@ -263,18 +257,18 @@
// These states define how layers are projected onto the physical display.
//
- // Layers are first clipped to `viewport'. They are then translated and
- // scaled from `viewport' to `frame'. Finally, they are rotated according
- // to `orientation', `width', and `height'.
+ // Layers are first clipped to `layerStackSpaceRect'. They are then translated and
+ // scaled from `layerStackSpaceRect' to `orientedDisplaySpaceRect'. Finally, they are rotated
+ // according to `orientation', `width', and `height'.
//
- // For example, assume viewport is Rect(0, 0, 200, 100), frame is Rect(20,
- // 10, 420, 210), and the size of the display is WxH. When orientation is
- // 0, layers will be scaled by a factor of 2 and translated by (20, 10).
- // When orientation is 1, layers will be additionally rotated by 90
- // degrees around the origin clockwise and translated by (W, 0).
+ // For example, assume layerStackSpaceRect is Rect(0, 0, 200, 100), orientedDisplaySpaceRect is
+ // Rect(20, 10, 420, 210), and the size of the display is WxH. When orientation is 0, layers
+ // will be scaled by a factor of 2 and translated by (20, 10). When orientation is 1, layers
+ // will be additionally rotated by 90 degrees around the origin clockwise and translated by (W,
+ // 0).
ui::Rotation orientation = ui::ROTATION_0;
- Rect viewport;
- Rect frame;
+ Rect layerStackSpaceRect;
+ Rect orientedDisplaySpaceRect;
uint32_t width, height;
@@ -283,12 +277,17 @@
};
struct InputWindowCommands {
+#ifndef NO_INPUT
+ std::vector<FocusRequest> focusRequests;
+#endif
bool syncInputWindows{false};
- void merge(const InputWindowCommands& other);
+ // Merges the passed in commands and returns true if there were any changes.
+ bool merge(const InputWindowCommands& other);
+ bool empty() const;
void clear();
- void write(Parcel& output) const;
- void read(const Parcel& input);
+ status_t write(Parcel& output) const;
+ status_t read(const Parcel& input);
};
static inline int compare_type(const ComposerState& lhs, const ComposerState& rhs) {
@@ -301,11 +300,65 @@
return compare_type(lhs.token, rhs.token);
}
-// Returns true if the frameRate and compatibility are valid values, false
-// othwerise. If either of the params are invalid, an error log is printed, and
-// functionName is added to the log to indicate which function call failed.
-// functionName can be null.
-bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* functionName);
+// Returns true if the frameRate is valid.
+//
+// @param frameRate the frame rate in Hz
+// @param compatibility a ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_*
+// @param changeFrameRateStrategy a ANATIVEWINDOW_CHANGE_FRAME_RATE_*
+// @param functionName calling function or nullptr. Used for logging
+// @param privileged whether caller has unscoped surfaceflinger access
+bool ValidateFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy,
+ const char* functionName, bool privileged = false);
+
+struct CaptureArgs {
+ const static int32_t UNSET_UID = -1;
+ virtual ~CaptureArgs() = default;
+
+ ui::PixelFormat pixelFormat{ui::PixelFormat::RGBA_8888};
+ Rect sourceCrop;
+ float frameScaleX{1};
+ float frameScaleY{1};
+ bool captureSecureLayers{false};
+ int32_t uid{UNSET_UID};
+ // Force capture to be in a color space. If the value is ui::Dataspace::UNKNOWN, the captured
+ // result will be in the display's colorspace.
+ // The display may use non-RGB dataspace (ex. displayP3) that could cause pixel data could be
+ // different from SRGB (byte per color), and failed when checking colors in tests.
+ // NOTE: In normal cases, we want the screen to be captured in display's colorspace.
+ ui::Dataspace dataspace = ui::Dataspace::UNKNOWN;
+
+ // The receiver of the capture can handle protected buffer. A protected buffer has
+ // GRALLOC_USAGE_PROTECTED usage bit and must not be accessed unprotected behaviour.
+ // Any read/write access from unprotected context will result in undefined behaviour.
+ // Protected contents are typically DRM contents. This has no direct implication to the
+ // secure property of the surface, which is specified by the application explicitly to avoid
+ // the contents being accessed/captured by screenshot or unsecure display.
+ bool allowProtected = false;
+
+ bool grayscale = false;
+
+ virtual status_t write(Parcel& output) const;
+ virtual status_t read(const Parcel& input);
+};
+
+struct DisplayCaptureArgs : CaptureArgs {
+ sp<IBinder> displayToken;
+ uint32_t width{0};
+ uint32_t height{0};
+ bool useIdentityTransform{false};
+
+ status_t write(Parcel& output) const override;
+ status_t read(const Parcel& input) override;
+};
+
+struct LayerCaptureArgs : CaptureArgs {
+ sp<IBinder> layerHandle;
+ std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> excludeHandles;
+ bool childrenOnly{false};
+
+ status_t write(Parcel& output) const override;
+ status_t read(const Parcel& input) override;
+};
}; // namespace android
diff --git a/libs/gui/include/gui/ScreenCaptureResults.h b/libs/gui/include/gui/ScreenCaptureResults.h
new file mode 100644
index 0000000..99c35c1
--- /dev/null
+++ b/libs/gui/include/gui/ScreenCaptureResults.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#pragma once
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
+
+namespace android::gui {
+
+struct ScreenCaptureResults : public Parcelable {
+public:
+ ScreenCaptureResults() = default;
+ virtual ~ScreenCaptureResults() = default;
+ status_t writeToParcel(android::Parcel* parcel) const override;
+ status_t readFromParcel(const android::Parcel* parcel) override;
+
+ sp<GraphicBuffer> buffer;
+ sp<Fence> fence = Fence::NO_FENCE;
+ bool capturedSecureLayers{false};
+ ui::Dataspace capturedDataspace{ui::Dataspace::V0_SRGB};
+ status_t result = OK;
+};
+
+} // namespace android::gui
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 55b4101..d22bdaa 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -18,6 +18,7 @@
#define ANDROID_GUI_SURFACE_H
#include <gui/BufferQueueDefs.h>
+#include <gui/FrameTimelineInfo.h>
#include <gui/HdrMetadata.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/IProducerListener.h>
@@ -68,7 +69,6 @@
: public ANativeObjectBase<ANativeWindow, Surface, RefBase>
{
public:
-
/*
* creates a Surface from the given IGraphicBufferProducer (which concrete
* implementation is a BufferQueue).
@@ -83,9 +83,15 @@
*
* the controlledByApp flag indicates that this Surface (producer) is
* controlled by the application. This flag is used at connect time.
+ *
+ * Pass in the SurfaceControlHandle to store a weak reference to the layer
+ * that the Surface was created from. This handle can be used to create a
+ * child surface without using the IGBP to identify the layer. This is used
+ * for surfaces created by the BlastBufferQueue whose IGBP is created on the
+ * client and cannot be verified in SF.
*/
- explicit Surface(const sp<IGraphicBufferProducer>& bufferProducer,
- bool controlledByApp = false);
+ explicit Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp = false,
+ const sp<IBinder>& surfaceControlHandle = nullptr);
/* getIGraphicBufferProducer() returns the IGraphicBufferProducer this
* Surface was created with. Usually it's an error to use the
@@ -93,6 +99,8 @@
*/
sp<IGraphicBufferProducer> getIGraphicBufferProducer() const;
+ sp<IBinder> getSurfaceControlHandle() const { return mSurfaceControlHandle; }
+
/* convenience function to check that the given surface is non NULL as
* well as its IGraphicBufferProducer */
static bool isValid(const sp<Surface>& surface) {
@@ -120,7 +128,7 @@
* delay during dequeueBuffer. If there are already the maximum number of
* buffers allocated, this function has no effect.
*/
- void allocateBuffers();
+ virtual void allocateBuffers();
/* Sets the generation number on the IGraphicBufferProducer and updates the
* generation number on any buffers attached to the Surface after this call.
@@ -179,7 +187,10 @@
status_t getUniqueId(uint64_t* outId) const;
status_t getConsumerUsage(uint64_t* outUsage) const;
- status_t setFrameRate(float frameRate, int8_t compatibility);
+ virtual status_t setFrameRate(float frameRate, int8_t compatibility,
+ int8_t changeFrameRateStrategy);
+ virtual status_t setFrameTimelineInfo(const FrameTimelineInfo& info);
+ virtual status_t getExtraBufferCount(int* extraBuffers) const;
protected:
virtual ~Surface();
@@ -265,6 +276,8 @@
int dispatchAddQueueInterceptor(va_list args);
int dispatchAddQueryInterceptor(va_list args);
int dispatchGetLastQueuedBuffer(va_list args);
+ int dispatchSetFrameTimelineInfo(va_list args);
+ int dispatchGetExtraBufferCount(va_list args);
bool transformToDisplayInverse();
protected:
@@ -335,6 +348,23 @@
static status_t attachAndQueueBufferWithDataspace(Surface* surface, sp<GraphicBuffer> buffer,
ui::Dataspace dataspace);
+ // Batch version of dequeueBuffer, cancelBuffer and queueBuffer
+ // Note that these batched operations are not supported when shared buffer mode is being used.
+ struct BatchBuffer {
+ ANativeWindowBuffer* buffer = nullptr;
+ int fenceFd = -1;
+ };
+ virtual int dequeueBuffers(std::vector<BatchBuffer>* buffers);
+ virtual int cancelBuffers(const std::vector<BatchBuffer>& buffers);
+
+ struct BatchQueuedBuffer {
+ ANativeWindowBuffer* buffer = nullptr;
+ int fenceFd = -1;
+ nsecs_t timestamp = NATIVE_WINDOW_TIMESTAMP_AUTO;
+ };
+ virtual int queueBuffers(
+ const std::vector<BatchQueuedBuffer>& buffers);
+
protected:
enum { NUM_BUFFER_SLOTS = BufferQueueDefs::NUM_BUFFER_SLOTS };
enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 };
@@ -364,6 +394,14 @@
void freeAllBuffers();
int getSlotFromBufferLocked(android_native_buffer_t* buffer) const;
+ void getDequeueBufferInputLocked(IGraphicBufferProducer::DequeueBufferInput* dequeueInput);
+
+ void getQueueBufferInputLocked(android_native_buffer_t* buffer, int fenceFd, nsecs_t timestamp,
+ IGraphicBufferProducer::QueueBufferInput* out);
+
+ void onBufferQueuedLocked(int slot, sp<Fence> fence,
+ const IGraphicBufferProducer::QueueBufferOutput& output);
+
struct BufferSlot {
sp<GraphicBuffer> buffer;
Region dirtyRegion;
@@ -539,6 +577,11 @@
bool mEnableFrameTimestamps = false;
std::unique_ptr<ProducerFrameEventHistory> mFrameEventHistory;
+ // Reference to the SurfaceFlinger layer that was used to create this
+ // surface. This is only populated when the Surface is created from
+ // a BlastBufferQueue.
+ sp<IBinder> mSurfaceControlHandle;
+
bool mReportRemovedBuffers = false;
std::vector<sp<GraphicBuffer>> mRemovedBuffers;
int mMaxBufferCount;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index adcb898..5bbd8e3 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -29,6 +29,7 @@
#include <utils/SortedVector.h>
#include <utils/threads.h>
+#include <ui/BlurRegion.h>
#include <ui/ConfigStoreTypes.h>
#include <ui/DisplayedFrameStats.h>
#include <ui/FrameStats.h>
@@ -79,6 +80,13 @@
using TransactionCompletedCallback =
std::function<void(nsecs_t /*latchTime*/, const sp<Fence>& /*presentFence*/,
const std::vector<SurfaceControlStats>& /*stats*/)>;
+using ReleaseBufferCallback =
+ std::function<void(uint64_t /* graphicsBufferId */, const sp<Fence>& /*releaseFence*/)>;
+
+using SurfaceStatsCallback =
+ std::function<void(void* /*context*/, nsecs_t /*latchTime*/,
+ const sp<Fence>& /*presentFence*/,
+ const SurfaceStats& /*stats*/)>;
// ---------------------------------------------------------------------------
@@ -107,57 +115,42 @@
static status_t getDisplayState(const sp<IBinder>& display, ui::DisplayState*);
// Get immutable information about given physical display.
- static status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo*);
+ static status_t getStaticDisplayInfo(const sp<IBinder>& display, ui::StaticDisplayInfo*);
- // Get configurations supported by given physical display.
- static status_t getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayConfig>*);
+ // Get dynamic information about given physical display.
+ static status_t getDynamicDisplayInfo(const sp<IBinder>& display, ui::DynamicDisplayInfo*);
- // Get the ID of the active DisplayConfig, as getDisplayConfigs index.
- static int getActiveConfig(const sp<IBinder>& display);
-
- // Shorthand for getDisplayConfigs element at getActiveConfig index.
- static status_t getActiveDisplayConfig(const sp<IBinder>& display, DisplayConfig*);
+ // Shorthand for the active display mode from getDynamicDisplayInfo().
+ // TODO(b/180391891): Update clients to use getDynamicDisplayInfo and remove this function.
+ static status_t getActiveDisplayMode(const sp<IBinder>& display, ui::DisplayMode*);
// Sets the refresh rate boundaries for the display.
- static status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
- int32_t defaultConfig, float primaryRefreshRateMin,
- float primaryRefreshRateMax,
- float appRequestRefreshRateMin,
- float appRequestRefreshRateMax);
+ static status_t setDesiredDisplayModeSpecs(
+ const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode,
+ bool allowGroupSwitching, float primaryRefreshRateMin, float primaryRefreshRateMax,
+ float appRequestRefreshRateMin, float appRequestRefreshRateMax);
// Gets the refresh rate boundaries for the display.
- static status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
- int32_t* outDefaultConfig,
- float* outPrimaryRefreshRateMin,
- float* outPrimaryRefreshRateMax,
- float* outAppRequestRefreshRateMin,
- float* outAppRequestRefreshRateMax);
-
- // Gets the list of supported color modes for the given display
- static status_t getDisplayColorModes(const sp<IBinder>& display,
- Vector<ui::ColorMode>* outColorModes);
+ static status_t getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
+ ui::DisplayModeId* outDefaultMode,
+ bool* outAllowGroupSwitching,
+ float* outPrimaryRefreshRateMin,
+ float* outPrimaryRefreshRateMax,
+ float* outAppRequestRefreshRateMin,
+ float* outAppRequestRefreshRateMax);
// Get the coordinates of the display's native color primaries
static status_t getDisplayNativePrimaries(const sp<IBinder>& display,
ui::DisplayPrimaries& outPrimaries);
- // Gets the active color mode for the given display
- static ui::ColorMode getActiveColorMode(const sp<IBinder>& display);
-
// Sets the active color mode for the given display
static status_t setActiveColorMode(const sp<IBinder>& display,
ui::ColorMode colorMode);
- // Reports whether the connected display supports Auto Low Latency Mode
- static bool getAutoLowLatencyModeSupport(const sp<IBinder>& display);
-
// Switches on/off Auto Low Latency Mode on the connected display. This should only be
// called if the connected display supports Auto Low Latency Mode as reported by
// #getAutoLowLatencyModeSupport
static void setAutoLowLatencyMode(const sp<IBinder>& display, bool on);
- // Reports whether the connected display supports Game content type
- static bool getGameContentTypeSupport(const sp<IBinder>& display);
-
// Turns Game mode on/off on the connected display. This should only be called
// if the display supports Game content type, as reported by #getGameContentTypeSupport
static void setGameContentType(const sp<IBinder>& display, bool on);
@@ -182,6 +175,11 @@
static bool getProtectedContentSupport();
/**
+ * Gets the context priority of surface flinger's render engine.
+ */
+ static int getGPUContextPriority();
+
+ /**
* Uncaches a buffer in ISurfaceComposer. It must be uncached via a transaction so that it is
* in order with other transactions that use buffers.
*/
@@ -214,17 +212,23 @@
* BAD_VALUE if the brightness value is invalid, or
* INVALID_OPERATION if brightness operaetions are not supported.
*/
- static status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness);
+ static status_t setDisplayBrightness(const sp<IBinder>& displayToken,
+ const gui::DisplayBrightness& brightness);
+
+ static status_t addHdrLayerInfoListener(const sp<IBinder>& displayToken,
+ const sp<gui::IHdrLayerInfoListener>& listener);
+ static status_t removeHdrLayerInfoListener(const sp<IBinder>& displayToken,
+ const sp<gui::IHdrLayerInfoListener>& listener);
/*
- * Sends a power hint to the composer. This function is asynchronous.
+ * Sends a power boost to the composer. This function is asynchronous.
*
- * hintId
- * hint id according to android::hardware::power::V1_0::PowerHint
+ * boostId
+ * boost id according to android::hardware::power::Boost
*
* Returns NO_ERROR upon success.
*/
- static status_t notifyPowerHint(int32_t hintId);
+ static status_t notifyPowerBoost(int32_t boostId);
/*
* Sets the global configuration for all the shadows drawn by SurfaceFlinger. Shadow follows
@@ -253,13 +257,13 @@
static sp<SurfaceComposerClient> getDefault();
//! Create a surface
- sp<SurfaceControl> createSurface(const String8& name, // name of the surface
- uint32_t w, // width in pixel
- uint32_t h, // height in pixel
- PixelFormat format, // pixel-format desired
- uint32_t flags = 0, // usage flags
- SurfaceControl* parent = nullptr, // parent
- LayerMetadata metadata = LayerMetadata(), // metadata
+ sp<SurfaceControl> createSurface(const String8& name, // name of the surface
+ uint32_t w, // width in pixel
+ uint32_t h, // height in pixel
+ PixelFormat format, // pixel-format desired
+ uint32_t flags = 0, // usage flags
+ const sp<IBinder>& parentHandle = nullptr, // parentHandle
+ LayerMetadata metadata = LayerMetadata(), // metadata
uint32_t* outTransformHint = nullptr);
status_t createSurfaceChecked(const String8& name, // name of the surface
@@ -267,9 +271,9 @@
uint32_t h, // height in pixel
PixelFormat format, // pixel-format desired
sp<SurfaceControl>* outSurface,
- uint32_t flags = 0, // usage flags
- SurfaceControl* parent = nullptr, // parent
- LayerMetadata metadata = LayerMetadata(), // metadata
+ uint32_t flags = 0, // usage flags
+ const sp<IBinder>& parentHandle = nullptr, // parentHandle
+ LayerMetadata metadata = LayerMetadata(), // metadata
uint32_t* outTransformHint = nullptr);
//! Create a surface
@@ -332,25 +336,30 @@
struct CallbackInfo {
// All the callbacks that have been requested for a TransactionCompletedListener in the
// Transaction
- std::unordered_set<CallbackId> callbackIds;
+ std::unordered_set<CallbackId, CallbackIdHash> callbackIds;
// All the SurfaceControls that have been modified in this TransactionCompletedListener's
// process that require a callback if there is one or more callbackIds set.
std::unordered_set<sp<SurfaceControl>, SCHash> surfaceControls;
};
class Transaction : public Parcelable {
+ private:
+ static std::atomic<uint32_t> idCounter;
+ int64_t generateId();
+
protected:
std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> mComposerStates;
- SortedVector<DisplayState > mDisplayStates;
+ SortedVector<DisplayState> mDisplayStates;
std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
mListenerCallbacks;
+ uint64_t mId;
+
uint32_t mForceSynchronous = 0;
uint32_t mTransactionNestCount = 0;
bool mAnimation = false;
- bool mEarlyWakeup = false;
- bool mExplicitEarlyWakeupStart = false;
- bool mExplicitEarlyWakeupEnd = false;
+ bool mEarlyWakeupStart = false;
+ bool mEarlyWakeupEnd = false;
// Indicates that the Transaction contains a buffer that should be cached
bool mContainsBuffer = false;
@@ -359,28 +368,38 @@
// to be presented. When it is not possible to present at exactly that time, it will be
// presented after the time has passed.
//
+ // If the client didn't pass a desired presentation time, mDesiredPresentTime will be
+ // populated to the time setBuffer was called, and mIsAutoTimestamp will be set to true.
+ //
// Desired present times that are more than 1 second in the future may be ignored.
// When a desired present time has already passed, the transaction will be presented as soon
// as possible.
//
// Transactions from the same process are presented in the same order that they are applied.
// The desired present time does not affect this ordering.
- int64_t mDesiredPresentTime = -1;
+ int64_t mDesiredPresentTime = 0;
+ bool mIsAutoTimestamp = true;
+
+ // The vsync id provided by Choreographer.getVsyncId and the input event id
+ FrameTimelineInfo mFrameTimelineInfo;
+
+ // If not null, transactions will be queued up using this token otherwise a common token
+ // per process will be used.
+ sp<IBinder> mApplyToken = nullptr;
InputWindowCommands mInputWindowCommands;
int mStatus = NO_ERROR;
- layer_state_t* getLayerState(const sp<IBinder>& surfaceHandle);
- layer_state_t* getLayerState(const sp<SurfaceControl>& sc) {
- return getLayerState(sc->getHandle());
- }
+ layer_state_t* getLayerState(const sp<SurfaceControl>& sc);
DisplayState& getDisplayState(const sp<IBinder>& token);
void cacheBuffers();
void registerSurfaceControlForCallback(const sp<SurfaceControl>& sc);
+ void setReleaseBufferCallback(layer_state_t* state, ReleaseBufferCallback callback);
+ void removeReleaseBufferCallback(layer_state_t* state);
public:
- Transaction() = default;
+ Transaction();
virtual ~Transaction() = default;
Transaction(Transaction const& other);
@@ -418,7 +437,7 @@
// If the relative is removed, the Surface will have no layer and be
// invisible, until the next time set(Relative)Layer is called.
Transaction& setRelativeLayer(const sp<SurfaceControl>& sc,
- const sp<IBinder>& relativeTo, int32_t z);
+ const sp<SurfaceControl>& relativeTo, int32_t z);
Transaction& setFlags(const sp<SurfaceControl>& sc,
uint32_t flags, uint32_t mask);
Transaction& setTransparentRegionHint(const sp<SurfaceControl>& sc,
@@ -427,33 +446,17 @@
float alpha);
Transaction& setMatrix(const sp<SurfaceControl>& sc,
float dsdx, float dtdx, float dtdy, float dsdy);
- Transaction& setCrop_legacy(const sp<SurfaceControl>& sc, const Rect& crop);
+ Transaction& setCrop(const sp<SurfaceControl>& sc, const Rect& crop);
Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius);
Transaction& setBackgroundBlurRadius(const sp<SurfaceControl>& sc,
int backgroundBlurRadius);
+ Transaction& setBlurRegions(const sp<SurfaceControl>& sc,
+ const std::vector<BlurRegion>& regions);
Transaction& setLayerStack(const sp<SurfaceControl>& sc, uint32_t layerStack);
Transaction& setMetadata(const sp<SurfaceControl>& sc, uint32_t key, const Parcel& p);
- // Defers applying any changes made in this transaction until the Layer
- // identified by handle reaches the given frameNumber. If the Layer identified
- // by handle is removed, then we will apply this transaction regardless of
- // what frame number has been reached.
- Transaction& deferTransactionUntil_legacy(const sp<SurfaceControl>& sc,
- const sp<IBinder>& handle, uint64_t frameNumber);
- // A variant of deferTransactionUntil_legacy which identifies the Layer we wait for by
- // Surface instead of Handle. Useful for clients which may not have the
- // SurfaceControl for some of their Surfaces. Otherwise behaves identically.
- Transaction& deferTransactionUntil_legacy(const sp<SurfaceControl>& sc,
- const sp<Surface>& barrierSurface,
- uint64_t frameNumber);
- // Reparents all children of this layer to the new parent handle.
- Transaction& reparentChildren(const sp<SurfaceControl>& sc,
- const sp<IBinder>& newParentHandle);
/// Reparents the current layer to the new parent handle. The new parent must not be null.
- // This can be used instead of reparentChildren if the caller wants to
- // only re-parent a specific child.
- Transaction& reparent(const sp<SurfaceControl>& sc,
- const sp<IBinder>& newParentHandle);
+ Transaction& reparent(const sp<SurfaceControl>& sc, const sp<SurfaceControl>& newParent);
Transaction& setColor(const sp<SurfaceControl>& sc, const half3& color);
@@ -464,9 +467,8 @@
Transaction& setTransform(const sp<SurfaceControl>& sc, uint32_t transform);
Transaction& setTransformToDisplayInverse(const sp<SurfaceControl>& sc,
bool transformToDisplayInverse);
- Transaction& setCrop(const sp<SurfaceControl>& sc, const Rect& crop);
- Transaction& setFrame(const sp<SurfaceControl>& sc, const Rect& frame);
- Transaction& setBuffer(const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer);
+ Transaction& setBuffer(const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
+ ReleaseBufferCallback callback = nullptr);
Transaction& setCachedBuffer(const sp<SurfaceControl>& sc, int32_t bufferId);
Transaction& setAcquireFence(const sp<SurfaceControl>& sc, const sp<Fence>& fence);
Transaction& setDataspace(const sp<SurfaceControl>& sc, ui::Dataspace dataspace);
@@ -482,31 +484,23 @@
// Sets information about the priority of the frame.
Transaction& setFrameRateSelectionPriority(const sp<SurfaceControl>& sc, int32_t priority);
+ Transaction& addTransactionCallback(TransactionCompletedCallbackTakesContext callback,
+ void* callbackContext, CallbackId::Type callbackType);
+
Transaction& addTransactionCompletedCallback(
TransactionCompletedCallbackTakesContext callback, void* callbackContext);
+ Transaction& addTransactionCommittedCallback(
+ TransactionCompletedCallbackTakesContext callback, void* callbackContext);
+
// ONLY FOR BLAST ADAPTER
Transaction& notifyProducerDisconnect(const sp<SurfaceControl>& sc);
-
- // Detaches all child surfaces (and their children recursively)
- // from their SurfaceControl.
- // The child SurfaceControls will not throw exceptions or return errors,
- // but transactions will have no effect.
- // The child surfaces will continue to follow their parent surfaces,
- // and remain eligible for rendering, but their relative state will be
- // frozen. We use this in the WindowManager, in app shutdown/relaunch
- // scenarios, where the app would otherwise clean up its child Surfaces.
- // Sometimes the WindowManager needs to extend their lifetime slightly
- // in order to perform an exit animation or prevent flicker.
- Transaction& detachChildren(const sp<SurfaceControl>& sc);
- // Set an override scaling mode as documented in <system/window.h>
- // the override scaling mode will take precedence over any client
- // specified scaling mode. -1 will clear the override scaling mode.
- Transaction& setOverrideScalingMode(const sp<SurfaceControl>& sc,
- int32_t overrideScalingMode);
+ // Set the framenumber generated by the graphics producer to mimic BufferQueue behaviour.
+ Transaction& setFrameNumber(const sp<SurfaceControl>& sc, uint64_t frameNumber);
#ifndef NO_INPUT
Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const InputWindowInfo& info);
+ Transaction& setFocusedWindow(const FocusRequest& request);
Transaction& syncInputWindows();
#endif
@@ -519,7 +513,7 @@
Transaction& setShadowRadius(const sp<SurfaceControl>& sc, float cornerRadius);
Transaction& setFrameRate(const sp<SurfaceControl>& sc, float frameRate,
- int8_t compatibility);
+ int8_t compatibility, int8_t changeFrameRateStrategy);
// Set by window manager indicating the layer and all its children are
// in a different orientation than the display. The hint suggests that
@@ -529,6 +523,25 @@
// a buffer of a different size.
Transaction& setFixedTransformHint(const sp<SurfaceControl>& sc, int32_t transformHint);
+ // Sets the frame timeline vsync id received from choreographer that corresponds
+ // to the transaction, and the input event id that identifies the input event that caused
+ // the current frame.
+ Transaction& setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo);
+
+ // Indicates that the consumer should acquire the next frame as soon as it
+ // can and not wait for a frame to become available. This is only relevant
+ // in shared buffer mode.
+ Transaction& setAutoRefresh(const sp<SurfaceControl>& sc, bool autoRefresh);
+
+ // Queues up transactions using this token in SurfaceFlinger. By default, all transactions
+ // from a client are placed on the same queue. This can be used to prevent multiple
+ // transactions from blocking each other.
+ Transaction& setApplyToken(const sp<IBinder>& token);
+
+ Transaction& setStretchEffect(const sp<SurfaceControl>& sc, float left, float top,
+ float right, float bottom, float vecX, float vecY,
+ float maxAmount);
+
status_t setDisplaySurface(const sp<IBinder>& token,
const sp<IGraphicBufferProducer>& bufferProducer);
@@ -548,9 +561,8 @@
const Rect& layerStackRect, const Rect& displayRect);
void setDisplaySize(const sp<IBinder>& token, uint32_t width, uint32_t height);
void setAnimationTransaction();
- void setEarlyWakeup();
- void setExplicitEarlyWakeupStart();
- void setExplicitEarlyWakeupEnd();
+ void setEarlyWakeupStart();
+ void setEarlyWakeupEnd();
};
status_t clearLayerFrameStats(const sp<IBinder>& token) const;
@@ -558,8 +570,10 @@
static status_t clearAnimationFrameStats();
static status_t getAnimationFrameStats(FrameStats* outStats);
- static status_t getHdrCapabilities(const sp<IBinder>& display,
- HdrCapabilities* outCapabilities);
+ static status_t overrideHdrTypes(const sp<IBinder>& display,
+ const std::vector<ui::Hdr>& hdrTypes);
+
+ static status_t onPullAtom(const int32_t atomId, std::string* outData, bool* success);
static void setDisplayProjection(const sp<IBinder>& token, ui::Rotation orientation,
const Rect& layerStackRect, const Rect& displayRect);
@@ -579,6 +593,8 @@
const sp<IBinder>& stopLayerHandle,
const sp<IRegionSamplingListener>& listener);
static status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener);
+ static status_t addFpsListener(int32_t taskId, const sp<gui::IFpsListener>& listener);
+ static status_t removeFpsListener(const sp<gui::IFpsListener>& listener);
private:
virtual void onFirstRef();
@@ -592,50 +608,56 @@
class ScreenshotClient {
public:
- // if cropping isn't required, callers may pass in a default Rect, e.g.:
- // capture(display, producer, Rect(), reqWidth, ...);
- static status_t capture(const sp<IBinder>& display, ui::Dataspace reqDataSpace,
- ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
- uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
- ui::Rotation rotation, bool captureSecureLayers,
- sp<GraphicBuffer>* outBuffer, bool& outCapturedSecureLayers);
- static status_t capture(const sp<IBinder>& display, ui::Dataspace reqDataSpace,
- ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
- uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
- ui::Rotation rotation, sp<GraphicBuffer>* outBuffer);
- static status_t capture(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace,
- sp<GraphicBuffer>* outBuffer);
- static status_t captureLayers(const sp<IBinder>& layerHandle, ui::Dataspace reqDataSpace,
- ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
- float frameScale, sp<GraphicBuffer>* outBuffer);
- static status_t captureChildLayers(
- const sp<IBinder>& layerHandle, ui::Dataspace reqDataSpace,
- ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
- const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>&
- excludeHandles,
- float frameScale, sp<GraphicBuffer>* outBuffer);
+ static status_t captureDisplay(const DisplayCaptureArgs& captureArgs,
+ const sp<IScreenCaptureListener>& captureListener);
+ static status_t captureDisplay(uint64_t displayOrLayerStack,
+ const sp<IScreenCaptureListener>& captureListener);
+ static status_t captureLayers(const LayerCaptureArgs& captureArgs,
+ const sp<IScreenCaptureListener>& captureListener);
};
// ---------------------------------------------------------------------------
+class JankDataListener : public VirtualLightRefBase {
+public:
+ virtual ~JankDataListener() = 0;
+ virtual void onJankDataAvailable(const std::vector<JankData>& jankData) = 0;
+};
+
class TransactionCompletedListener : public BnTransactionCompletedListener {
TransactionCompletedListener();
- CallbackId getNextIdLocked() REQUIRES(mMutex);
+ int64_t getNextIdLocked() REQUIRES(mMutex);
std::mutex mMutex;
bool mListening GUARDED_BY(mMutex) = false;
- CallbackId mCallbackIdCounter GUARDED_BY(mMutex) = 1;
-
+ int64_t mCallbackIdCounter GUARDED_BY(mMutex) = 1;
struct CallbackTranslation {
TransactionCompletedCallback callbackFunction;
std::unordered_map<sp<IBinder>, sp<SurfaceControl>, SurfaceComposerClient::IBinderHash>
surfaceControls;
};
- std::unordered_map<CallbackId, CallbackTranslation> mCallbacks GUARDED_BY(mMutex);
+ struct SurfaceStatsCallbackEntry {
+ SurfaceStatsCallbackEntry(void* context, void* cookie, SurfaceStatsCallback callback)
+ : context(context),
+ cookie(cookie),
+ callback(callback) {}
+
+ void* context;
+ void* cookie;
+ SurfaceStatsCallback callback;
+ };
+
+ std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> mCallbacks
+ GUARDED_BY(mMutex);
+ std::multimap<sp<IBinder>, sp<JankDataListener>> mJankListeners GUARDED_BY(mMutex);
+ std::unordered_map<uint64_t /* graphicsBufferId */, ReleaseBufferCallback>
+ mReleaseBufferCallbacks GUARDED_BY(mMutex);
+ std::multimap<sp<IBinder>, SurfaceStatsCallbackEntry>
+ mSurfaceStatsListeners GUARDED_BY(mMutex);
public:
static sp<TransactionCompletedListener> getInstance();
@@ -646,13 +668,38 @@
CallbackId addCallbackFunction(
const TransactionCompletedCallback& callbackFunction,
const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>&
- surfaceControls);
+ surfaceControls,
+ CallbackId::Type callbackType);
- void addSurfaceControlToCallbacks(const sp<SurfaceControl>& surfaceControl,
- const std::unordered_set<CallbackId>& callbackIds);
+ void addSurfaceControlToCallbacks(
+ const sp<SurfaceControl>& surfaceControl,
+ const std::unordered_set<CallbackId, CallbackIdHash>& callbackIds);
- // Overrides BnTransactionCompletedListener's onTransactionCompleted
+ /*
+ * Adds a jank listener to be informed about SurfaceFlinger's jank classification for a specific
+ * surface. Jank classifications arrive as part of the transaction callbacks about previous
+ * frames submitted to this Surface.
+ */
+ void addJankListener(const sp<JankDataListener>& listener, sp<SurfaceControl> surfaceControl);
+
+ /**
+ * Removes a jank listener previously added to addJankCallback.
+ */
+ void removeJankListener(const sp<JankDataListener>& listener);
+
+ void addSurfaceStatsListener(void* context, void* cookie, sp<SurfaceControl> surfaceControl,
+ SurfaceStatsCallback listener);
+ void removeSurfaceStatsListener(void* context, void* cookie);
+
+ void setReleaseBufferCallback(uint64_t /* graphicsBufferId */, ReleaseBufferCallback);
+ void removeReleaseBufferCallback(uint64_t /* graphicsBufferId */);
+
+ // BnTransactionCompletedListener overrides
void onTransactionCompleted(ListenerStats stats) override;
+ void onReleaseBuffer(uint64_t /* graphicsBufferId */, sp<Fence> releaseFence) override;
+
+private:
+ ReleaseBufferCallback popReleaseBufferCallbackLocked(uint64_t /* graphicsBufferId */);
};
} // namespace android
diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h
index ac2bbcc..9ee4636 100644
--- a/libs/gui/include/gui/SurfaceControl.h
+++ b/libs/gui/include/gui/SurfaceControl.h
@@ -20,7 +20,6 @@
#include <stdint.h>
#include <sys/types.h>
-#include <utils/KeyedVector.h>
#include <utils/RefBase.h>
#include <utils/threads.h>
@@ -38,14 +37,19 @@
class IGraphicBufferProducer;
class Surface;
class SurfaceComposerClient;
+class BLASTBufferQueue;
// ---------------------------------------------------------------------------
class SurfaceControl : public RefBase
{
public:
- static sp<SurfaceControl> readFromParcel(const Parcel* parcel);
- void writeToParcel(Parcel* parcel);
+ static status_t readFromParcel(const Parcel& parcel, sp<SurfaceControl>* outSurfaceControl);
+ status_t writeToParcel(Parcel& parcel);
+
+ static status_t readNullableFromParcel(const Parcel& parcel,
+ sp<SurfaceControl>* outSurfaceControl);
+ static status_t writeNullableToParcel(Parcel& parcel, const sp<SurfaceControl>& surfaceControl);
static bool isValid(const sp<SurfaceControl>& surface) {
return (surface != nullptr) && surface->isValid();
@@ -67,11 +71,13 @@
static status_t writeSurfaceToParcel(
const sp<SurfaceControl>& control, Parcel* parcel);
- sp<Surface> getSurface() const;
- sp<Surface> createSurface() const;
+ sp<Surface> getSurface();
+ sp<Surface> createSurface();
sp<IBinder> getHandle() const;
+ sp<IBinder> getLayerStateHandle() const;
+ int32_t getLayerId() const;
- sp<IGraphicBufferProducer> getIGraphicBufferProducer() const;
+ sp<IGraphicBufferProducer> getIGraphicBufferProducer();
status_t clearLayerFrameStats() const;
status_t getLayerFrameStats(FrameStats* outStats) const;
@@ -81,11 +87,16 @@
uint32_t getTransformHint() const;
void setTransformHint(uint32_t hint);
+ void updateDefaultBufferSize(uint32_t width, uint32_t height);
explicit SurfaceControl(const sp<SurfaceControl>& other);
SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle,
- const sp<IGraphicBufferProducer>& gbp, uint32_t transformHint = 0);
+ const sp<IGraphicBufferProducer>& gbp, int32_t layerId,
+ uint32_t width = 0, uint32_t height = 0, PixelFormat format = 0,
+ uint32_t transformHint = 0, uint32_t flags = 0);
+
+ sp<SurfaceControl> getParentingLayer();
private:
// can't be copied
@@ -97,7 +108,7 @@
~SurfaceControl();
- sp<Surface> generateSurfaceLocked() const;
+ sp<Surface> generateSurfaceLocked();
status_t validate() const;
sp<SurfaceComposerClient> mClient;
@@ -105,7 +116,14 @@
sp<IGraphicBufferProducer> mGraphicBufferProducer;
mutable Mutex mLock;
mutable sp<Surface> mSurfaceData;
+ mutable sp<BLASTBufferQueue> mBbq;
+ mutable sp<SurfaceControl> mBbqChild;
+ int32_t mLayerId;
uint32_t mTransformHint;
+ uint32_t mWidth;
+ uint32_t mHeight;
+ PixelFormat mFormat;
+ uint32_t mCreateFlags;
};
}; // namespace android
diff --git a/libs/gui/include/gui/SyncScreenCaptureListener.h b/libs/gui/include/gui/SyncScreenCaptureListener.h
new file mode 100644
index 0000000..0784fbc
--- /dev/null
+++ b/libs/gui/include/gui/SyncScreenCaptureListener.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <android/gui/BnScreenCaptureListener.h>
+#include <gui/SurfaceComposerClient.h>
+#include <future>
+
+namespace android {
+
+using gui::ScreenCaptureResults;
+
+struct SyncScreenCaptureListener : gui::BnScreenCaptureListener {
+public:
+ binder::Status onScreenCaptureCompleted(const ScreenCaptureResults& captureResults) override {
+ resultsPromise.set_value(captureResults);
+ return binder::Status::ok();
+ }
+
+ ScreenCaptureResults waitForResults() {
+ std::future<ScreenCaptureResults> resultsFuture = resultsPromise.get_future();
+ const auto screenCaptureResults = resultsFuture.get();
+ screenCaptureResults.fence->waitForever("");
+ return screenCaptureResults;
+ }
+
+private:
+ std::promise<ScreenCaptureResults> resultsPromise;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/include/gui/TransactionTracing.h b/libs/gui/include/gui/TransactionTracing.h
new file mode 100644
index 0000000..9efba47
--- /dev/null
+++ b/libs/gui/include/gui/TransactionTracing.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <android/gui/BnTransactionTraceListener.h>
+#include <utils/Mutex.h>
+
+namespace android {
+
+class TransactionTraceListener : public gui::BnTransactionTraceListener {
+ static std::mutex sMutex;
+ static sp<TransactionTraceListener> sInstance;
+
+ TransactionTraceListener();
+
+public:
+ static sp<TransactionTraceListener> getInstance();
+
+ binder::Status onToggled(bool enabled) override;
+
+ bool isTracingEnabled();
+
+private:
+ bool mTracingEnabled = false;
+};
+
+} // namespace android
diff --git a/libs/gui/include/gui/view/Surface.h b/libs/gui/include/gui/view/Surface.h
index cc64fd4..f7dcbc6 100644
--- a/libs/gui/include/gui/view/Surface.h
+++ b/libs/gui/include/gui/view/Surface.h
@@ -21,6 +21,7 @@
#include <utils/StrongPointer.h>
#include <utils/String16.h>
+#include <binder/IBinder.h>
#include <binder/Parcelable.h>
namespace android {
@@ -43,6 +44,7 @@
String16 name;
sp<IGraphicBufferProducer> graphicBufferProducer;
+ sp<IBinder> surfaceControlHandle;
virtual status_t writeToParcel(Parcel* parcel) const override;
virtual status_t readFromParcel(const Parcel* parcel) override;
diff --git a/libs/gui/include/private/gui/ParcelUtils.h b/libs/gui/include/private/gui/ParcelUtils.h
new file mode 100644
index 0000000..1cdd07e
--- /dev/null
+++ b/libs/gui/include/private/gui/ParcelUtils.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#pragma once
+
+#include <cstring>
+
+#define SAFE_PARCEL(FUNC, ...) \
+ { \
+ status_t error = FUNC(__VA_ARGS__); \
+ if (error) { \
+ ALOGE("ERROR(%s, %d). Failed to call parcel %s(%s)", strerror(-error), error, #FUNC, \
+ #__VA_ARGS__); \
+ return error; \
+ } \
+ }
+
+#define SAFE_PARCEL_READ_SIZE(FUNC, COUNT, SIZE) \
+ { \
+ SAFE_PARCEL(FUNC, COUNT); \
+ if (static_cast<unsigned int>(*COUNT) > SIZE) { \
+ ALOGE("ERROR(BAD_VALUE). %s was greater than dataSize", #COUNT); \
+ return BAD_VALUE; \
+ } \
+ }
\ No newline at end of file
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index 6077b4e..c801c62 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -23,7 +23,7 @@
srcs: [
"BLASTBufferQueue_test.cpp",
- "BufferItemConsumer_test.cpp",
+ "BufferItemConsumer_test.cpp",
"BufferQueue_test.cpp",
"CpuConsumer_test.cpp",
"EndToEndNativeInputTest.cpp",
@@ -67,6 +67,30 @@
header_libs: ["libsurfaceflinger_headers"],
}
+// Build the tests that need to run with both 32bit and 64bit.
+cc_test {
+ name: "libgui_multilib_test",
+ test_suites: ["device-tests"],
+
+ clang: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ srcs: [
+ "DisplayEventStructLayout_test.cpp",
+ ],
+
+ shared_libs: [
+ "libgui",
+ ],
+
+ compile_multilib: "both",
+
+ header_libs: ["libsurfaceflinger_headers"],
+}
+
// Build a separate binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
// This test has a main method, and requires a separate binary to be built.
// To add move tests like this, just add additional cc_test statements,
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index da5bbdd..5a5da97 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -24,9 +24,11 @@
#include <gui/FrameTimestamps.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/IProducerListener.h>
+#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
+#include <gui/SyncScreenCaptureListener.h>
#include <private/gui/ComposerService.h>
-#include <ui/DisplayConfig.h>
+#include <ui/DisplayMode.h>
#include <ui/GraphicBuffer.h>
#include <ui/GraphicTypes.h>
#include <ui/Transform.h>
@@ -43,20 +45,21 @@
class BLASTBufferQueueHelper {
public:
BLASTBufferQueueHelper(const sp<SurfaceControl>& sc, int width, int height) {
- mBlastBufferQueueAdapter = new BLASTBufferQueue(sc, width, height);
+ mBlastBufferQueueAdapter = new BLASTBufferQueue("TestBLASTBufferQueue", sc, width, height,
+ PIXEL_FORMAT_RGBA_8888);
}
void update(const sp<SurfaceControl>& sc, int width, int height) {
- mBlastBufferQueueAdapter->update(sc, width, height);
+ mBlastBufferQueueAdapter->update(sc, width, height, PIXEL_FORMAT_RGBA_8888);
}
void setNextTransaction(Transaction* next) {
mBlastBufferQueueAdapter->setNextTransaction(next);
}
- int getWidth() { return mBlastBufferQueueAdapter->mWidth; }
+ int getWidth() { return mBlastBufferQueueAdapter->mSize.width; }
- int getHeight() { return mBlastBufferQueueAdapter->mHeight; }
+ int getHeight() { return mBlastBufferQueueAdapter->mSize.height; }
Transaction* getNextTransaction() { return mBlastBufferQueueAdapter->mNextTransaction; }
@@ -70,13 +73,34 @@
void waitForCallbacks() {
std::unique_lock lock{mBlastBufferQueueAdapter->mMutex};
- while (mBlastBufferQueueAdapter->mSubmitted.size() > 0) {
+ // Wait until all but one of the submitted buffers have been released.
+ while (mBlastBufferQueueAdapter->mSubmitted.size() > 1) {
mBlastBufferQueueAdapter->mCallbackCV.wait(lock);
}
}
+ void setTransactionCompleteCallback(int64_t frameNumber) {
+ mBlastBufferQueueAdapter->setTransactionCompleteCallback(frameNumber, [&](int64_t frame) {
+ std::unique_lock lock{mMutex};
+ mLastTransactionCompleteFrameNumber = frame;
+ mCallbackCV.notify_all();
+ });
+ }
+
+ void waitForCallback(int64_t frameNumber) {
+ std::unique_lock lock{mMutex};
+ // Wait until all but one of the submitted buffers have been released.
+ while (mLastTransactionCompleteFrameNumber < frameNumber) {
+ mCallbackCV.wait(lock);
+ }
+ }
+
private:
sp<BLASTBufferQueue> mBlastBufferQueueAdapter;
+
+ std::mutex mMutex;
+ std::condition_variable mCallbackCV;
+ int64_t mLastTransactionCompleteFrameNumber = -1;
};
class BLASTBufferQueueTest : public ::testing::Test {
@@ -104,9 +128,9 @@
t.apply();
t.clear();
- DisplayConfig config;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(mDisplayToken, &config));
- const ui::Size& resolution = config.resolution;
+ ui::DisplayMode mode;
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(mDisplayToken, &mode));
+ const ui::Size& resolution = mode.resolution;
mDisplayWidth = resolution.getWidth();
mDisplayHeight = resolution.getHeight();
@@ -116,14 +140,20 @@
/*parent*/ nullptr);
t.setLayerStack(mSurfaceControl, 0)
.setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
- .setFrame(mSurfaceControl, Rect(resolution))
.show(mSurfaceControl)
.setDataspace(mSurfaceControl, ui::Dataspace::V0_SRGB)
.apply();
+
+ mCaptureArgs.displayToken = mDisplayToken;
+ mCaptureArgs.dataspace = ui::Dataspace::V0_SRGB;
}
- void setUpProducer(BLASTBufferQueueHelper adapter, sp<IGraphicBufferProducer>& producer) {
- auto igbProducer = adapter.getIGraphicBufferProducer();
+ void setUpProducer(BLASTBufferQueueHelper& adapter, sp<IGraphicBufferProducer>& producer) {
+ producer = adapter.getIGraphicBufferProducer();
+ setUpProducer(producer);
+ }
+
+ void setUpProducer(sp<IGraphicBufferProducer>& igbProducer) {
ASSERT_NE(nullptr, igbProducer.get());
ASSERT_EQ(NO_ERROR, igbProducer->setMaxDequeuedBufferCount(2));
IGraphicBufferProducer::QueueBufferOutput qbOutput;
@@ -131,7 +161,6 @@
igbProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false,
&qbOutput));
ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
- producer = igbProducer;
}
void fillBuffer(uint32_t* bufData, Rect rect, uint32_t stride, uint8_t r, uint8_t g,
@@ -165,18 +194,20 @@
void checkScreenCapture(uint8_t r, uint8_t g, uint8_t b, Rect region, int32_t border = 0,
bool outsideRegion = false) {
+ sp<GraphicBuffer>& captureBuf = mCaptureResults.buffer;
const auto epsilon = 3;
- const auto width = mScreenCaptureBuf->getWidth();
- const auto height = mScreenCaptureBuf->getHeight();
- const auto stride = mScreenCaptureBuf->getStride();
+ const auto width = captureBuf->getWidth();
+ const auto height = captureBuf->getHeight();
+ const auto stride = captureBuf->getStride();
uint32_t* bufData;
- mScreenCaptureBuf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_READ_OFTEN),
- reinterpret_cast<void**>(&bufData));
+ captureBuf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_READ_OFTEN),
+ reinterpret_cast<void**>(&bufData));
for (uint32_t row = 0; row < height; row++) {
for (uint32_t col = 0; col < width; col++) {
uint8_t* pixel = (uint8_t*)(bufData + (row * stride) + col);
+ ASSERT_NE(nullptr, pixel);
bool inRegion;
if (!outsideRegion) {
inRegion = row >= region.top + border && row < region.bottom - border &&
@@ -186,18 +217,58 @@
col >= region.left - border && col < region.right + border;
}
if (!outsideRegion && inRegion) {
- EXPECT_GE(epsilon, abs(r - *(pixel)));
- EXPECT_GE(epsilon, abs(g - *(pixel + 1)));
- EXPECT_GE(epsilon, abs(b - *(pixel + 2)));
+ ASSERT_GE(epsilon, abs(r - *(pixel)));
+ ASSERT_GE(epsilon, abs(g - *(pixel + 1)));
+ ASSERT_GE(epsilon, abs(b - *(pixel + 2)));
} else if (outsideRegion && !inRegion) {
- EXPECT_GE(epsilon, abs(r - *(pixel)));
- EXPECT_GE(epsilon, abs(g - *(pixel + 1)));
- EXPECT_GE(epsilon, abs(b - *(pixel + 2)));
+ ASSERT_GE(epsilon, abs(r - *(pixel)));
+ ASSERT_GE(epsilon, abs(g - *(pixel + 1)));
+ ASSERT_GE(epsilon, abs(b - *(pixel + 2)));
}
+ ASSERT_EQ(false, ::testing::Test::HasFailure());
}
}
- mScreenCaptureBuf->unlock();
- ASSERT_EQ(false, ::testing::Test::HasFailure());
+ captureBuf->unlock();
+ }
+
+ static status_t captureDisplay(DisplayCaptureArgs& captureArgs,
+ ScreenCaptureResults& captureResults) {
+ const auto sf = ComposerService::getComposerService();
+ SurfaceComposerClient::Transaction().apply(true);
+
+ const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
+ status_t status = sf->captureDisplay(captureArgs, captureListener);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ captureResults = captureListener->waitForResults();
+ return captureResults.result;
+ }
+
+ void queueBuffer(sp<IGraphicBufferProducer> igbp, uint8_t r, uint8_t g, uint8_t b,
+ nsecs_t presentTimeDelay) {
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+ auto ret = igbp->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+ PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+ nullptr, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+ ASSERT_EQ(OK, igbp->requestBuffer(slot, &buf));
+
+ uint32_t* bufData;
+ buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
+ reinterpret_cast<void**>(&bufData));
+ fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight() / 2), buf->getStride(), r, g, b);
+ buf->unlock();
+
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ nsecs_t timestampNanos = systemTime() + presentTimeDelay;
+ IGraphicBufferProducer::QueueBufferInput input(timestampNanos, false, HAL_DATASPACE_UNKNOWN,
+ Rect(mDisplayWidth, mDisplayHeight / 2),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+ Fence::NO_FENCE);
+ igbp->queueBuffer(slot, input, &qbOutput);
}
sp<SurfaceComposerClient> mClient;
@@ -206,10 +277,12 @@
sp<IBinder> mDisplayToken;
sp<SurfaceControl> mSurfaceControl;
- sp<GraphicBuffer> mScreenCaptureBuf;
uint32_t mDisplayWidth;
uint32_t mDisplayHeight;
+
+ DisplayCaptureArgs mCaptureArgs;
+ ScreenCaptureResults mCaptureResults;
};
TEST_F(BLASTBufferQueueTest, CreateBLASTBufferQueue) {
@@ -228,8 +301,15 @@
PIXEL_FORMAT_RGBA_8888);
adapter.update(updateSurface, mDisplayWidth / 2, mDisplayHeight / 2);
ASSERT_EQ(updateSurface, adapter.getSurfaceControl());
- ASSERT_EQ(mDisplayWidth / 2, adapter.getWidth());
- ASSERT_EQ(mDisplayHeight / 2, adapter.getHeight());
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+
+ int32_t width;
+ igbProducer->query(NATIVE_WINDOW_WIDTH, &width);
+ ASSERT_EQ(mDisplayWidth / 2, width);
+ int32_t height;
+ igbProducer->query(NATIVE_WINDOW_HEIGHT, &height);
+ ASSERT_EQ(mDisplayHeight / 2, height);
}
TEST_F(BLASTBufferQueueTest, SetNextTransaction) {
@@ -255,7 +335,8 @@
nsecs_t desiredPresentTime = systemTime() + nsecs_t(5 * 1e8);
IGraphicBufferProducer::QueueBufferOutput qbOutput;
- IGraphicBufferProducer::QueueBufferInput input(desiredPresentTime, false, HAL_DATASPACE_UNKNOWN,
+ IGraphicBufferProducer::QueueBufferInput input(desiredPresentTime, true /* autotimestamp */,
+ HAL_DATASPACE_UNKNOWN,
Rect(mDisplayWidth, mDisplayHeight),
NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
Fence::NO_FENCE);
@@ -291,7 +372,8 @@
buf->unlock();
IGraphicBufferProducer::QueueBufferOutput qbOutput;
- IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+ IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */,
+ HAL_DATASPACE_UNKNOWN,
Rect(mDisplayWidth, mDisplayHeight),
NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
Fence::NO_FENCE);
@@ -301,12 +383,7 @@
adapter.waitForCallbacks();
// capture screen and verify that it is red
- bool capturedSecureLayers;
- ASSERT_EQ(NO_ERROR,
- mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
- ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(),
- mDisplayWidth, mDisplayHeight,
- /*useIdentityTransform*/ false));
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
}
@@ -341,7 +418,8 @@
nullptr, nullptr);
ASSERT_EQ(NO_ERROR, ret);
IGraphicBufferProducer::QueueBufferOutput qbOutput;
- IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+ IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */,
+ HAL_DATASPACE_UNKNOWN,
Rect(mDisplayWidth, mDisplayHeight),
NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
Fence::NO_FENCE);
@@ -374,7 +452,8 @@
buf->unlock();
IGraphicBufferProducer::QueueBufferOutput qbOutput;
- IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+ IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */,
+ HAL_DATASPACE_UNKNOWN,
Rect(mDisplayWidth, mDisplayHeight / 2),
NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
Fence::NO_FENCE);
@@ -383,14 +462,11 @@
adapter.waitForCallbacks();
// capture screen and verify that it is red
- bool capturedSecureLayers;
- ASSERT_EQ(NO_ERROR,
- mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
- ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(),
- mDisplayWidth, mDisplayHeight,
- /*useIdentityTransform*/ false));
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+
ASSERT_NO_FATAL_FAILURE(
- checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+ checkScreenCapture(r, g, b,
+ {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight / 2}));
}
TEST_F(BLASTBufferQueueTest, SetCrop_ScalingModeScaleCrop) {
@@ -407,7 +483,7 @@
ASSERT_NE(nullptr, bg.get());
Transaction t;
t.setLayerStack(bg, 0)
- .setCrop_legacy(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
+ .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
.setColor(bg, half3{0, 0, 0})
.setLayer(bg, 0)
.apply();
@@ -435,7 +511,8 @@
buf->unlock();
IGraphicBufferProducer::QueueBufferOutput qbOutput;
- IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+ IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */,
+ HAL_DATASPACE_UNKNOWN,
Rect(bufferSideLength, finalCropSideLength),
NATIVE_WINDOW_SCALING_MODE_SCALE_CROP, 0,
Fence::NO_FENCE);
@@ -444,21 +521,239 @@
adapter.waitForCallbacks();
// capture screen and verify that it is red
- bool capturedSecureLayers;
- ASSERT_EQ(NO_ERROR,
- mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
- ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(),
- mDisplayWidth, mDisplayHeight,
- /*useIdentityTransform*/ false));
- ASSERT_NO_FATAL_FAILURE(
- checkScreenCapture(r, g, b,
- {0, 0, (int32_t)bufferSideLength, (int32_t)bufferSideLength}));
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(r, g, b,
+ {10, 10, (int32_t)bufferSideLength - 10,
+ (int32_t)bufferSideLength - 10}));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(0, 0, 0,
{0, 0, (int32_t)bufferSideLength, (int32_t)bufferSideLength},
/*border*/ 0, /*outsideRegion*/ true));
}
+TEST_F(BLASTBufferQueueTest, ScaleCroppedBufferToBufferSize) {
+ // add black background
+ auto bg = mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceEffect);
+ ASSERT_NE(nullptr, bg.get());
+ Transaction t;
+ t.setLayerStack(bg, 0)
+ .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
+ .setColor(bg, half3{0, 0, 0})
+ .setLayer(bg, 0)
+ .apply();
+
+ Rect windowSize(1000, 1000);
+ Rect bufferSize(windowSize);
+ Rect bufferCrop(200, 200, 700, 700);
+
+ BLASTBufferQueueHelper adapter(mSurfaceControl, windowSize.getWidth(), windowSize.getHeight());
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+ auto ret = igbProducer->dequeueBuffer(&slot, &fence, bufferSize.getWidth(),
+ bufferSize.getHeight(), PIXEL_FORMAT_RGBA_8888,
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+ ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+ uint32_t* bufData;
+ buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
+ reinterpret_cast<void**>(&bufData));
+ // fill buffer with grey
+ fillBuffer(bufData, bufferSize, buf->getStride(), 127, 127, 127);
+
+ // fill crop area with different colors so we can verify the cropped region has been scaled
+ // correctly.
+ fillBuffer(bufData, Rect(200, 200, 450, 450), buf->getStride(), /* rgb */ 255, 0, 0);
+ fillBuffer(bufData, Rect(200, 451, 450, 700), buf->getStride(), /* rgb */ 0, 255, 0);
+ fillBuffer(bufData, Rect(451, 200, 700, 450), buf->getStride(), /* rgb */ 0, 0, 255);
+ fillBuffer(bufData, Rect(451, 451, 700, 700), buf->getStride(), /* rgb */ 255, 0, 0);
+ buf->unlock();
+
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */,
+ HAL_DATASPACE_UNKNOWN,
+ bufferCrop /* Rect::INVALID_RECT */,
+ NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW, 0,
+ Fence::NO_FENCE);
+ igbProducer->queueBuffer(slot, input, &qbOutput);
+ ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
+
+ adapter.waitForCallbacks();
+
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+
+ // Verify cropped region is scaled correctly.
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(255, 0, 0, {10, 10, 490, 490}));
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 255, 0, {10, 510, 490, 990}));
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 255, {510, 10, 990, 490}));
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(255, 0, 0, {510, 510, 990, 990}));
+ // Verify outside region is black.
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 0,
+ {0, 0, (int32_t)windowSize.getWidth(),
+ (int32_t)windowSize.getHeight()},
+ /*border*/ 0, /*outsideRegion*/ true));
+}
+
+TEST_F(BLASTBufferQueueTest, ScaleCroppedBufferToWindowSize) {
+ // add black background
+ auto bg = mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceEffect);
+ ASSERT_NE(nullptr, bg.get());
+ Transaction t;
+ t.setLayerStack(bg, 0)
+ .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
+ .setColor(bg, half3{0, 0, 0})
+ .setLayer(bg, 0)
+ .apply();
+
+ Rect windowSize(1000, 1000);
+ Rect bufferSize(500, 500);
+ Rect bufferCrop(100, 100, 350, 350);
+
+ BLASTBufferQueueHelper adapter(mSurfaceControl, windowSize.getWidth(), windowSize.getHeight());
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+ auto ret = igbProducer->dequeueBuffer(&slot, &fence, bufferSize.getWidth(),
+ bufferSize.getHeight(), PIXEL_FORMAT_RGBA_8888,
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+ ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+ uint32_t* bufData;
+ buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
+ reinterpret_cast<void**>(&bufData));
+ // fill buffer with grey
+ fillBuffer(bufData, bufferSize, buf->getStride(), 127, 127, 127);
+
+ // fill crop area with different colors so we can verify the cropped region has been scaled
+ // correctly.
+ fillBuffer(bufData, Rect(100, 100, 225, 225), buf->getStride(), /* rgb */ 255, 0, 0);
+ fillBuffer(bufData, Rect(100, 226, 225, 350), buf->getStride(), /* rgb */ 0, 255, 0);
+ fillBuffer(bufData, Rect(226, 100, 350, 225), buf->getStride(), /* rgb */ 0, 0, 255);
+ fillBuffer(bufData, Rect(226, 226, 350, 350), buf->getStride(), /* rgb */ 255, 0, 0);
+ buf->unlock();
+
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */,
+ HAL_DATASPACE_UNKNOWN,
+ bufferCrop /* Rect::INVALID_RECT */,
+ NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW, 0,
+ Fence::NO_FENCE);
+ igbProducer->queueBuffer(slot, input, &qbOutput);
+ ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
+
+ adapter.waitForCallbacks();
+
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ // Verify cropped region is scaled correctly.
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(255, 0, 0, {10, 10, 490, 490}));
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 255, 0, {10, 510, 490, 990}));
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 255, {510, 10, 990, 490}));
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(255, 0, 0, {510, 510, 990, 990}));
+ // Verify outside region is black.
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 0,
+ {0, 0, (int32_t)windowSize.getWidth(),
+ (int32_t)windowSize.getHeight()},
+ /*border*/ 0, /*outsideRegion*/ true));
+}
+
+class TestProducerListener : public BnProducerListener {
+public:
+ sp<IGraphicBufferProducer> mIgbp;
+ TestProducerListener(const sp<IGraphicBufferProducer>& igbp) : mIgbp(igbp) {}
+ void onBufferReleased() override {
+ sp<GraphicBuffer> buffer;
+ sp<Fence> fence;
+ mIgbp->detachNextBuffer(&buffer, &fence);
+ }
+};
+
+TEST_F(BLASTBufferQueueTest, CustomProducerListener) {
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ sp<IGraphicBufferProducer> igbProducer = adapter.getIGraphicBufferProducer();
+ ASSERT_NE(nullptr, igbProducer.get());
+ ASSERT_EQ(NO_ERROR, igbProducer->setMaxDequeuedBufferCount(2));
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ ASSERT_EQ(NO_ERROR,
+ igbProducer->connect(new TestProducerListener(igbProducer), NATIVE_WINDOW_API_CPU,
+ false, &qbOutput));
+ ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
+ for (int i = 0; i < 3; i++) {
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+ auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+ PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+ nullptr, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+ ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */,
+ HAL_DATASPACE_UNKNOWN,
+ Rect(mDisplayWidth, mDisplayHeight),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+ Fence::NO_FENCE);
+ igbProducer->queueBuffer(slot, input, &qbOutput);
+ }
+ adapter.waitForCallbacks();
+}
+
+TEST_F(BLASTBufferQueueTest, QueryNativeWindowQueuesToWindowComposer) {
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+
+ sp<android::Surface> surface = new Surface(adapter.getIGraphicBufferProducer());
+ ANativeWindow* nativeWindow = (ANativeWindow*)(surface.get());
+ int queuesToNativeWindow = 0;
+ int err = nativeWindow->query(nativeWindow, NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER,
+ &queuesToNativeWindow);
+ ASSERT_EQ(NO_ERROR, err);
+ ASSERT_EQ(queuesToNativeWindow, 1);
+}
+
+// Test a slow producer doesn't hold up a faster producer from the same client. Essentially tests
+// BBQ uses separate transaction queues.
+TEST_F(BLASTBufferQueueTest, OutOfOrderTransactionTest) {
+ sp<SurfaceControl> bgSurface =
+ mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState);
+ ASSERT_NE(nullptr, bgSurface.get());
+ Transaction t;
+ t.setLayerStack(bgSurface, 0)
+ .show(bgSurface)
+ .setDataspace(bgSurface, ui::Dataspace::V0_SRGB)
+ .setLayer(bgSurface, std::numeric_limits<int32_t>::max() - 1)
+ .apply();
+
+ BLASTBufferQueueHelper slowAdapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ sp<IGraphicBufferProducer> slowIgbProducer;
+ setUpProducer(slowAdapter, slowIgbProducer);
+ nsecs_t presentTimeDelay = std::chrono::nanoseconds(500ms).count();
+ queueBuffer(slowIgbProducer, 0 /* r */, 255 /* g */, 0 /* b */, presentTimeDelay);
+
+ BLASTBufferQueueHelper fastAdapter(bgSurface, mDisplayWidth, mDisplayHeight);
+ sp<IGraphicBufferProducer> fastIgbProducer;
+ setUpProducer(fastAdapter, fastIgbProducer);
+ uint8_t r = 255;
+ uint8_t g = 0;
+ uint8_t b = 0;
+ queueBuffer(fastIgbProducer, r, g, b, 0 /* presentTimeDelay */);
+ fastAdapter.waitForCallbacks();
+
+ // capture screen and verify that it is red
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(r, g, b,
+ {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight / 2}));
+}
+
class BLASTBufferQueueTransformTest : public BLASTBufferQueueTest {
public:
void test(uint32_t tr) {
@@ -481,20 +776,17 @@
fillQuadrants(buf);
IGraphicBufferProducer::QueueBufferOutput qbOutput;
- IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+ IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */,
+ HAL_DATASPACE_UNKNOWN,
Rect(bufWidth, bufHeight),
- NATIVE_WINDOW_SCALING_MODE_FREEZE, tr,
- Fence::NO_FENCE);
+ NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW,
+ tr, Fence::NO_FENCE);
igbProducer->queueBuffer(slot, input, &qbOutput);
ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
adapter.waitForCallbacks();
- bool capturedSecureLayers;
- ASSERT_EQ(NO_ERROR,
- mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
- ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888,
- Rect(), mDisplayWidth, mDisplayHeight,
- /*useIdentityTransform*/ false));
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+
switch (tr) {
case ui::Transform::ROT_0:
ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 0,
@@ -654,7 +946,7 @@
void setUpAndQueueBuffer(const sp<IGraphicBufferProducer>& igbProducer,
nsecs_t* requestedPresentTime, nsecs_t* postedTime,
IGraphicBufferProducer::QueueBufferOutput* qbOutput,
- bool getFrameTimestamps) {
+ bool getFrameTimestamps, nsecs_t requestedPresentTimeDelay = 0) {
int slot;
sp<Fence> fence;
sp<GraphicBuffer> buf;
@@ -664,7 +956,7 @@
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
- nsecs_t requestedTime = systemTime();
+ nsecs_t requestedTime = systemTime() + requestedPresentTimeDelay;
if (requestedPresentTime) *requestedPresentTime = requestedTime;
IGraphicBufferProducer::QueueBufferInput input(requestedTime, false, HAL_DATASPACE_UNKNOWN,
Rect(mDisplayWidth, mDisplayHeight),
@@ -674,6 +966,7 @@
if (postedTime) *postedTime = systemTime();
igbProducer->queueBuffer(slot, input, qbOutput);
}
+ sp<SurfaceControl> mBufferQueueSurfaceControl;
};
TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_Basic) {
@@ -685,6 +978,7 @@
IGraphicBufferProducer::QueueBufferOutput qbOutput;
nsecs_t requestedPresentTimeA = 0;
nsecs_t postedTimeA = 0;
+ adapter.setTransactionCompleteCallback(1);
setUpAndQueueBuffer(igbProducer, &requestedPresentTimeA, &postedTimeA, &qbOutput, true);
history.applyDelta(qbOutput.frameTimestamps);
@@ -695,7 +989,7 @@
ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime);
ASSERT_GE(events->postedTime, postedTimeA);
- adapter.waitForCallbacks();
+ adapter.waitForCallback(1);
// queue another buffer so we query for frame event deltas
nsecs_t requestedPresentTimeB = 0;
@@ -726,4 +1020,55 @@
// wait for any callbacks that have not been received
adapter.waitForCallbacks();
}
+
+TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_DroppedFrame) {
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+
+ ProducerFrameEventHistory history;
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ nsecs_t requestedPresentTimeA = 0;
+ nsecs_t postedTimeA = 0;
+ nsecs_t presentTimeDelay = std::chrono::nanoseconds(500ms).count();
+ setUpAndQueueBuffer(igbProducer, &requestedPresentTimeA, &postedTimeA, &qbOutput, true,
+ presentTimeDelay);
+ history.applyDelta(qbOutput.frameTimestamps);
+
+ FrameEvents* events = nullptr;
+ events = history.getFrame(1);
+ ASSERT_NE(nullptr, events);
+ ASSERT_EQ(1, events->frameNumber);
+ ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime);
+ ASSERT_GE(events->postedTime, postedTimeA);
+
+ // queue another buffer so the first can be dropped
+ nsecs_t requestedPresentTimeB = 0;
+ nsecs_t postedTimeB = 0;
+ setUpAndQueueBuffer(igbProducer, &requestedPresentTimeB, &postedTimeB, &qbOutput, true);
+ history.applyDelta(qbOutput.frameTimestamps);
+ events = history.getFrame(1);
+ ASSERT_NE(nullptr, events);
+
+ // frame number, requestedPresentTime, and postTime should not have changed
+ ASSERT_EQ(1, events->frameNumber);
+ ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime);
+ ASSERT_GE(events->postedTime, postedTimeA);
+
+ // a valid latchtime should not be set
+ ASSERT_FALSE(events->hasLatchInfo());
+ ASSERT_FALSE(events->hasDequeueReadyInfo());
+
+ ASSERT_NE(nullptr, events->gpuCompositionDoneFence);
+ ASSERT_NE(nullptr, events->displayPresentFence);
+ ASSERT_NE(nullptr, events->releaseFence);
+
+ // we should also have gotten the initial values for the next frame
+ events = history.getFrame(2);
+ ASSERT_NE(nullptr, events);
+ ASSERT_EQ(2, events->frameNumber);
+ ASSERT_EQ(requestedPresentTimeB, events->requestedPresentTime);
+ ASSERT_GE(events->postedTime, postedTimeB);
+}
+
} // namespace android
diff --git a/libs/gui/tests/DisplayEventStructLayout_test.cpp b/libs/gui/tests/DisplayEventStructLayout_test.cpp
new file mode 100644
index 0000000..bcd39db
--- /dev/null
+++ b/libs/gui/tests/DisplayEventStructLayout_test.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 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 <gtest/gtest.h>
+#include <gui/DisplayEventReceiver.h>
+
+namespace android::test {
+
+#define CHECK_OFFSET(type, member, expected_offset) \
+ static_assert((offsetof(type, member) == (expected_offset)), "")
+
+TEST(DisplayEventStructLayoutTest, TestEventAlignment) {
+ CHECK_OFFSET(DisplayEventReceiver::Event, vsync, 24);
+ CHECK_OFFSET(DisplayEventReceiver::Event, hotplug, 24);
+ CHECK_OFFSET(DisplayEventReceiver::Event, modeChange, 24);
+
+ CHECK_OFFSET(DisplayEventReceiver::Event::Header, type, 0);
+ CHECK_OFFSET(DisplayEventReceiver::Event::Header, displayId, 8);
+ CHECK_OFFSET(DisplayEventReceiver::Event::Header, timestamp, 16);
+
+ CHECK_OFFSET(DisplayEventReceiver::Event::VSync, count, 0);
+ CHECK_OFFSET(DisplayEventReceiver::Event::VSync, expectedVSyncTimestamp, 8);
+ CHECK_OFFSET(DisplayEventReceiver::Event::VSync, deadlineTimestamp, 16);
+ CHECK_OFFSET(DisplayEventReceiver::Event::VSync, frameInterval, 24);
+ CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncId, 32);
+
+ CHECK_OFFSET(DisplayEventReceiver::Event::Hotplug, connected, 0);
+
+ CHECK_OFFSET(DisplayEventReceiver::Event::ModeChange, modeId, 0);
+ CHECK_OFFSET(DisplayEventReceiver::Event::ModeChange, vsyncPeriod, 8);
+
+ CHECK_OFFSET(DisplayEventReceiver::Event::FrameRateOverride, uid, 0);
+ CHECK_OFFSET(DisplayEventReceiver::Event::FrameRateOverride, frameRateHz, 8);
+}
+
+} // namespace android::test
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index b1d3ecb..49c44a7 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -36,18 +36,20 @@
#include <gui/SurfaceComposerClient.h>
#include <gui/SurfaceControl.h>
-#include <input/InputWindow.h>
-#include <input/IInputFlinger.h>
-#include <input/InputTransport.h>
+#include <android/os/IInputFlinger.h>
#include <input/Input.h>
+#include <input/InputTransport.h>
+#include <input/InputWindow.h>
-#include <ui/DisplayConfig.h>
+#include <ui/DisplayMode.h>
#include <ui/Rect.h>
#include <ui/Region.h>
+using android::os::IInputFlinger;
-namespace android {
-namespace test {
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+namespace android::test {
using Transaction = SurfaceComposerClient::Transaction;
@@ -62,16 +64,16 @@
// We use the top 10 layers as a way to haphazardly place ourselves above anything else.
static const int LAYER_BASE = INT32_MAX - 10;
+static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 5s;
class InputSurface {
public:
InputSurface(const sp<SurfaceControl> &sc, int width, int height) {
mSurfaceControl = sc;
- InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel);
-
mInputFlinger = getInputFlinger();
- mInputFlinger->registerInputChannel(mServerChannel);
+ mClientChannel = std::make_shared<InputChannel>();
+ mInputFlinger->createInputChannel("testchannels", mClientChannel.get());
populateInputInfo(width, height);
@@ -153,28 +155,60 @@
EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS);
}
- ~InputSurface() {
- mInputFlinger->unregisterInputChannel(mServerChannel);
+ void expectTapWithFlag(int x, int y, int32_t flags) {
+ InputEvent *ev = consumeEvent();
+ ASSERT_NE(ev, nullptr);
+ ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, ev->getType());
+ MotionEvent *mev = static_cast<MotionEvent *>(ev);
+ EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction());
+ EXPECT_EQ(x, mev->getX(0));
+ EXPECT_EQ(y, mev->getY(0));
+ EXPECT_EQ(flags, mev->getFlags() & flags);
+
+ ev = consumeEvent();
+ ASSERT_NE(ev, nullptr);
+ ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, ev->getType());
+ mev = static_cast<MotionEvent *>(ev);
+ EXPECT_EQ(AMOTION_EVENT_ACTION_UP, mev->getAction());
+ EXPECT_EQ(flags, mev->getFlags() & flags);
}
- void doTransaction(std::function<void(SurfaceComposerClient::Transaction&,
- const sp<SurfaceControl>&)> transactionBody) {
+ virtual ~InputSurface() {
+ mInputFlinger->removeInputChannel(mClientChannel->getConnectionToken());
+ }
+
+ virtual void doTransaction(
+ std::function<void(SurfaceComposerClient::Transaction &, const sp<SurfaceControl> &)>
+ transactionBody) {
SurfaceComposerClient::Transaction t;
transactionBody(t, mSurfaceControl);
t.apply(true);
}
- void showAt(int x, int y) {
+ virtual void showAt(int x, int y, Rect crop = Rect(0, 0, 100, 100)) {
SurfaceComposerClient::Transaction t;
t.show(mSurfaceControl);
t.setInputWindowInfo(mSurfaceControl, mInputInfo);
t.setLayer(mSurfaceControl, LAYER_BASE);
t.setPosition(mSurfaceControl, x, y);
- t.setCrop_legacy(mSurfaceControl, Rect(0, 0, 100, 100));
+ t.setCrop(mSurfaceControl, crop);
t.setAlpha(mSurfaceControl, 1);
t.apply(true);
}
+ void requestFocus() {
+ SurfaceComposerClient::Transaction t;
+ FocusRequest request;
+ request.token = mInputInfo.token;
+ request.windowName = mInputInfo.name;
+ request.focusedToken = nullptr;
+ request.focusedWindowName = "";
+ request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
+ request.displayId = 0;
+ t.setFocusedWindow(request);
+ t.apply(true);
+ }
+
private:
void waitForEventAvailable() {
struct pollfd fd;
@@ -185,14 +219,13 @@
}
void populateInputInfo(int width, int height) {
- mInputInfo.token = mServerChannel->getConnectionToken();
+ mInputInfo.token = mClientChannel->getConnectionToken();
mInputInfo.name = "Test info";
- mInputInfo.layoutParamsFlags = InputWindowInfo::FLAG_NOT_TOUCH_MODAL;
- mInputInfo.layoutParamsType = InputWindowInfo::TYPE_BASE_APPLICATION;
- mInputInfo.dispatchingTimeout = seconds_to_nanoseconds(5);
+ mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCH_MODAL;
+ mInputInfo.type = InputWindowInfo::Type::BASE_APPLICATION;
+ mInputInfo.dispatchingTimeout = 5s;
mInputInfo.globalScaleFactor = 1.0;
- mInputInfo.canReceiveKeys = true;
- mInputInfo.hasFocus = true;
+ mInputInfo.focusable = true;
mInputInfo.hasWallpaper = false;
mInputInfo.paused = false;
@@ -201,19 +234,19 @@
// TODO: Fill in from SF?
mInputInfo.ownerPid = 11111;
mInputInfo.ownerUid = 11111;
- mInputInfo.inputFeatures = 0;
mInputInfo.displayId = 0;
InputApplicationInfo aInfo;
aInfo.token = new BBinder();
aInfo.name = "Test app info";
- aInfo.dispatchingTimeout = seconds_to_nanoseconds(5);
+ aInfo.dispatchingTimeoutMillis =
+ std::chrono::duration_cast<std::chrono::milliseconds>(DISPATCHING_TIMEOUT).count();
mInputInfo.applicationInfo = aInfo;
}
public:
sp<SurfaceControl> mSurfaceControl;
- sp<InputChannel> mServerChannel, mClientChannel;
+ std::shared_ptr<InputChannel> mClientChannel;
sp<IInputFlinger> mInputFlinger;
InputWindowInfo mInputInfo;
@@ -222,6 +255,57 @@
InputConsumer* mInputConsumer;
};
+class BlastInputSurface : public InputSurface {
+public:
+ BlastInputSurface(const sp<SurfaceControl> &sc, const sp<SurfaceControl> &parentSc, int width,
+ int height)
+ : InputSurface(sc, width, height) {
+ mParentSurfaceControl = parentSc;
+ }
+
+ ~BlastInputSurface() = default;
+
+ static std::unique_ptr<BlastInputSurface> makeBlastInputSurface(
+ const sp<SurfaceComposerClient> &scc, int width, int height) {
+ sp<SurfaceControl> parentSc =
+ scc->createSurface(String8("Test Parent Surface"), 0 /* bufHeight */,
+ 0 /* bufWidth */, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceContainer);
+
+ sp<SurfaceControl> surfaceControl =
+ scc->createSurface(String8("Test Buffer Surface"), width, height,
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState,
+ parentSc->getHandle());
+ return std::make_unique<BlastInputSurface>(surfaceControl, parentSc, width, height);
+ }
+
+ void doTransaction(
+ std::function<void(SurfaceComposerClient::Transaction &, const sp<SurfaceControl> &)>
+ transactionBody) override {
+ SurfaceComposerClient::Transaction t;
+ transactionBody(t, mParentSurfaceControl);
+ t.apply(true);
+ }
+
+ void showAt(int x, int y, Rect crop = Rect(0, 0, 100, 100)) override {
+ SurfaceComposerClient::Transaction t;
+ t.show(mParentSurfaceControl);
+ t.setLayer(mParentSurfaceControl, LAYER_BASE);
+ t.setPosition(mParentSurfaceControl, x, y);
+ t.setCrop(mParentSurfaceControl, crop);
+
+ t.show(mSurfaceControl);
+ t.setInputWindowInfo(mSurfaceControl, mInputInfo);
+ t.setCrop(mSurfaceControl, crop);
+ t.setAlpha(mSurfaceControl, 1);
+ t.apply(true);
+ }
+
+private:
+ sp<SurfaceControl> mParentSurfaceControl;
+};
+
class InputSurfacesTest : public ::testing::Test {
public:
InputSurfacesTest() {
@@ -235,13 +319,13 @@
const auto display = mComposerClient->getInternalDisplayToken();
ASSERT_NE(display, nullptr);
- DisplayConfig config;
- ASSERT_EQ(NO_ERROR, mComposerClient->getActiveDisplayConfig(display, &config));
+ ui::DisplayMode mode;
+ ASSERT_EQ(NO_ERROR, mComposerClient->getActiveDisplayMode(display, &mode));
// After a new buffer is queued, SurfaceFlinger is notified and will
// latch the new buffer on next vsync. Let's heuristically wait for 3
// vsyncs.
- mBufferPostDelay = static_cast<int32_t>(1e6 / config.refreshRate) * 3;
+ mBufferPostDelay = static_cast<int32_t>(1e6 / mode.refreshRate) * 3;
}
void TearDown() {
@@ -252,15 +336,12 @@
return InputSurface::makeColorInputSurface(mComposerClient, width, height);
}
- void postBuffer(const sp<SurfaceControl> &layer) {
- // wait for previous transactions (such as setSize) to complete
- Transaction().apply(true);
- ANativeWindow_Buffer buffer = {};
- EXPECT_EQ(NO_ERROR, layer->getSurface()->lock(&buffer, nullptr));
- ASSERT_EQ(NO_ERROR, layer->getSurface()->unlockAndPost());
- // Request an empty transaction to get applied synchronously to ensure the buffer is
- // latched.
- Transaction().apply(true);
+ void postBuffer(const sp<SurfaceControl> &layer, int32_t w, int32_t h) {
+ int64_t usageFlags = BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE;
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(w, h, PIXEL_FORMAT_RGBA_8888, 1, usageFlags, "test");
+ Transaction().setBuffer(layer, buffer).apply(true);
usleep(mBufferPostDelay);
}
@@ -280,7 +361,6 @@
TEST_F(InputSurfacesTest, can_receive_input) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
surface->showAt(100, 100);
- surface->assertFocusChange(true);
injectTap(101, 101);
@@ -296,12 +376,9 @@
TEST_F(InputSurfacesTest, input_respects_positioning) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
surface->showAt(100, 100);
- surface->assertFocusChange(true);
std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100);
surface2->showAt(200, 200);
- surface->assertFocusChange(false);
- surface2->assertFocusChange(true);
injectTap(201, 201);
surface2->expectTap(1, 1);
@@ -328,16 +405,11 @@
std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100);
surface->showAt(10, 10);
- surface->assertFocusChange(true);
surface2->showAt(10, 10);
- surface->assertFocusChange(false);
- surface2->assertFocusChange(true);
surface->doTransaction([](auto &t, auto &sc) {
t.setLayer(sc, LAYER_BASE + 1);
});
- surface2->assertFocusChange(false);
- surface->assertFocusChange(true);
injectTap(11, 11);
surface->expectTap(1, 1);
@@ -345,8 +417,6 @@
surface2->doTransaction([](auto &t, auto &sc) {
t.setLayer(sc, LAYER_BASE + 1);
});
- surface2->assertFocusChange(true);
- surface->assertFocusChange(false);
injectTap(11, 11);
surface2->expectTap(1, 1);
@@ -354,8 +424,6 @@
surface2->doTransaction([](auto &t, auto &sc) {
t.hide(sc);
});
- surface2->assertFocusChange(false);
- surface->assertFocusChange(true);
injectTap(11, 11);
surface->expectTap(1, 1);
@@ -368,12 +436,9 @@
std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
bgSurface->showAt(100, 100);
- bgSurface->assertFocusChange(true);
fgSurface->mInputInfo.surfaceInset = 5;
fgSurface->showAt(100, 100);
- fgSurface->assertFocusChange(true);
- bgSurface->assertFocusChange(false);
injectTap(106, 106);
fgSurface->expectTap(1, 1);
@@ -387,16 +452,13 @@
std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
std::unique_ptr<InputSurface> childSurface = makeSurface(100, 100);
parentSurface->showAt(100, 100);
- parentSurface->assertFocusChange(true);
childSurface->mInputInfo.surfaceInset = 10;
childSurface->showAt(100, 100);
- childSurface->assertFocusChange(true);
- parentSurface->assertFocusChange(false);
childSurface->doTransaction([&](auto &t, auto &sc) {
t.setPosition(sc, -5, -5);
- t.reparent(sc, parentSurface->mSurfaceControl->getHandle());
+ t.reparent(sc, parentSurface->mSurfaceControl);
});
injectTap(106, 106);
@@ -411,12 +473,9 @@
std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
bgSurface->showAt(100, 100);
- bgSurface->assertFocusChange(true);
fgSurface->mInputInfo.surfaceInset = 5;
fgSurface->showAt(100, 100);
- bgSurface->assertFocusChange(false);
- fgSurface->assertFocusChange(true);
fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 4.0); });
@@ -433,7 +492,6 @@
// In case we pass the very big inset without any checking.
fgSurface->mInputInfo.surfaceInset = INT32_MAX;
fgSurface->showAt(100, 100);
- fgSurface->assertFocusChange(true);
fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); });
@@ -450,7 +508,6 @@
t.setTransparentRegionHint(sc, transparentRegion);
});
surface->showAt(100, 100);
- surface->assertFocusChange(true);
injectTap(101, 101);
surface->expectTap(1, 1);
}
@@ -461,34 +518,28 @@
// Original bug ref: b/120839715
TEST_F(InputSurfacesTest, input_ignores_buffer_layer_buffer) {
std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
- std::unique_ptr<InputSurface> bufferSurface =
- InputSurface::makeBufferInputSurface(mComposerClient, 100, 100);
+ std::unique_ptr<BlastInputSurface> bufferSurface =
+ BlastInputSurface::makeBlastInputSurface(mComposerClient, 100, 100);
bgSurface->showAt(10, 10);
- bgSurface->assertFocusChange(true);
bufferSurface->showAt(10, 10);
- bgSurface->assertFocusChange(false);
- bufferSurface->assertFocusChange(true);
injectTap(11, 11);
bufferSurface->expectTap(1, 1);
- postBuffer(bufferSurface->mSurfaceControl);
+ postBuffer(bufferSurface->mSurfaceControl, 100, 100);
injectTap(11, 11);
bufferSurface->expectTap(1, 1);
}
TEST_F(InputSurfacesTest, input_ignores_buffer_layer_alpha) {
std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
- std::unique_ptr<InputSurface> bufferSurface =
- InputSurface::makeBufferInputSurface(mComposerClient, 100, 100);
- postBuffer(bufferSurface->mSurfaceControl);
+ std::unique_ptr<BlastInputSurface> bufferSurface =
+ BlastInputSurface::makeBlastInputSurface(mComposerClient, 100, 100);
+ postBuffer(bufferSurface->mSurfaceControl, 100, 100);
bgSurface->showAt(10, 10);
- bgSurface->assertFocusChange(true);
bufferSurface->showAt(10, 10);
- bufferSurface->assertFocusChange(true);
- bgSurface->assertFocusChange(false);
injectTap(11, 11);
bufferSurface->expectTap(1, 1);
@@ -504,10 +555,7 @@
std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
bgSurface->showAt(10, 10);
- bgSurface->assertFocusChange(true);
fgSurface->showAt(10, 10);
- bgSurface->assertFocusChange(false);
- fgSurface->assertFocusChange(true);
injectTap(11, 11);
fgSurface->expectTap(1, 1);
@@ -524,17 +572,12 @@
InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);
bgSurface->showAt(10, 10);
- bgSurface->assertFocusChange(true);
containerSurface->showAt(10, 10);
- bgSurface->assertFocusChange(false);
- containerSurface->assertFocusChange(true);
injectTap(11, 11);
containerSurface->expectTap(1, 1);
containerSurface->doTransaction([](auto &t, auto &sc) { t.hide(sc); });
- containerSurface->assertFocusChange(false);
- bgSurface->assertFocusChange(true);
injectTap(11, 11);
bgSurface->expectTap(1, 1);
@@ -543,7 +586,6 @@
TEST_F(InputSurfacesTest, input_respects_outscreen) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
surface->showAt(-1, -1);
- surface->assertFocusChange(true);
injectTap(0, 0);
surface->expectTap(1, 1);
@@ -555,11 +597,183 @@
InputSurface::makeCursorInputSurface(mComposerClient, 10, 10);
surface->showAt(10, 10);
- surface->assertFocusChange(true);
cursorSurface->showAt(10, 10);
injectTap(11, 11);
surface->expectTap(1, 1);
}
+
+TEST_F(InputSurfacesTest, can_be_focused) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->showAt(100, 100);
+ surface->requestFocus();
+
+ surface->assertFocusChange(true);
}
+
+TEST_F(InputSurfacesTest, rotate_surface) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->showAt(10, 10);
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setMatrix(sc, 0, 1, -1, 0); // 90 degrees
+ });
+ injectTap(8, 11);
+ surface->expectTap(1, 2);
+
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setMatrix(sc, -1, 0, 0, -1); // 180 degrees
+ });
+ injectTap(9, 8);
+ surface->expectTap(1, 2);
+
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setMatrix(sc, 0, -1, 1, 0); // 270 degrees
+ });
+ injectTap(12, 9);
+ surface->expectTap(1, 2);
}
+
+TEST_F(InputSurfacesTest, rotate_surface_with_scale) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->showAt(10, 10);
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setMatrix(sc, 0, 2, -4, 0); // 90 degrees
+ });
+ injectTap(2, 12);
+ surface->expectTap(1, 2);
+
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setMatrix(sc, -2, 0, 0, -4); // 180 degrees
+ });
+ injectTap(8, 2);
+ surface->expectTap(1, 2);
+
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setMatrix(sc, 0, -2, 4, 0); // 270 degrees
+ });
+ injectTap(18, 8);
+ surface->expectTap(1, 2);
+}
+
+TEST_F(InputSurfacesTest, rotate_surface_with_scale_and_insets) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->mInputInfo.surfaceInset = 5;
+ surface->showAt(100, 100);
+
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setMatrix(sc, 0, 2, -4, 0); // 90 degrees
+ });
+ injectTap(40, 120);
+ surface->expectTap(5, 10);
+
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setMatrix(sc, -2, 0, 0, -4); // 180 degrees
+ });
+ injectTap(80, 40);
+ surface->expectTap(5, 10);
+
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setMatrix(sc, 0, -2, 4, 0); // 270 degrees
+ });
+ injectTap(160, 80);
+ surface->expectTap(5, 10);
+}
+
+TEST_F(InputSurfacesTest, touch_flag_obscured) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->showAt(100, 100);
+
+ // Add non touchable window to fully cover touchable window. Window behind gets touch, but
+ // with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED
+ std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
+ nonTouchableSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
+ nonTouchableSurface->mInputInfo.ownerUid = 22222;
+ // Overriding occlusion mode otherwise the touch would be discarded at InputDispatcher by
+ // the default obscured/untrusted touch filter introduced in S.
+ nonTouchableSurface->mInputInfo.touchOcclusionMode = TouchOcclusionMode::ALLOW;
+ nonTouchableSurface->showAt(100, 100);
+
+ injectTap(190, 199);
+ surface->expectTapWithFlag(90, 99, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED);
+}
+
+TEST_F(InputSurfacesTest, touch_flag_partially_obscured_with_crop) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->showAt(100, 100);
+
+ // Add non touchable window to cover touchable window, but parent is cropped to not cover area
+ // that will be tapped. Window behind gets touch, but with flag
+ // AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED
+ std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
+ nonTouchableSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
+ parentSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
+ nonTouchableSurface->mInputInfo.ownerUid = 22222;
+ parentSurface->mInputInfo.ownerUid = 22222;
+ nonTouchableSurface->showAt(0, 0);
+ parentSurface->showAt(100, 100);
+
+ nonTouchableSurface->doTransaction([&](auto &t, auto &sc) {
+ t.setCrop(parentSurface->mSurfaceControl, Rect(0, 0, 50, 50));
+ t.reparent(sc, parentSurface->mSurfaceControl);
+ });
+
+ injectTap(190, 199);
+ surface->expectTapWithFlag(90, 99, AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED);
+}
+
+TEST_F(InputSurfacesTest, touch_not_obscured_with_crop) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->showAt(100, 100);
+
+ // Add non touchable window to cover touchable window, but parent is cropped to avoid covering
+ // the touchable window. Window behind gets touch with no obscured flags.
+ std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
+ nonTouchableSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
+ parentSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
+ nonTouchableSurface->mInputInfo.ownerUid = 22222;
+ parentSurface->mInputInfo.ownerUid = 22222;
+ nonTouchableSurface->showAt(0, 0);
+ parentSurface->showAt(50, 50);
+
+ nonTouchableSurface->doTransaction([&](auto &t, auto &sc) {
+ t.setCrop(parentSurface->mSurfaceControl, Rect(0, 0, 50, 50));
+ t.reparent(sc, parentSurface->mSurfaceControl);
+ });
+
+ injectTap(101, 110);
+ surface->expectTap(1, 10);
+}
+
+TEST_F(InputSurfacesTest, touch_not_obscured_with_zero_sized_bql) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+
+ std::unique_ptr<InputSurface> bufferSurface =
+ InputSurface::makeBufferInputSurface(mComposerClient, 0, 0);
+ bufferSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
+ bufferSurface->mInputInfo.ownerUid = 22222;
+
+ surface->showAt(10, 10);
+ bufferSurface->showAt(50, 50, Rect::EMPTY_RECT);
+
+ injectTap(11, 11);
+ surface->expectTap(1, 1);
+}
+
+TEST_F(InputSurfacesTest, touch_not_obscured_with_zero_sized_blast) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+
+ std::unique_ptr<BlastInputSurface> bufferSurface =
+ BlastInputSurface::makeBlastInputSurface(mComposerClient, 0, 0);
+ bufferSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
+ bufferSurface->mInputInfo.ownerUid = 22222;
+
+ surface->showAt(10, 10);
+ bufferSurface->showAt(50, 50, Rect::EMPTY_RECT);
+
+ injectTap(11, 11);
+ surface->expectTap(1, 1);
+}
+
+} // namespace android::test
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index 15bd32d..2af2fe1 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -70,6 +70,9 @@
const int QUEUE_BUFFER_INPUT_SCALING_MODE = 0;
const int QUEUE_BUFFER_INPUT_TRANSFORM = 0;
const sp<Fence> QUEUE_BUFFER_INPUT_FENCE = Fence::NO_FENCE;
+ const uint32_t QUEUE_BUFFER_INPUT_STICKY_TRANSFORM = 0;
+ const bool QUEUE_BUFFER_INPUT_GET_TIMESTAMPS = 0;
+ const int QUEUE_BUFFER_INPUT_SLOT = -1;
// Enums to control which IGraphicBufferProducer backend to test.
enum IGraphicBufferProducerTestCode {
@@ -156,6 +159,9 @@
scalingMode = QUEUE_BUFFER_INPUT_SCALING_MODE;
transform = QUEUE_BUFFER_INPUT_TRANSFORM;
fence = QUEUE_BUFFER_INPUT_FENCE;
+ stickyTransform = QUEUE_BUFFER_INPUT_STICKY_TRANSFORM;
+ getTimestamps = QUEUE_BUFFER_INPUT_GET_TIMESTAMPS;
+ slot = QUEUE_BUFFER_INPUT_SLOT;
}
IGraphicBufferProducer::QueueBufferInput build() {
@@ -166,7 +172,10 @@
crop,
scalingMode,
transform,
- fence);
+ fence,
+ stickyTransform,
+ getTimestamps,
+ slot);
}
QueueBufferInputBuilder& setTimestamp(int64_t timestamp) {
@@ -204,6 +213,21 @@
return *this;
}
+ QueueBufferInputBuilder& setStickyTransform(uint32_t stickyTransform) {
+ this->stickyTransform = stickyTransform;
+ return *this;
+ }
+
+ QueueBufferInputBuilder& setGetTimestamps(bool getTimestamps) {
+ this->getTimestamps = getTimestamps;
+ return *this;
+ }
+
+ QueueBufferInputBuilder& setSlot(int slot) {
+ this->slot = slot;
+ return *this;
+ }
+
private:
int64_t timestamp;
bool isAutoTimestamp;
@@ -212,17 +236,17 @@
int scalingMode;
uint32_t transform;
sp<Fence> fence;
+ uint32_t stickyTransform;
+ bool getTimestamps;
+ int slot;
}; // struct QueueBufferInputBuilder
- // To easily store dequeueBuffer results into containers
- struct DequeueBufferResult {
- int slot;
- sp<Fence> fence;
- };
-
- status_t dequeueBuffer(uint32_t w, uint32_t h, uint32_t format, uint32_t usage, DequeueBufferResult* result) {
- return mProducer->dequeueBuffer(&result->slot, &result->fence, w, h, format, usage,
- nullptr, nullptr);
+ status_t dequeueBuffer(uint32_t w, uint32_t h, uint32_t format, uint32_t usage,
+ IGraphicBufferProducer::DequeueBufferOutput* result) {
+ result->result =
+ mProducer->dequeueBuffer(&result->slot, &result->fence, w, h, format, usage,
+ &result->bufferAge, nullptr);
+ return result->result;
}
void setupDequeueRequestBuffer(int *slot, sp<Fence> *fence,
@@ -336,6 +360,27 @@
EXPECT_OK(mProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &value));
EXPECT_EQ(DEFAULT_CONSUMER_USAGE_BITS, value);
+ { // Test the batched version
+ std::vector<int32_t> inputs = {
+ NATIVE_WINDOW_WIDTH,
+ NATIVE_WINDOW_HEIGHT,
+ NATIVE_WINDOW_FORMAT,
+ NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND,
+ NATIVE_WINDOW_CONSUMER_USAGE_BITS };
+ using QueryOutput = IGraphicBufferProducer::QueryOutput;
+ std::vector<QueryOutput> outputs;
+ EXPECT_OK(mProducer->query(inputs, &outputs));
+ EXPECT_EQ(DEFAULT_WIDTH, static_cast<uint32_t>(outputs[0].value));
+ EXPECT_EQ(DEFAULT_HEIGHT, static_cast<uint32_t>(outputs[1].value));
+ EXPECT_EQ(DEFAULT_FORMAT, outputs[2].value);
+ EXPECT_LE(0, outputs[3].value);
+ EXPECT_FALSE(outputs[4].value);
+ EXPECT_EQ(DEFAULT_CONSUMER_USAGE_BITS, outputs[5].value);
+ for (const QueryOutput& output : outputs) {
+ EXPECT_OK(output.result);
+ }
+ }
}
TEST_P(IGraphicBufferProducerTest, Query_ReturnsError) {
@@ -358,6 +403,24 @@
EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_TRANSFORM_HINT, &value));
// TODO: Consider documented the above enums as unsupported or make a new enum for IGBP
+ { // Test the batched version
+ std::vector<int32_t> inputs = {
+ -1,
+ static_cast<int32_t>(0xDEADBEEF),
+ NATIVE_WINDOW_QUERY_LAST_OFF_BY_ONE,
+ NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER,
+ NATIVE_WINDOW_CONCRETE_TYPE,
+ NATIVE_WINDOW_DEFAULT_WIDTH,
+ NATIVE_WINDOW_DEFAULT_HEIGHT,
+ NATIVE_WINDOW_TRANSFORM_HINT};
+ using QueryOutput = IGraphicBufferProducer::QueryOutput;
+ std::vector<QueryOutput> outputs;
+ EXPECT_OK(mProducer->query(inputs, &outputs));
+ for (const QueryOutput& output : outputs) {
+ EXPECT_EQ(BAD_VALUE, output.result);
+ }
+ }
+
// Value was NULL
EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_FORMAT, /*value*/nullptr));
@@ -416,29 +479,113 @@
// Buffer was not in the dequeued state
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output));
+
+ { // Test batched methods
+ constexpr size_t BATCH_SIZE = 4;
+
+ ASSERT_OK(mProducer->setMaxDequeuedBufferCount(BATCH_SIZE));
+ // Dequeue
+ using DequeueBufferInput = IGraphicBufferProducer::DequeueBufferInput;
+ using DequeueBufferOutput = IGraphicBufferProducer::DequeueBufferOutput;
+ DequeueBufferInput dequeueInput;
+ dequeueInput.width = DEFAULT_WIDTH;
+ dequeueInput.height = DEFAULT_HEIGHT;
+ dequeueInput.format = DEFAULT_FORMAT;
+ dequeueInput.usage = TEST_PRODUCER_USAGE_BITS;
+ dequeueInput.getTimestamps = false;
+ std::vector<DequeueBufferInput> dequeueInputs(BATCH_SIZE, dequeueInput);
+ std::vector<DequeueBufferOutput> dequeueOutputs;
+ EXPECT_OK(mProducer->dequeueBuffers(dequeueInputs, &dequeueOutputs));
+ ASSERT_EQ(dequeueInputs.size(), dequeueOutputs.size());
+
+ // Request
+ std::vector<int32_t> requestInputs;
+ requestInputs.reserve(BATCH_SIZE);
+ for (const DequeueBufferOutput& dequeueOutput : dequeueOutputs) {
+ ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+ dequeueOutput.result);
+ requestInputs.emplace_back(dequeueOutput.slot);
+ }
+ using RequestBufferOutput = IGraphicBufferProducer::RequestBufferOutput;
+ std::vector<RequestBufferOutput> requestOutputs;
+ EXPECT_OK(mProducer->requestBuffers(requestInputs, &requestOutputs));
+ ASSERT_EQ(requestInputs.size(), requestOutputs.size());
+ for (const RequestBufferOutput& requestOutput : requestOutputs) {
+ EXPECT_OK(requestOutput.result);
+ }
+
+ // Queue
+ using QueueBufferInput = IGraphicBufferProducer::QueueBufferInput;
+ using QueueBufferOutput = IGraphicBufferProducer::QueueBufferOutput;
+ std::vector<QueueBufferInput> queueInputs;
+ queueInputs.reserve(BATCH_SIZE);
+ for (const DequeueBufferOutput& dequeueOutput : dequeueOutputs) {
+ queueInputs.emplace_back(CreateBufferInput()).slot =
+ dequeueOutput.slot;
+ }
+ std::vector<QueueBufferOutput> queueOutputs;
+ EXPECT_OK(mProducer->queueBuffers(queueInputs, &queueOutputs));
+ ASSERT_EQ(queueInputs.size(), queueOutputs.size());
+ for (const QueueBufferOutput& queueOutput : queueOutputs) {
+ EXPECT_OK(queueOutput.result);
+ }
+
+ // Re-queue
+ EXPECT_OK(mProducer->queueBuffers(queueInputs, &queueOutputs));
+ ASSERT_EQ(queueInputs.size(), queueOutputs.size());
+ for (const QueueBufferOutput& queueOutput : queueOutputs) {
+ EXPECT_EQ(BAD_VALUE, queueOutput.result);
+ }
+ }
}
TEST_P(IGraphicBufferProducerTest, Queue_ReturnsError) {
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+ using QueueBufferInput = IGraphicBufferProducer::QueueBufferInput;
+ using QueueBufferOutput = IGraphicBufferProducer::QueueBufferOutput;
// Invalid slot number
{
// A generic "valid" input
- IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
- IGraphicBufferProducer::QueueBufferOutput output;
+ QueueBufferInput input = CreateBufferInput();
+ QueueBufferOutput output;
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/-1, input, &output));
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/0xDEADBEEF, input, &output));
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(BufferQueue::NUM_BUFFER_SLOTS,
input, &output));
+
+ { // Test with the batched version
+ constexpr size_t BATCH_SIZE = 16;
+ input.slot = -1;
+ std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+ std::vector<QueueBufferOutput> outputs;
+ EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+ ASSERT_EQ(inputs.size(), outputs.size());
+ for (const QueueBufferOutput& output : outputs) {
+ EXPECT_EQ(BAD_VALUE, output.result);
+ }
+ }
}
// Slot was not in the dequeued state (all slots start out in Free state)
{
- IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
- IGraphicBufferProducer::QueueBufferOutput output;
+ QueueBufferInput input = CreateBufferInput();
+ QueueBufferOutput output;
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/0, input, &output));
+
+ { // Test with the batched version
+ constexpr size_t BATCH_SIZE = 16;
+ input.slot = 0;
+ std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+ std::vector<QueueBufferOutput> outputs;
+ EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+ ASSERT_EQ(inputs.size(), outputs.size());
+ for (const QueueBufferOutput& output : outputs) {
+ EXPECT_EQ(BAD_VALUE, output.result);
+ }
+ }
}
// Put the slot into the "dequeued" state for the rest of the test
@@ -453,10 +600,22 @@
// Slot was enqueued without requesting a buffer
{
- IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
- IGraphicBufferProducer::QueueBufferOutput output;
+ QueueBufferInput input = CreateBufferInput();
+ QueueBufferOutput output;
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output));
+
+ { // Test with the batched version
+ constexpr size_t BATCH_SIZE = 16;
+ input.slot = dequeuedSlot;
+ std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+ std::vector<QueueBufferOutput> outputs;
+ EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+ ASSERT_EQ(inputs.size(), outputs.size());
+ for (const QueueBufferOutput& output : outputs) {
+ EXPECT_EQ(BAD_VALUE, output.result);
+ }
+ }
}
// Request the buffer so that the rest of the tests don't fail on earlier checks.
@@ -467,11 +626,23 @@
{
sp<Fence> nullFence = nullptr;
- IGraphicBufferProducer::QueueBufferInput input =
+ QueueBufferInput input =
QueueBufferInputBuilder().setFence(nullFence).build();
- IGraphicBufferProducer::QueueBufferOutput output;
+ QueueBufferOutput output;
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output));
+
+ { // Test with the batched version
+ constexpr size_t BATCH_SIZE = 16;
+ input.slot = dequeuedSlot;
+ std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+ std::vector<QueueBufferOutput> outputs;
+ EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+ ASSERT_EQ(inputs.size(), outputs.size());
+ for (const QueueBufferOutput& output : outputs) {
+ EXPECT_EQ(BAD_VALUE, output.result);
+ }
+ }
}
// Scaling mode was unknown
@@ -482,9 +653,33 @@
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output));
+ { // Test with the batched version
+ constexpr size_t BATCH_SIZE = 16;
+ input.slot = dequeuedSlot;
+ std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+ std::vector<QueueBufferOutput> outputs;
+ EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+ ASSERT_EQ(inputs.size(), outputs.size());
+ for (const QueueBufferOutput& output : outputs) {
+ EXPECT_EQ(BAD_VALUE, output.result);
+ }
+ }
+
input = QueueBufferInputBuilder().setScalingMode(0xDEADBEEF).build();
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output));
+
+ { // Test with the batched version
+ constexpr size_t BATCH_SIZE = 16;
+ input.slot = dequeuedSlot;
+ std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+ std::vector<QueueBufferOutput> outputs;
+ EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+ ASSERT_EQ(inputs.size(), outputs.size());
+ for (const QueueBufferOutput& output : outputs) {
+ EXPECT_EQ(BAD_VALUE, output.result);
+ }
+ }
}
// Crop rect is out of bounds of the buffer dimensions
@@ -495,6 +690,18 @@
IGraphicBufferProducer::QueueBufferOutput output;
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output));
+
+ { // Test with the batched version
+ constexpr size_t BATCH_SIZE = 16;
+ input.slot = dequeuedSlot;
+ std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+ std::vector<QueueBufferOutput> outputs;
+ EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+ ASSERT_EQ(inputs.size(), outputs.size());
+ for (const QueueBufferOutput& output : outputs) {
+ EXPECT_EQ(BAD_VALUE, output.result);
+ }
+ }
}
// Abandon the buffer queue so that the last test fails
@@ -507,6 +714,18 @@
// TODO(b/73267953): Make BufferHub honor producer and consumer connection.
EXPECT_EQ(NO_INIT, mProducer->queueBuffer(dequeuedSlot, input, &output));
+
+ { // Test with the batched version
+ constexpr size_t BATCH_SIZE = 16;
+ input.slot = dequeuedSlot;
+ std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+ std::vector<QueueBufferOutput> outputs;
+ EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+ ASSERT_EQ(inputs.size(), outputs.size());
+ for (const QueueBufferOutput& output : outputs) {
+ EXPECT_EQ(NO_INIT, output.result);
+ }
+ }
}
}
@@ -525,6 +744,44 @@
// No return code, but at least test that it doesn't blow up...
// TODO: add a return code
mProducer->cancelBuffer(dequeuedSlot, dequeuedFence);
+
+ { // Test batched methods
+ constexpr size_t BATCH_SIZE = 4;
+ ASSERT_OK(mProducer->setMaxDequeuedBufferCount(BATCH_SIZE));
+
+ // Dequeue
+ using DequeueBufferInput = IGraphicBufferProducer::DequeueBufferInput;
+ using DequeueBufferOutput = IGraphicBufferProducer::DequeueBufferOutput;
+ DequeueBufferInput dequeueInput;
+ dequeueInput.width = DEFAULT_WIDTH;
+ dequeueInput.height = DEFAULT_HEIGHT;
+ dequeueInput.format = DEFAULT_FORMAT;
+ dequeueInput.usage = TEST_PRODUCER_USAGE_BITS;
+ dequeueInput.getTimestamps = false;
+ std::vector<DequeueBufferInput> dequeueInputs(BATCH_SIZE, dequeueInput);
+ std::vector<DequeueBufferOutput> dequeueOutputs;
+ EXPECT_OK(mProducer->dequeueBuffers(dequeueInputs, &dequeueOutputs));
+ ASSERT_EQ(dequeueInputs.size(), dequeueOutputs.size());
+
+ // Cancel
+ using CancelBufferInput = IGraphicBufferProducer::CancelBufferInput;
+ std::vector<CancelBufferInput> cancelInputs;
+ cancelInputs.reserve(BATCH_SIZE);
+ for (const DequeueBufferOutput& dequeueOutput : dequeueOutputs) {
+ ASSERT_EQ(OK,
+ ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+ dequeueOutput.result);
+ CancelBufferInput& cancelInput = cancelInputs.emplace_back();
+ cancelInput.slot = dequeueOutput.slot;
+ cancelInput.fence = dequeueOutput.fence;
+ }
+ std::vector<status_t> cancelOutputs;
+ EXPECT_OK(mProducer->cancelBuffers(cancelInputs, &cancelOutputs));
+ ASSERT_EQ(cancelInputs.size(), cancelOutputs.size());
+ for (status_t result : cancelOutputs) {
+ EXPECT_OK(result);
+ }
+ }
}
TEST_P(IGraphicBufferProducerTest, SetMaxDequeuedBufferCount_Succeeds) {
@@ -541,11 +798,11 @@
<< "bufferCount: " << minBuffers;
// Should now be able to dequeue up to minBuffers times
- DequeueBufferResult result;
+ IGraphicBufferProducer::DequeueBufferOutput result;
for (int i = 0; i < minBuffers; ++i) {
EXPECT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(dequeueBuffer(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS, &result)))
+ TEST_PRODUCER_USAGE_BITS, &result)))
<< "iteration: " << i << ", slot: " << result.slot;
}
@@ -558,7 +815,6 @@
ASSERT_OK(mProducer->requestBuffer(result.slot, &buffer));
ASSERT_OK(mProducer->queueBuffer(result.slot, input, &output));
-
// Should now be able to dequeue up to maxBuffers times
int dequeuedSlot = -1;
sp<Fence> dequeuedFence;
@@ -794,6 +1050,71 @@
EXPECT_OK(mProducer->attachBuffer(&slot, buffer));
EXPECT_OK(buffer->initCheck());
+
+ ASSERT_OK(mProducer->detachBuffer(slot));
+
+ { // Test batched methods
+ constexpr size_t BATCH_SIZE = 4;
+ ASSERT_OK(mProducer->setMaxDequeuedBufferCount(BATCH_SIZE));
+
+ // Dequeue
+ using DequeueBufferInput = IGraphicBufferProducer::DequeueBufferInput;
+ using DequeueBufferOutput = IGraphicBufferProducer::DequeueBufferOutput;
+ DequeueBufferInput dequeueInput;
+ dequeueInput.width = DEFAULT_WIDTH;
+ dequeueInput.height = DEFAULT_HEIGHT;
+ dequeueInput.format = DEFAULT_FORMAT;
+ dequeueInput.usage = TEST_PRODUCER_USAGE_BITS;
+ dequeueInput.getTimestamps = false;
+ std::vector<DequeueBufferInput> dequeueInputs(BATCH_SIZE, dequeueInput);
+ std::vector<DequeueBufferOutput> dequeueOutputs;
+ EXPECT_OK(mProducer->dequeueBuffers(dequeueInputs, &dequeueOutputs));
+ ASSERT_EQ(dequeueInputs.size(), dequeueOutputs.size());
+
+ // Request
+ std::vector<int32_t> requestInputs;
+ requestInputs.reserve(BATCH_SIZE);
+ for (const DequeueBufferOutput& dequeueOutput : dequeueOutputs) {
+ ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+ dequeueOutput.result);
+ requestInputs.emplace_back(dequeueOutput.slot);
+ }
+ using RequestBufferOutput = IGraphicBufferProducer::RequestBufferOutput;
+ std::vector<RequestBufferOutput> requestOutputs;
+ EXPECT_OK(mProducer->requestBuffers(requestInputs, &requestOutputs));
+ ASSERT_EQ(requestInputs.size(), requestOutputs.size());
+ for (const RequestBufferOutput& requestOutput : requestOutputs) {
+ EXPECT_OK(requestOutput.result);
+ }
+
+ // Detach
+ std::vector<int32_t> detachInputs;
+ detachInputs.reserve(BATCH_SIZE);
+ for (const DequeueBufferOutput& dequeueOutput : dequeueOutputs) {
+ detachInputs.emplace_back(dequeueOutput.slot);
+ }
+ std::vector<status_t> detachOutputs;
+ EXPECT_OK(mProducer->detachBuffers(detachInputs, &detachOutputs));
+ ASSERT_EQ(detachInputs.size(), detachOutputs.size());
+ for (status_t result : detachOutputs) {
+ EXPECT_OK(result);
+ }
+
+ // Attach
+ using AttachBufferOutput = IGraphicBufferProducer::AttachBufferOutput;
+ std::vector<sp<GraphicBuffer>> attachInputs;
+ attachInputs.reserve(BATCH_SIZE);
+ for (const RequestBufferOutput& requestOutput : requestOutputs) {
+ attachInputs.emplace_back(requestOutput.buffer);
+ }
+ std::vector<AttachBufferOutput> attachOutputs;
+ EXPECT_OK(mProducer->attachBuffers(attachInputs, &attachOutputs));
+ ASSERT_EQ(attachInputs.size(), attachOutputs.size());
+ for (const AttachBufferOutput& attachOutput : attachOutputs) {
+ EXPECT_OK(attachOutput.result);
+ EXPECT_NE(-1, attachOutput.slot);
+ }
+ }
}
#if USE_BUFFER_HUB_AS_BUFFER_QUEUE
diff --git a/libs/gui/tests/SamplingDemo.cpp b/libs/gui/tests/SamplingDemo.cpp
index 5c1bebb..0cd150d 100644
--- a/libs/gui/tests/SamplingDemo.cpp
+++ b/libs/gui/tests/SamplingDemo.cpp
@@ -46,8 +46,7 @@
SurfaceComposerClient::Transaction{}
.setLayer(mButton, 0x7fffffff)
- .setCrop_legacy(mButton,
- {0, 0, width - 2 * BUTTON_PADDING, height - 2 * BUTTON_PADDING})
+ .setCrop(mButton, {0, 0, width - 2 * BUTTON_PADDING, height - 2 * BUTTON_PADDING})
.setPosition(mButton, samplingArea.left + BUTTON_PADDING,
samplingArea.top + BUTTON_PADDING)
.setColor(mButton, half3{1, 1, 1})
@@ -59,9 +58,8 @@
SurfaceComposerClient::Transaction{}
.setLayer(mButtonBlend, 0x7ffffffe)
- .setCrop_legacy(mButtonBlend,
- {0, 0, width - 2 * SAMPLE_AREA_PADDING,
- height - 2 * SAMPLE_AREA_PADDING})
+ .setCrop(mButtonBlend,
+ {0, 0, width - 2 * SAMPLE_AREA_PADDING, height - 2 * SAMPLE_AREA_PADDING})
.setPosition(mButtonBlend, samplingArea.left + SAMPLE_AREA_PADDING,
samplingArea.top + SAMPLE_AREA_PADDING)
.setColor(mButtonBlend, half3{1, 1, 1})
@@ -77,7 +75,7 @@
SurfaceComposerClient::Transaction{}
.setLayer(mSamplingArea, 0x7ffffffd)
- .setCrop_legacy(mSamplingArea, {0, 0, 100, 32})
+ .setCrop(mSamplingArea, {0, 0, 100, 32})
.setPosition(mSamplingArea, 490, 1606)
.setColor(mSamplingArea, half3{0, 1, 0})
.setAlpha(mSamplingArea, 0.1)
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 592913c..ea8c295 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -28,9 +28,11 @@
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
+#include <gui/SyncScreenCaptureListener.h>
#include <inttypes.h>
#include <private/gui/ComposerService.h>
#include <ui/BufferQueueDefs.h>
+#include <ui/DisplayMode.h>
#include <ui/Rect.h>
#include <utils/String8.h>
@@ -100,14 +102,13 @@
// test flakiness.
mSurfaceControl = mComposerClient->createSurface(
String8("Test Surface"), 32, 32, PIXEL_FORMAT_RGBA_8888, 0);
+ SurfaceComposerClient::Transaction().apply(true);
ASSERT_TRUE(mSurfaceControl != nullptr);
ASSERT_TRUE(mSurfaceControl->isValid());
Transaction t;
- ASSERT_EQ(NO_ERROR, t.setLayer(mSurfaceControl, 0x7fffffff)
- .show(mSurfaceControl)
- .apply());
+ ASSERT_EQ(NO_ERROR, t.setLayer(mSurfaceControl, 0x7fffffff).show(mSurfaceControl).apply());
mSurface = mSurfaceControl->getSurface();
ASSERT_TRUE(mSurface != nullptr);
@@ -197,6 +198,20 @@
ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU));
}
+ static status_t captureDisplay(DisplayCaptureArgs& captureArgs,
+ ScreenCaptureResults& captureResults) {
+ const auto sf = ComposerService::getComposerService();
+ SurfaceComposerClient::Transaction().apply(true);
+
+ const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
+ status_t status = sf->captureDisplay(captureArgs, captureListener);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ captureResults = captureListener->waitForResults();
+ return captureResults.result;
+ }
+
sp<Surface> mSurface;
sp<SurfaceComposerClient> mComposerClient;
sp<SurfaceControl> mSurfaceControl;
@@ -244,11 +259,13 @@
const sp<IBinder> display = sf->getInternalDisplayToken();
ASSERT_FALSE(display == nullptr);
- sp<GraphicBuffer> outBuffer;
- bool ignored;
- ASSERT_EQ(NO_ERROR,
- sf->captureScreen(display, &outBuffer, ignored, ui::Dataspace::V0_SRGB,
- ui::PixelFormat::RGBA_8888, Rect(), 64, 64, false));
+ DisplayCaptureArgs captureArgs;
+ captureArgs.displayToken = display;
+ captureArgs.width = 64;
+ captureArgs.height = 64;
+
+ ScreenCaptureResults captureResults;
+ ASSERT_EQ(NO_ERROR, captureDisplay(captureArgs, captureResults));
ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(),
NATIVE_WINDOW_API_CPU));
@@ -278,9 +295,7 @@
&buf));
ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf, -1));
}
- ASSERT_EQ(NO_ERROR,
- sf->captureScreen(display, &outBuffer, ignored, ui::Dataspace::V0_SRGB,
- ui::PixelFormat::RGBA_8888, Rect(), 64, 64, false));
+ ASSERT_EQ(NO_ERROR, captureDisplay(captureArgs, captureResults));
}
TEST_F(SurfaceTest, ConcreteTypeIsSurface) {
@@ -662,8 +677,7 @@
NewFrameEventsEntry mNewFrameEntryOverride = { 0, 0, 0, nullptr };
};
-
-class FakeSurfaceComposer : public ISurfaceComposer{
+class FakeSurfaceComposer : public ISurfaceComposer {
public:
~FakeSurfaceComposer() override {}
@@ -673,7 +687,7 @@
sp<ISurfaceComposerClient> createConnection() override { return nullptr; }
sp<IDisplayEventConnection> createDisplayEventConnection(
- ISurfaceComposer::VsyncSource, ISurfaceComposer::ConfigChanged) override {
+ ISurfaceComposer::VsyncSource, ISurfaceComposer::EventRegistrationFlags) override {
return nullptr;
}
sp<IBinder> createDisplay(const String8& /*displayName*/,
@@ -681,13 +695,17 @@
void destroyDisplay(const sp<IBinder>& /*display */) override {}
std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const override { return {}; }
sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId) const override { return nullptr; }
- void setTransactionState(const Vector<ComposerState>& /*state*/,
- const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
- const sp<IBinder>& /*applyToken*/,
- const InputWindowCommands& /*inputWindowCommands*/,
- int64_t /*desiredPresentTime*/, const client_cache_t& /*cachedBuffer*/,
- bool /*hasListenerCallbacks*/,
- const std::vector<ListenerCallbacks>& /*listenerCallbacks*/) override {
+ status_t setTransactionState(const FrameTimelineInfo& /*frameTimelineInfo*/,
+ const Vector<ComposerState>& /*state*/,
+ const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
+ const sp<IBinder>& /*applyToken*/,
+ const InputWindowCommands& /*inputWindowCommands*/,
+ int64_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
+ const client_cache_t& /*cachedBuffer*/,
+ bool /*hasListenerCallbacks*/,
+ const std::vector<ListenerCallbacks>& /*listenerCallbacks*/,
+ uint64_t /*transactionId*/) override {
+ return NO_ERROR;
}
void bootFinished() override {}
@@ -716,10 +734,11 @@
}
void setPowerMode(const sp<IBinder>& /*display*/, int /*mode*/) override {}
- status_t getDisplayInfo(const sp<IBinder>& /*display*/, DisplayInfo*) override {
+ status_t getStaticDisplayInfo(const sp<IBinder>& /*display*/, ui::StaticDisplayInfo*) override {
return NO_ERROR;
}
- status_t getDisplayConfigs(const sp<IBinder>& /*display*/, Vector<DisplayConfig>*) override {
+ status_t getDynamicDisplayInfo(const sp<IBinder>& /*display*/,
+ ui::DynamicDisplayInfo*) override {
return NO_ERROR;
}
status_t getDisplayState(const sp<IBinder>& /*display*/, ui::DisplayState*) override {
@@ -727,58 +746,37 @@
}
status_t getDisplayStats(const sp<IBinder>& /*display*/,
DisplayStatInfo* /*stats*/) override { return NO_ERROR; }
- int getActiveConfig(const sp<IBinder>& /*display*/) override { return 0; }
- status_t getDisplayColorModes(const sp<IBinder>& /*display*/,
- Vector<ColorMode>* /*outColorModes*/) override {
- return NO_ERROR;
- }
status_t getDisplayNativePrimaries(const sp<IBinder>& /*display*/,
ui::DisplayPrimaries& /*primaries*/) override {
return NO_ERROR;
}
- ColorMode getActiveColorMode(const sp<IBinder>& /*display*/)
- override {
- return ColorMode::NATIVE;
- }
status_t setActiveColorMode(const sp<IBinder>& /*display*/,
ColorMode /*colorMode*/) override { return NO_ERROR; }
- status_t captureScreen(const sp<IBinder>& /*display*/, sp<GraphicBuffer>* /*outBuffer*/,
- bool& /*outCapturedSecureLayers*/, ui::Dataspace /*reqDataspace*/,
- ui::PixelFormat /*reqPixelFormat*/, const Rect& /*sourceCrop*/,
- uint32_t /*reqWidth*/, uint32_t /*reqHeight*/,
- bool /*useIdentityTransform*/, ui::Rotation,
- bool /*captureSecureLayers*/) override {
- return NO_ERROR;
- }
- status_t getAutoLowLatencyModeSupport(const sp<IBinder>& /*display*/,
- bool* /*outSupport*/) const override {
+ status_t captureDisplay(const DisplayCaptureArgs& /* captureArgs */,
+ const sp<IScreenCaptureListener>& /* captureListener */) override {
return NO_ERROR;
}
void setAutoLowLatencyMode(const sp<IBinder>& /*display*/, bool /*on*/) override {}
- status_t getGameContentTypeSupport(const sp<IBinder>& /*display*/,
- bool* /*outSupport*/) const override {
- return NO_ERROR;
- }
void setGameContentType(const sp<IBinder>& /*display*/, bool /*on*/) override {}
- status_t captureScreen(uint64_t /*displayOrLayerStack*/, ui::Dataspace* /*outDataspace*/,
- sp<GraphicBuffer>* /*outBuffer*/) override {
+ status_t captureDisplay(uint64_t /*displayOrLayerStack*/,
+ const sp<IScreenCaptureListener>& /* captureListener */) override {
return NO_ERROR;
}
virtual status_t captureLayers(
- const sp<IBinder>& /*parentHandle*/, sp<GraphicBuffer>* /*outBuffer*/,
- ui::Dataspace /*reqDataspace*/, ui::PixelFormat /*reqPixelFormat*/,
- const Rect& /*sourceCrop*/,
- const std::unordered_set<sp<IBinder>,
- ISurfaceComposer::SpHash<IBinder>>& /*excludeHandles*/,
- float /*frameScale*/, bool /*childrenOnly*/) override {
+ const LayerCaptureArgs& /* captureArgs */,
+ const sp<IScreenCaptureListener>& /* captureListener */) override {
return NO_ERROR;
}
status_t clearAnimationFrameStats() override { return NO_ERROR; }
status_t getAnimationFrameStats(FrameStats* /*outStats*/) const override {
return NO_ERROR;
}
- status_t getHdrCapabilities(const sp<IBinder>& /*display*/,
- HdrCapabilities* /*outCapabilities*/) const override {
+ status_t overrideHdrTypes(const sp<IBinder>& /*display*/,
+ const std::vector<ui::Hdr>& /*hdrTypes*/) override {
+ return NO_ERROR;
+ }
+ status_t onPullAtom(const int32_t /*atomId*/, std::string* /*outData*/,
+ bool* /*success*/) override {
return NO_ERROR;
}
status_t enableVSyncInjections(bool /*enable*/) override {
@@ -820,7 +818,17 @@
return NO_ERROR;
}
status_t setDisplayBrightness(const sp<IBinder>& /*displayToken*/,
- float /*brightness*/) override {
+ const gui::DisplayBrightness& /*brightness*/) override {
+ return NO_ERROR;
+ }
+
+ status_t addHdrLayerInfoListener(const sp<IBinder>&,
+ const sp<gui::IHdrLayerInfoListener>&) override {
+ return NO_ERROR;
+ }
+
+ status_t removeHdrLayerInfoListener(const sp<IBinder>&,
+ const sp<gui::IHdrLayerInfoListener>&) override {
return NO_ERROR;
}
@@ -833,23 +841,29 @@
const sp<IRegionSamplingListener>& /*listener*/) override {
return NO_ERROR;
}
- status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& /*displayToken*/,
- int32_t /*defaultConfig*/,
- float /*primaryRefreshRateMin*/,
- float /*primaryRefreshRateMax*/,
- float /*appRequestRefreshRateMin*/,
- float /*appRequestRefreshRateMax*/) {
+ status_t addFpsListener(int32_t /*taskId*/, const sp<gui::IFpsListener>& /*listener*/) {
return NO_ERROR;
}
- status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& /*displayToken*/,
- int32_t* /*outDefaultConfig*/,
- float* /*outPrimaryRefreshRateMin*/,
- float* /*outPrimaryRefreshRateMax*/,
- float* /*outAppRequestRefreshRateMin*/,
- float* /*outAppRequestRefreshRateMax*/) override {
+ status_t removeFpsListener(const sp<gui::IFpsListener>& /*listener*/) { return NO_ERROR; }
+ status_t setDesiredDisplayModeSpecs(const sp<IBinder>& /*displayToken*/,
+ ui::DisplayModeId /*defaultMode*/,
+ bool /*allowGroupSwitching*/,
+ float /*primaryRefreshRateMin*/,
+ float /*primaryRefreshRateMax*/,
+ float /*appRequestRefreshRateMin*/,
+ float /*appRequestRefreshRateMax*/) {
+ return NO_ERROR;
+ }
+ status_t getDesiredDisplayModeSpecs(const sp<IBinder>& /*displayToken*/,
+ ui::DisplayModeId* /*outDefaultMode*/,
+ bool* /*outAllowGroupSwitching*/,
+ float* /*outPrimaryRefreshRateMin*/,
+ float* /*outPrimaryRefreshRateMax*/,
+ float* /*outAppRequestRefreshRateMin*/,
+ float* /*outAppRequestRefreshRateMax*/) override {
return NO_ERROR;
};
- status_t notifyPowerHint(int32_t /*hintId*/) override { return NO_ERROR; }
+ status_t notifyPowerBoost(int32_t /*boostId*/) override { return NO_ERROR; }
status_t setGlobalShadowSettings(const half4& /*ambientColor*/, const half4& /*spotColor*/,
float /*lightPosY*/, float /*lightPosZ*/,
@@ -858,11 +872,27 @@
}
status_t setFrameRate(const sp<IGraphicBufferProducer>& /*surface*/, float /*frameRate*/,
- int8_t /*compatibility*/) override {
+ int8_t /*compatibility*/, int8_t /*changeFrameRateStrategy*/) override {
return NO_ERROR;
}
- status_t acquireFrameRateFlexibilityToken(sp<IBinder>* /*outToken*/) { return NO_ERROR; }
+ status_t acquireFrameRateFlexibilityToken(sp<IBinder>* /*outToken*/) override {
+ return NO_ERROR;
+ }
+
+ status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& /*surface*/,
+ const FrameTimelineInfo& /*frameTimelineInfo*/) override {
+ return NO_ERROR;
+ }
+
+ status_t addTransactionTraceListener(
+ const sp<gui::ITransactionTraceListener>& /*listener*/) override {
+ return NO_ERROR;
+ }
+
+ int getGPUContextPriority() override { return 0; };
+
+ status_t getExtraBufferCount(int* /*extraBuffers*/) const override { return NO_ERROR; }
protected:
IBinder* onAsBinder() override { return nullptr; }
@@ -1999,4 +2029,86 @@
EXPECT_EQ(BufferQueueDefs::NUM_BUFFER_SLOTS, count);
}
+TEST_F(SurfaceTest, BatchOperations) {
+ const int BUFFER_COUNT = 16;
+ const int BATCH_SIZE = 8;
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
+ sp<Surface> surface = new Surface(producer);
+ sp<ANativeWindow> window(surface);
+ sp<StubProducerListener> listener = new StubProducerListener();
+
+ ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, /*listener*/listener,
+ /*reportBufferRemoval*/false));
+
+ ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT));
+
+ std::vector<Surface::BatchBuffer> buffers(BATCH_SIZE);
+
+ // Batch dequeued buffers can be queued individually
+ ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers));
+ for (size_t i = 0; i < BATCH_SIZE; i++) {
+ ANativeWindowBuffer* buffer = buffers[i].buffer;
+ int fence = buffers[i].fenceFd;
+ ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, fence));
+ }
+
+ // Batch dequeued buffers can be canceled individually
+ ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers));
+ for (size_t i = 0; i < BATCH_SIZE; i++) {
+ ANativeWindowBuffer* buffer = buffers[i].buffer;
+ int fence = buffers[i].fenceFd;
+ ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence));
+ }
+
+ // Batch dequeued buffers can be batch cancelled
+ ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers));
+ ASSERT_EQ(NO_ERROR, surface->cancelBuffers(buffers));
+
+ // Batch dequeued buffers can be batch queued
+ ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers));
+ std::vector<Surface::BatchQueuedBuffer> queuedBuffers(BATCH_SIZE);
+ for (size_t i = 0; i < BATCH_SIZE; i++) {
+ queuedBuffers[i].buffer = buffers[i].buffer;
+ queuedBuffers[i].fenceFd = buffers[i].fenceFd;
+ queuedBuffers[i].timestamp = NATIVE_WINDOW_TIMESTAMP_AUTO;
+ }
+ ASSERT_EQ(NO_ERROR, surface->queueBuffers(queuedBuffers));
+
+ ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU));
+}
+
+TEST_F(SurfaceTest, BatchIllegalOperations) {
+ const int BUFFER_COUNT = 16;
+ const int BATCH_SIZE = 8;
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
+ sp<Surface> surface = new Surface(producer);
+ sp<ANativeWindow> window(surface);
+ sp<StubProducerListener> listener = new StubProducerListener();
+
+ ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, /*listener*/listener,
+ /*reportBufferRemoval*/false));
+
+ ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT));
+
+ std::vector<Surface::BatchBuffer> buffers(BATCH_SIZE);
+ std::vector<Surface::BatchQueuedBuffer> queuedBuffers(BATCH_SIZE);
+
+ // Batch operations are invalid in shared buffer mode
+ surface->setSharedBufferMode(true);
+ ASSERT_EQ(INVALID_OPERATION, surface->dequeueBuffers(&buffers));
+ ASSERT_EQ(INVALID_OPERATION, surface->cancelBuffers(buffers));
+ ASSERT_EQ(INVALID_OPERATION, surface->queueBuffers(queuedBuffers));
+ surface->setSharedBufferMode(false);
+
+ ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU));
+}
+
} // namespace android
diff --git a/libs/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp
index d98ffc6..1bfe462 100644
--- a/libs/gui/view/Surface.cpp
+++ b/libs/gui/view/Surface.cpp
@@ -45,7 +45,9 @@
if (res != OK) return res;
}
- return IGraphicBufferProducer::exportToParcel(graphicBufferProducer, parcel);
+ res = IGraphicBufferProducer::exportToParcel(graphicBufferProducer, parcel);
+ if (res != OK) return res;
+ return parcel->writeStrongBinder(surfaceControlHandle);
}
status_t Surface::readFromParcel(const Parcel* parcel) {
@@ -68,6 +70,7 @@
}
graphicBufferProducer = IGraphicBufferProducer::createFromParcel(parcel);
+ surfaceControlHandle = parcel->readStrongBinder();
return OK;
}
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 393c0f6..6f79f4b 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -23,6 +23,17 @@
default_applicable_licenses: ["frameworks_native_license"],
}
+filegroup {
+ name: "inputconstants_aidl",
+ srcs: [
+ "android/os/BlockUntrustedTouchesMode.aidl",
+ "android/os/IInputConstants.aidl",
+ "android/os/InputEventInjectionResult.aidl",
+ "android/os/InputEventInjectionSync.aidl",
+ "android/os/TouchOcclusionMode.aidl",
+ ],
+}
+
cc_library {
name: "libinput",
host_supported: true,
@@ -34,11 +45,15 @@
srcs: [
"Input.cpp",
"InputDevice.cpp",
+ "InputEventLabels.cpp",
"Keyboard.cpp",
"KeyCharacterMap.cpp",
"KeyLayoutMap.cpp",
+ "LatencyStatistics.cpp",
"PropertyMap.cpp",
"TouchVideoFrame.cpp",
+ "VelocityControl.cpp",
+ "VelocityTracker.cpp",
"VirtualKeyMap.cpp",
],
@@ -53,19 +68,32 @@
"libcutils",
],
+ static_libs: [
+ "libui-types",
+ ],
+
+ export_static_lib_headers: [
+ "libui-types",
+ ],
+
target: {
android: {
srcs: [
- "IInputFlinger.cpp",
- "InputApplication.cpp",
"InputTransport.cpp",
"InputWindow.cpp",
- "ISetInputWindowsListener.cpp",
- "LatencyStatistics.cpp",
- "VelocityControl.cpp",
- "VelocityTracker.cpp",
+ "android/FocusRequest.aidl",
+ "android/InputApplicationInfo.aidl",
+ "android/os/BlockUntrustedTouchesMode.aidl",
+ "android/os/IInputConstants.aidl",
+ "android/os/IInputFlinger.aidl",
+ "android/os/InputEventInjectionResult.aidl",
+ "android/os/InputEventInjectionSync.aidl",
+ "android/os/ISetInputWindowsListener.aidl",
+ "android/os/TouchOcclusionMode.aidl",
],
+ export_shared_lib_headers: ["libbinder"],
+
shared_libs: [
"libutils",
"libbinder",
@@ -80,7 +108,33 @@
shared: {
enabled: false,
},
+ include_dirs: [
+ "frameworks/native/libs/arect/include",
+ ],
},
+ linux_glibc: {
+ srcs: [
+ "InputTransport.cpp",
+ "InputWindow.cpp",
+ "android/FocusRequest.aidl",
+ "android/InputApplicationInfo.aidl",
+ "android/os/IInputConstants.aidl",
+ "android/os/IInputFlinger.aidl",
+ "android/os/ISetInputWindowsListener.aidl",
+ "android/os/TouchOcclusionMode.aidl",
+ ],
+ static_libs: [
+ "libhostgraphics",
+ ],
+ shared_libs: [
+ "libbinder",
+ ],
+ },
+ },
+
+ aidl: {
+ local_include_dirs: ["."],
+ export_aidl_headers: true,
},
}
diff --git a/libs/input/IInputFlinger.cpp b/libs/input/IInputFlinger.cpp
deleted file mode 100644
index 8ec5165..0000000
--- a/libs/input/IInputFlinger.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2013 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 <stdint.h>
-#include <sys/types.h>
-
-#include <binder/Parcel.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-
-#include <input/IInputFlinger.h>
-
-namespace android {
-
-class BpInputFlinger : public BpInterface<IInputFlinger> {
-public:
- explicit BpInputFlinger(const sp<IBinder>& impl) :
- BpInterface<IInputFlinger>(impl) { }
-
- virtual void setInputWindows(const std::vector<InputWindowInfo>& inputInfo,
- const sp<ISetInputWindowsListener>& setInputWindowsListener) {
- Parcel data, reply;
- data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor());
-
- data.writeUint32(static_cast<uint32_t>(inputInfo.size()));
- for (const auto& info : inputInfo) {
- info.write(data);
- }
- data.writeStrongBinder(IInterface::asBinder(setInputWindowsListener));
-
- remote()->transact(BnInputFlinger::SET_INPUT_WINDOWS_TRANSACTION, data, &reply,
- IBinder::FLAG_ONEWAY);
- }
-
- virtual void registerInputChannel(const sp<InputChannel>& channel) {
- Parcel data, reply;
- data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor());
- channel->write(data);
- remote()->transact(BnInputFlinger::REGISTER_INPUT_CHANNEL_TRANSACTION, data, &reply);
- }
-
- virtual void unregisterInputChannel(const sp<InputChannel>& channel) {
- Parcel data, reply;
- data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor());
- channel->write(data);
- remote()->transact(BnInputFlinger::UNREGISTER_INPUT_CHANNEL_TRANSACTION, data, &reply);
- }
-};
-
-IMPLEMENT_META_INTERFACE(InputFlinger, "android.input.IInputFlinger");
-
-status_t BnInputFlinger::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
- switch(code) {
- case SET_INPUT_WINDOWS_TRANSACTION: {
- CHECK_INTERFACE(IInputFlinger, data, reply);
- size_t count = data.readUint32();
- if (count > data.dataSize()) {
- return BAD_VALUE;
- }
- std::vector<InputWindowInfo> handles;
- for (size_t i = 0; i < count; i++) {
- handles.push_back(InputWindowInfo::read(data));
- }
- const sp<ISetInputWindowsListener> setInputWindowsListener =
- ISetInputWindowsListener::asInterface(data.readStrongBinder());
- setInputWindows(handles, setInputWindowsListener);
- break;
- }
- case REGISTER_INPUT_CHANNEL_TRANSACTION: {
- CHECK_INTERFACE(IInputFlinger, data, reply);
- sp<InputChannel> channel = InputChannel::read(data);
- registerInputChannel(channel);
- break;
- }
- case UNREGISTER_INPUT_CHANNEL_TRANSACTION: {
- CHECK_INTERFACE(IInputFlinger, data, reply);
- sp<InputChannel> channel = InputChannel::read(data);
- unregisterInputChannel(channel);
- break;
- }
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
- return NO_ERROR;
-}
-
-};
diff --git a/libs/input/ISetInputWindowsListener.cpp b/libs/input/ISetInputWindowsListener.cpp
deleted file mode 100644
index a0330da..0000000
--- a/libs/input/ISetInputWindowsListener.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2019 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 <input/ISetInputWindowsListener.h>
-
-namespace android {
-
-class BpSetInputWindowsListener : public BpInterface<ISetInputWindowsListener> {
-public:
- explicit BpSetInputWindowsListener(const sp<IBinder>& impl)
- : BpInterface<ISetInputWindowsListener>(impl) {
- }
-
- virtual ~BpSetInputWindowsListener() = default;
-
- virtual void onSetInputWindowsFinished() {
- Parcel data, reply;
- data.writeInterfaceToken(ISetInputWindowsListener::getInterfaceDescriptor());
- remote()->transact(BnSetInputWindowsListener::ON_SET_INPUT_WINDOWS_FINISHED, data, &reply,
- IBinder::FLAG_ONEWAY);
- }
-};
-
-IMPLEMENT_META_INTERFACE(SetInputWindowsListener, "android.input.ISetInputWindowsListener");
-
-status_t BnSetInputWindowsListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags) {
- switch(code) {
- case ON_SET_INPUT_WINDOWS_FINISHED: {
- CHECK_INTERFACE(ISetInputWindowsListener, data, reply);
- onSetInputWindowsFinished();
- return NO_ERROR;
- }
- default: {
- return BBinder::onTransact(code, data, reply, flags);
- }
- }
-}
-
-} // namespace android
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 31aa685..5600eb3 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -17,19 +17,26 @@
#define LOG_TAG "Input"
//#define LOG_NDEBUG 0
+#include <attestation/HmacKeyManager.h>
#include <cutils/compiler.h>
+#include <inttypes.h>
#include <limits.h>
#include <string.h>
+#include <android-base/stringprintf.h>
#include <input/Input.h>
#include <input/InputDevice.h>
#include <input/InputEventLabels.h>
-#ifdef __ANDROID__
+#ifdef __linux__
#include <binder/Parcel.h>
+#endif
+#ifdef __ANDROID__
#include <sys/random.h>
#endif
+using android::base::StringPrintf;
+
namespace android {
const char* motionClassificationToString(MotionClassification classification) {
@@ -82,6 +89,12 @@
case AINPUT_EVENT_TYPE_FOCUS: {
return "FOCUS";
}
+ case AINPUT_EVENT_TYPE_CAPTURE: {
+ return "CAPTURE";
+ }
+ case AINPUT_EVENT_TYPE_DRAG: {
+ return "DRAG";
+ }
}
return "UNKNOWN";
}
@@ -135,11 +148,11 @@
// --- KeyEvent ---
const char* KeyEvent::getLabel(int32_t keyCode) {
- return getLabelByKeyCode(keyCode);
+ return InputEventLookup::getLabelByKeyCode(keyCode);
}
int32_t KeyEvent::getKeyCodeFromLabel(const char* label) {
- return getKeyCodeByLabel(label);
+ return InputEventLookup::getKeyCodeByLabel(label);
}
void KeyEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
@@ -249,7 +262,7 @@
setAxisValue(AMOTION_EVENT_AXIS_Y, getY() + yOffset);
}
-#ifdef __ANDROID__
+#ifdef __linux__
status_t PointerCoords::readFromParcel(Parcel* parcel) {
bits = parcel->readInt64();
@@ -301,6 +314,11 @@
}
}
+void PointerCoords::transform(const ui::Transform& transform) {
+ vec2 newCoords = transform.transform(getX(), getY());
+ setAxisValue(AMOTION_EVENT_AXIS_X, newCoords.x);
+ setAxisValue(AMOTION_EVENT_AXIS_Y, newCoords.y);
+}
// --- PointerProperties ---
@@ -320,10 +338,10 @@
void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton,
int32_t flags, int32_t edgeFlags, int32_t metaState,
- int32_t buttonState, MotionClassification classification, float xScale,
- float yScale, float xOffset, float yOffset, float xPrecision,
- float yPrecision, float rawXCursorPosition, float rawYCursorPosition,
- nsecs_t downTime, nsecs_t eventTime, size_t pointerCount,
+ int32_t buttonState, MotionClassification classification,
+ const ui::Transform& transform, float xPrecision, float yPrecision,
+ float rawXCursorPosition, float rawYCursorPosition, nsecs_t downTime,
+ nsecs_t eventTime, size_t pointerCount,
const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords) {
InputEvent::initialize(id, deviceId, source, displayId, hmac);
@@ -334,10 +352,7 @@
mMetaState = metaState;
mButtonState = buttonState;
mClassification = classification;
- mXScale = xScale;
- mYScale = yScale;
- mXOffset = xOffset;
- mYOffset = yOffset;
+ mTransform = transform;
mXPrecision = xPrecision;
mYPrecision = yPrecision;
mRawXCursorPosition = rawXCursorPosition;
@@ -360,10 +375,7 @@
mMetaState = other->mMetaState;
mButtonState = other->mButtonState;
mClassification = other->mClassification;
- mXScale = other->mXScale;
- mYScale = other->mYScale;
- mXOffset = other->mXOffset;
- mYOffset = other->mYOffset;
+ mTransform = other->mTransform;
mXPrecision = other->mXPrecision;
mYPrecision = other->mYPrecision;
mRawXCursorPosition = other->mRawXCursorPosition;
@@ -376,7 +388,7 @@
mSamplePointerCoords = other->mSamplePointerCoords;
} else {
mSampleEventTimes.clear();
- mSampleEventTimes.push(other->getEventTime());
+ mSampleEventTimes.push_back(other->getEventTime());
mSamplePointerCoords.clear();
size_t pointerCount = other->getPointerCount();
size_t historySize = other->getHistorySize();
@@ -388,23 +400,25 @@
void MotionEvent::addSample(
int64_t eventTime,
const PointerCoords* pointerCoords) {
- mSampleEventTimes.push(eventTime);
+ mSampleEventTimes.push_back(eventTime);
mSamplePointerCoords.appendArray(pointerCoords, getPointerCount());
}
float MotionEvent::getXCursorPosition() const {
- const float rawX = getRawXCursorPosition();
- return rawX * mXScale + mXOffset;
+ vec2 vals = mTransform.transform(getRawXCursorPosition(), getRawYCursorPosition());
+ return vals.x;
}
float MotionEvent::getYCursorPosition() const {
- const float rawY = getRawYCursorPosition();
- return rawY * mYScale + mYOffset;
+ vec2 vals = mTransform.transform(getRawXCursorPosition(), getRawYCursorPosition());
+ return vals.y;
}
void MotionEvent::setCursorPosition(float x, float y) {
- mRawXCursorPosition = (x - mXOffset) / mXScale;
- mRawYCursorPosition = (y - mYOffset) / mYScale;
+ ui::Transform inverse = mTransform.inverse();
+ vec2 vals = inverse.transform(x, y);
+ mRawXCursorPosition = vals.x;
+ mRawYCursorPosition = vals.y;
}
const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const {
@@ -416,14 +430,7 @@
}
float MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const {
- float value = getRawPointerCoords(pointerIndex)->getAxisValue(axis);
- switch (axis) {
- case AMOTION_EVENT_AXIS_X:
- return value * mXScale + mXOffset;
- case AMOTION_EVENT_AXIS_Y:
- return value * mYScale + mYOffset;
- }
- return value;
+ return getHistoricalAxisValue(axis, pointerIndex, getHistorySize());
}
const PointerCoords* MotionEvent::getHistoricalRawPointerCoords(
@@ -438,14 +445,23 @@
float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex,
size_t historicalIndex) const {
- float value = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
+ if (axis != AMOTION_EVENT_AXIS_X && axis != AMOTION_EVENT_AXIS_Y) {
+ return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
+ }
+
+ float rawX = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getX();
+ float rawY = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getY();
+ vec2 vals = mTransform.transform(rawX, rawY);
+
switch (axis) {
case AMOTION_EVENT_AXIS_X:
- return value * mXScale + mXOffset;
+ return vals.x;
case AMOTION_EVENT_AXIS_Y:
- return value * mYScale + mYOffset;
+ return vals.y;
}
- return value;
+
+ // This should never happen
+ return 0;
}
ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const {
@@ -459,23 +475,24 @@
}
void MotionEvent::offsetLocation(float xOffset, float yOffset) {
- mXOffset += xOffset;
- mYOffset += yOffset;
+ float currXOffset = mTransform.tx();
+ float currYOffset = mTransform.ty();
+ mTransform.set(currXOffset + xOffset, currYOffset + yOffset);
}
void MotionEvent::scale(float globalScaleFactor) {
- mXOffset *= globalScaleFactor;
- mYOffset *= globalScaleFactor;
+ mTransform.set(mTransform.tx() * globalScaleFactor, mTransform.ty() * globalScaleFactor);
mXPrecision *= globalScaleFactor;
mYPrecision *= globalScaleFactor;
size_t numSamples = mSamplePointerCoords.size();
for (size_t i = 0; i < numSamples; i++) {
- mSamplePointerCoords.editItemAt(i).scale(globalScaleFactor);
+ mSamplePointerCoords.editItemAt(i).scale(globalScaleFactor, globalScaleFactor,
+ globalScaleFactor);
}
}
-static void transformPoint(const float matrix[9], float x, float y, float *outX, float *outY) {
+static vec2 transformPoint(const std::array<float, 9>& matrix, float x, float y) {
// Apply perspective transform like Skia.
float newX = matrix[0] * x + matrix[1] * y + matrix[2];
float newY = matrix[3] * x + matrix[4] * y + matrix[5];
@@ -483,22 +500,25 @@
if (newZ) {
newZ = 1.0f / newZ;
}
- *outX = newX * newZ;
- *outY = newY * newZ;
+ vec2 transformedPoint;
+ transformedPoint.x = newX * newZ;
+ transformedPoint.y = newY * newZ;
+ return transformedPoint;
}
-static float transformAngle(const float matrix[9], float angleRadians,
- float originX, float originY) {
+static float transformAngle(const std::array<float, 9>& matrix, float angleRadians, float originX,
+ float originY) {
// Construct and transform a vector oriented at the specified clockwise angle from vertical.
// Coordinate system: down is increasing Y, right is increasing X.
float x = sinf(angleRadians);
float y = -cosf(angleRadians);
- transformPoint(matrix, x, y, &x, &y);
- x -= originX;
- y -= originY;
+ vec2 transformedPoint = transformPoint(matrix, x, y);
+
+ transformedPoint.x -= originX;
+ transformedPoint.y -= originY;
// Derive the transformed vector's clockwise angle from vertical.
- float result = atan2f(x, -y);
+ float result = atan2f(transformedPoint.x, -transformedPoint.y);
if (result < - M_PI_2) {
result += M_PI;
} else if (result > M_PI_2) {
@@ -507,51 +527,51 @@
return result;
}
-void MotionEvent::transform(const float matrix[9]) {
- // The tricky part of this implementation is to preserve the value of
- // rawX and rawY. So we apply the transformation to the first point
- // then derive an appropriate new X/Y offset that will preserve rawX
- // and rawY for that point.
- float oldXOffset = mXOffset;
- float oldYOffset = mYOffset;
- float newX, newY;
- float scaledRawX = getRawX(0) * mXScale;
- float scaledRawY = getRawY(0) * mYScale;
- transformPoint(matrix, scaledRawX + oldXOffset, scaledRawY + oldYOffset, &newX, &newY);
- mXOffset = newX - scaledRawX;
- mYOffset = newY - scaledRawY;
+void MotionEvent::transform(const std::array<float, 9>& matrix) {
+ // We want to preserve the rawX and rawY so we just update the transform
+ // using the values of the transform passed in
+ ui::Transform newTransform;
+ newTransform.set(matrix);
+ mTransform = newTransform * mTransform;
// Determine how the origin is transformed by the matrix so that we
// can transform orientation vectors.
- float originX, originY;
- transformPoint(matrix, 0, 0, &originX, &originY);
-
- // Apply the transformation to cursor position.
- if (isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) {
- float x = mRawXCursorPosition * mXScale + oldXOffset;
- float y = mRawYCursorPosition * mYScale + oldYOffset;
- transformPoint(matrix, x, y, &x, &y);
- mRawXCursorPosition = (x - mXOffset) / mXScale;
- mRawYCursorPosition = (y - mYOffset) / mYScale;
- }
+ vec2 origin = transformPoint(matrix, 0, 0);
// Apply the transformation to all samples.
size_t numSamples = mSamplePointerCoords.size();
for (size_t i = 0; i < numSamples; i++) {
PointerCoords& c = mSamplePointerCoords.editItemAt(i);
- float x = c.getAxisValue(AMOTION_EVENT_AXIS_X) * mXScale + oldXOffset;
- float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y) * mYScale + oldYOffset;
- transformPoint(matrix, x, y, &x, &y);
- c.setAxisValue(AMOTION_EVENT_AXIS_X, (x - mXOffset) / mXScale);
- c.setAxisValue(AMOTION_EVENT_AXIS_Y, (y - mYOffset) / mYScale);
-
float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
- transformAngle(matrix, orientation, originX, originY));
+ transformAngle(matrix, orientation, origin.x, origin.y));
}
}
-#ifdef __ANDROID__
+#ifdef __linux__
+static status_t readFromParcel(ui::Transform& transform, const Parcel& parcel) {
+ float dsdx, dtdx, tx, dtdy, dsdy, ty;
+ status_t status = parcel.readFloat(&dsdx);
+ status |= parcel.readFloat(&dtdx);
+ status |= parcel.readFloat(&tx);
+ status |= parcel.readFloat(&dtdy);
+ status |= parcel.readFloat(&dsdy);
+ status |= parcel.readFloat(&ty);
+
+ transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1});
+ return status;
+}
+
+static status_t writeToParcel(const ui::Transform& transform, Parcel& parcel) {
+ status_t status = parcel.writeFloat(transform.dsdx());
+ status |= parcel.writeFloat(transform.dtdx());
+ status |= parcel.writeFloat(transform.tx());
+ status |= parcel.writeFloat(transform.dtdy());
+ status |= parcel.writeFloat(transform.dsdy());
+ status |= parcel.writeFloat(transform.ty());
+ return status;
+}
+
status_t MotionEvent::readFromParcel(Parcel* parcel) {
size_t pointerCount = parcel->readInt32();
size_t sampleCount = parcel->readInt32();
@@ -577,10 +597,11 @@
mMetaState = parcel->readInt32();
mButtonState = parcel->readInt32();
mClassification = static_cast<MotionClassification>(parcel->readByte());
- mXScale = parcel->readFloat();
- mYScale = parcel->readFloat();
- mXOffset = parcel->readFloat();
- mYOffset = parcel->readFloat();
+
+ result = android::readFromParcel(mTransform, *parcel);
+ if (result != OK) {
+ return result;
+ }
mXPrecision = parcel->readFloat();
mYPrecision = parcel->readFloat();
mRawXCursorPosition = parcel->readFloat();
@@ -590,7 +611,7 @@
mPointerProperties.clear();
mPointerProperties.setCapacity(pointerCount);
mSampleEventTimes.clear();
- mSampleEventTimes.setCapacity(sampleCount);
+ mSampleEventTimes.reserve(sampleCount);
mSamplePointerCoords.clear();
mSamplePointerCoords.setCapacity(sampleCount * pointerCount);
@@ -603,7 +624,7 @@
while (sampleCount > 0) {
sampleCount--;
- mSampleEventTimes.push(parcel->readInt64());
+ mSampleEventTimes.push_back(parcel->readInt64());
for (size_t i = 0; i < pointerCount; i++) {
mSamplePointerCoords.push();
status_t status = mSamplePointerCoords.editTop().readFromParcel(parcel);
@@ -635,10 +656,11 @@
parcel->writeInt32(mMetaState);
parcel->writeInt32(mButtonState);
parcel->writeByte(static_cast<int8_t>(mClassification));
- parcel->writeFloat(mXScale);
- parcel->writeFloat(mYScale);
- parcel->writeFloat(mXOffset);
- parcel->writeFloat(mYOffset);
+
+ status_t result = android::writeToParcel(mTransform, *parcel);
+ if (result != OK) {
+ return result;
+ }
parcel->writeFloat(mXPrecision);
parcel->writeFloat(mYPrecision);
parcel->writeFloat(mRawXCursorPosition);
@@ -653,7 +675,7 @@
const PointerCoords* pc = mSamplePointerCoords.array();
for (size_t h = 0; h < sampleCount; h++) {
- parcel->writeInt64(mSampleEventTimes.itemAt(h));
+ parcel->writeInt64(mSampleEventTimes[h]);
for (size_t i = 0; i < pointerCount; i++) {
status_t status = (pc++)->writeToParcel(parcel);
if (status) {
@@ -683,30 +705,44 @@
}
const char* MotionEvent::getLabel(int32_t axis) {
- return getAxisLabel(axis);
+ return InputEventLookup::getAxisLabel(axis);
}
int32_t MotionEvent::getAxisFromLabel(const char* label) {
- return getAxisByLabel(label);
+ return InputEventLookup::getAxisByLabel(label);
}
-const char* MotionEvent::actionToString(int32_t action) {
+std::string MotionEvent::actionToString(int32_t action) {
// Convert MotionEvent action to string
switch (action & AMOTION_EVENT_ACTION_MASK) {
case AMOTION_EVENT_ACTION_DOWN:
return "DOWN";
- case AMOTION_EVENT_ACTION_MOVE:
- return "MOVE";
case AMOTION_EVENT_ACTION_UP:
return "UP";
+ case AMOTION_EVENT_ACTION_MOVE:
+ return "MOVE";
case AMOTION_EVENT_ACTION_CANCEL:
return "CANCEL";
+ case AMOTION_EVENT_ACTION_OUTSIDE:
+ return "OUTSIDE";
case AMOTION_EVENT_ACTION_POINTER_DOWN:
return "POINTER_DOWN";
case AMOTION_EVENT_ACTION_POINTER_UP:
return "POINTER_UP";
+ case AMOTION_EVENT_ACTION_HOVER_MOVE:
+ return "HOVER_MOVE";
+ case AMOTION_EVENT_ACTION_SCROLL:
+ return "SCROLL";
+ case AMOTION_EVENT_ACTION_HOVER_ENTER:
+ return "HOVER_ENTER";
+ case AMOTION_EVENT_ACTION_HOVER_EXIT:
+ return "HOVER_EXIT";
+ case AMOTION_EVENT_ACTION_BUTTON_PRESS:
+ return "BUTTON_PRESS";
+ case AMOTION_EVENT_ACTION_BUTTON_RELEASE:
+ return "BUTTON_RELEASE";
}
- return "UNKNOWN";
+ return android::base::StringPrintf("%" PRId32, action);
}
// --- FocusEvent ---
@@ -724,6 +760,36 @@
mInTouchMode = from.mInTouchMode;
}
+// --- CaptureEvent ---
+
+void CaptureEvent::initialize(int32_t id, bool pointerCaptureEnabled) {
+ InputEvent::initialize(id, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN,
+ ADISPLAY_ID_NONE, INVALID_HMAC);
+ mPointerCaptureEnabled = pointerCaptureEnabled;
+}
+
+void CaptureEvent::initialize(const CaptureEvent& from) {
+ InputEvent::initialize(from);
+ mPointerCaptureEnabled = from.mPointerCaptureEnabled;
+}
+
+// --- DragEvent ---
+
+void DragEvent::initialize(int32_t id, float x, float y, bool isExiting) {
+ InputEvent::initialize(id, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN,
+ ADISPLAY_ID_NONE, INVALID_HMAC);
+ mIsExiting = isExiting;
+ mX = x;
+ mY = y;
+}
+
+void DragEvent::initialize(const DragEvent& from) {
+ InputEvent::initialize(from);
+ mIsExiting = from.mIsExiting;
+ mX = from.mX;
+ mY = from.mY;
+}
+
// --- PooledInputEventFactory ---
PooledInputEventFactory::PooledInputEventFactory(size_t maxPoolSize) :
@@ -760,6 +826,24 @@
return event;
}
+CaptureEvent* PooledInputEventFactory::createCaptureEvent() {
+ if (mCaptureEventPool.empty()) {
+ return new CaptureEvent();
+ }
+ CaptureEvent* event = mCaptureEventPool.front().release();
+ mCaptureEventPool.pop();
+ return event;
+}
+
+DragEvent* PooledInputEventFactory::createDragEvent() {
+ if (mDragEventPool.empty()) {
+ return new DragEvent();
+ }
+ DragEvent* event = mDragEventPool.front().release();
+ mDragEventPool.pop();
+ return event;
+}
+
void PooledInputEventFactory::recycle(InputEvent* event) {
switch (event->getType()) {
case AINPUT_EVENT_TYPE_KEY:
@@ -780,6 +864,19 @@
return;
}
break;
+ case AINPUT_EVENT_TYPE_CAPTURE:
+ if (mCaptureEventPool.size() < mMaxPoolSize) {
+ mCaptureEventPool.push(
+ std::unique_ptr<CaptureEvent>(static_cast<CaptureEvent*>(event)));
+ return;
+ }
+ break;
+ case AINPUT_EVENT_TYPE_DRAG:
+ if (mDragEventPool.size() < mMaxPoolSize) {
+ mDragEventPool.push(std::unique_ptr<DragEvent>(static_cast<DragEvent*>(event)));
+ return;
+ }
+ break;
}
delete event;
}
diff --git a/libs/input/InputApplication.cpp b/libs/input/InputApplication.cpp
deleted file mode 100644
index 1d9f8a7..0000000
--- a/libs/input/InputApplication.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-#define LOG_TAG "InputApplication"
-
-#include <input/InputApplication.h>
-
-#include <android/log.h>
-
-namespace android {
-
-// --- InputApplicationHandle ---
-
-InputApplicationHandle::InputApplicationHandle() {
-}
-
-InputApplicationHandle::~InputApplicationHandle() {
-}
-
-InputApplicationInfo InputApplicationInfo::read(const Parcel& from) {
- InputApplicationInfo ret;
- ret.token = from.readStrongBinder();
- ret.name = from.readString8().c_str();
- ret.dispatchingTimeout = from.readInt64();
-
- return ret;
-}
-
-status_t InputApplicationInfo::write(Parcel& output) const {
- output.writeStrongBinder(token);
- output.writeString8(String8(name.c_str()));
- output.writeInt64(dispatchingTimeout);
-
- return OK;
-}
-
-} // namespace android
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index 4db9e06..31027b6 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -23,6 +23,7 @@
#include <android-base/stringprintf.h>
#include <input/InputDevice.h>
#include <input/InputEventLabels.h>
+#include <input/NamedEnum.h>
using android::base::StringPrintf;
@@ -46,9 +47,9 @@
static void appendInputDeviceConfigurationFileRelativePath(std::string& path,
const std::string& name, InputDeviceConfigurationFileType type) {
- path += CONFIGURATION_FILE_DIR[type];
+ path += CONFIGURATION_FILE_DIR[static_cast<int32_t>(type)];
path += name;
- path += CONFIGURATION_FILE_EXTENSION[type];
+ path += CONFIGURATION_FILE_EXTENSION[static_cast<int32_t>(type)];
}
std::string getInputDeviceConfigurationFilePathByDeviceIdentifier(
@@ -153,14 +154,24 @@
initialize(-1, 0, -1, InputDeviceIdentifier(), "", false, false);
}
-InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) :
- mId(other.mId), mGeneration(other.mGeneration), mControllerNumber(other.mControllerNumber),
- mIdentifier(other.mIdentifier), mAlias(other.mAlias), mIsExternal(other.mIsExternal),
- mHasMic(other.mHasMic), mSources(other.mSources),
- mKeyboardType(other.mKeyboardType), mKeyCharacterMap(other.mKeyCharacterMap),
- mHasVibrator(other.mHasVibrator), mHasButtonUnderPad(other.mHasButtonUnderPad),
- mMotionRanges(other.mMotionRanges) {
-}
+InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other)
+ : mId(other.mId),
+ mGeneration(other.mGeneration),
+ mControllerNumber(other.mControllerNumber),
+ mIdentifier(other.mIdentifier),
+ mAlias(other.mAlias),
+ mIsExternal(other.mIsExternal),
+ mHasMic(other.mHasMic),
+ mSources(other.mSources),
+ mKeyboardType(other.mKeyboardType),
+ mKeyCharacterMap(other.mKeyCharacterMap),
+ mHasVibrator(other.mHasVibrator),
+ mHasBattery(other.mHasBattery),
+ mHasButtonUnderPad(other.mHasButtonUnderPad),
+ mHasSensor(other.mHasSensor),
+ mMotionRanges(other.mMotionRanges),
+ mSensors(other.mSensors),
+ mLights(other.mLights) {}
InputDeviceInfo::~InputDeviceInfo() {
}
@@ -178,8 +189,12 @@
mSources = 0;
mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE;
mHasVibrator = false;
+ mHasBattery = false;
mHasButtonUnderPad = false;
+ mHasSensor = false;
mMotionRanges.clear();
+ mSensors.clear();
+ mLights.clear();
}
const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange(
@@ -208,4 +223,58 @@
mMotionRanges.push_back(range);
}
+void InputDeviceInfo::addSensorInfo(const InputDeviceSensorInfo& info) {
+ if (mSensors.find(info.type) != mSensors.end()) {
+ ALOGW("Sensor type %s already exists, will be replaced by new sensor added.",
+ NamedEnum::string(info.type).c_str());
+ }
+ mSensors.insert_or_assign(info.type, info);
+}
+
+void InputDeviceInfo::addBatteryInfo(const InputDeviceBatteryInfo& info) {
+ if (mBatteries.find(info.id) != mBatteries.end()) {
+ ALOGW("Battery id %d already exists, will be replaced by new battery added.", info.id);
+ }
+ mBatteries.insert_or_assign(info.id, info);
+}
+
+void InputDeviceInfo::addLightInfo(const InputDeviceLightInfo& info) {
+ if (mLights.find(info.id) != mLights.end()) {
+ ALOGW("Light id %d already exists, will be replaced by new light added.", info.id);
+ }
+ mLights.insert_or_assign(info.id, info);
+}
+
+const std::vector<InputDeviceSensorType> InputDeviceInfo::getSensorTypes() {
+ std::vector<InputDeviceSensorType> types;
+ for (const auto& [type, info] : mSensors) {
+ types.push_back(type);
+ }
+ return types;
+}
+
+const InputDeviceSensorInfo* InputDeviceInfo::getSensorInfo(InputDeviceSensorType type) {
+ auto it = mSensors.find(type);
+ if (it == mSensors.end()) {
+ return nullptr;
+ }
+ return &it->second;
+}
+
+const std::vector<int32_t> InputDeviceInfo::getLightIds() {
+ std::vector<int32_t> ids;
+ for (const auto& [id, info] : mLights) {
+ ids.push_back(id);
+ }
+ return ids;
+}
+
+const InputDeviceLightInfo* InputDeviceInfo::getLightInfo(int32_t id) {
+ auto it = mLights.find(id);
+ if (it == mLights.end()) {
+ return nullptr;
+ }
+ return &it->second;
+}
+
} // namespace android
diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp
new file mode 100644
index 0000000..c0aa2e2
--- /dev/null
+++ b/libs/input/InputEventLabels.cpp
@@ -0,0 +1,451 @@
+/*
+ * Copyright (C) 2020 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 <input/InputEventLabels.h>
+
+#define DEFINE_KEYCODE(key) { #key, AKEYCODE_##key }
+#define DEFINE_AXIS(axis) { #axis, AMOTION_EVENT_AXIS_##axis }
+#define DEFINE_LED(led) { #led, ALED_##led }
+#define DEFINE_FLAG(flag) { #flag, POLICY_FLAG_##flag }
+
+namespace android {
+
+// NOTE: If you add a new keycode here you must also add it to several other files.
+// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
+#define KEYCODES_SEQUENCE \
+ DEFINE_KEYCODE(UNKNOWN), \
+ DEFINE_KEYCODE(SOFT_LEFT), \
+ DEFINE_KEYCODE(SOFT_RIGHT), \
+ DEFINE_KEYCODE(HOME), \
+ DEFINE_KEYCODE(BACK), \
+ DEFINE_KEYCODE(CALL), \
+ DEFINE_KEYCODE(ENDCALL), \
+ DEFINE_KEYCODE(0), \
+ DEFINE_KEYCODE(1), \
+ DEFINE_KEYCODE(2), \
+ DEFINE_KEYCODE(3), \
+ DEFINE_KEYCODE(4), \
+ DEFINE_KEYCODE(5), \
+ DEFINE_KEYCODE(6), \
+ DEFINE_KEYCODE(7), \
+ DEFINE_KEYCODE(8), \
+ DEFINE_KEYCODE(9), \
+ DEFINE_KEYCODE(STAR), \
+ DEFINE_KEYCODE(POUND), \
+ DEFINE_KEYCODE(DPAD_UP), \
+ DEFINE_KEYCODE(DPAD_DOWN), \
+ DEFINE_KEYCODE(DPAD_LEFT), \
+ DEFINE_KEYCODE(DPAD_RIGHT), \
+ DEFINE_KEYCODE(DPAD_CENTER), \
+ DEFINE_KEYCODE(VOLUME_UP), \
+ DEFINE_KEYCODE(VOLUME_DOWN), \
+ DEFINE_KEYCODE(POWER), \
+ DEFINE_KEYCODE(CAMERA), \
+ DEFINE_KEYCODE(CLEAR), \
+ DEFINE_KEYCODE(A), \
+ DEFINE_KEYCODE(B), \
+ DEFINE_KEYCODE(C), \
+ DEFINE_KEYCODE(D), \
+ DEFINE_KEYCODE(E), \
+ DEFINE_KEYCODE(F), \
+ DEFINE_KEYCODE(G), \
+ DEFINE_KEYCODE(H), \
+ DEFINE_KEYCODE(I), \
+ DEFINE_KEYCODE(J), \
+ DEFINE_KEYCODE(K), \
+ DEFINE_KEYCODE(L), \
+ DEFINE_KEYCODE(M), \
+ DEFINE_KEYCODE(N), \
+ DEFINE_KEYCODE(O), \
+ DEFINE_KEYCODE(P), \
+ DEFINE_KEYCODE(Q), \
+ DEFINE_KEYCODE(R), \
+ DEFINE_KEYCODE(S), \
+ DEFINE_KEYCODE(T), \
+ DEFINE_KEYCODE(U), \
+ DEFINE_KEYCODE(V), \
+ DEFINE_KEYCODE(W), \
+ DEFINE_KEYCODE(X), \
+ DEFINE_KEYCODE(Y), \
+ DEFINE_KEYCODE(Z), \
+ DEFINE_KEYCODE(COMMA), \
+ DEFINE_KEYCODE(PERIOD), \
+ DEFINE_KEYCODE(ALT_LEFT), \
+ DEFINE_KEYCODE(ALT_RIGHT), \
+ DEFINE_KEYCODE(SHIFT_LEFT), \
+ DEFINE_KEYCODE(SHIFT_RIGHT), \
+ DEFINE_KEYCODE(TAB), \
+ DEFINE_KEYCODE(SPACE), \
+ DEFINE_KEYCODE(SYM), \
+ DEFINE_KEYCODE(EXPLORER), \
+ DEFINE_KEYCODE(ENVELOPE), \
+ DEFINE_KEYCODE(ENTER), \
+ DEFINE_KEYCODE(DEL), \
+ DEFINE_KEYCODE(GRAVE), \
+ DEFINE_KEYCODE(MINUS), \
+ DEFINE_KEYCODE(EQUALS), \
+ DEFINE_KEYCODE(LEFT_BRACKET), \
+ DEFINE_KEYCODE(RIGHT_BRACKET), \
+ DEFINE_KEYCODE(BACKSLASH), \
+ DEFINE_KEYCODE(SEMICOLON), \
+ DEFINE_KEYCODE(APOSTROPHE), \
+ DEFINE_KEYCODE(SLASH), \
+ DEFINE_KEYCODE(AT), \
+ DEFINE_KEYCODE(NUM), \
+ DEFINE_KEYCODE(HEADSETHOOK), \
+ DEFINE_KEYCODE(FOCUS), \
+ DEFINE_KEYCODE(PLUS), \
+ DEFINE_KEYCODE(MENU), \
+ DEFINE_KEYCODE(NOTIFICATION), \
+ DEFINE_KEYCODE(SEARCH), \
+ DEFINE_KEYCODE(MEDIA_PLAY_PAUSE), \
+ DEFINE_KEYCODE(MEDIA_STOP), \
+ DEFINE_KEYCODE(MEDIA_NEXT), \
+ DEFINE_KEYCODE(MEDIA_PREVIOUS), \
+ DEFINE_KEYCODE(MEDIA_REWIND), \
+ DEFINE_KEYCODE(MEDIA_FAST_FORWARD), \
+ DEFINE_KEYCODE(MUTE), \
+ DEFINE_KEYCODE(PAGE_UP), \
+ DEFINE_KEYCODE(PAGE_DOWN), \
+ DEFINE_KEYCODE(PICTSYMBOLS), \
+ DEFINE_KEYCODE(SWITCH_CHARSET), \
+ DEFINE_KEYCODE(BUTTON_A), \
+ DEFINE_KEYCODE(BUTTON_B), \
+ DEFINE_KEYCODE(BUTTON_C), \
+ DEFINE_KEYCODE(BUTTON_X), \
+ DEFINE_KEYCODE(BUTTON_Y), \
+ DEFINE_KEYCODE(BUTTON_Z), \
+ DEFINE_KEYCODE(BUTTON_L1), \
+ DEFINE_KEYCODE(BUTTON_R1), \
+ DEFINE_KEYCODE(BUTTON_L2), \
+ DEFINE_KEYCODE(BUTTON_R2), \
+ DEFINE_KEYCODE(BUTTON_THUMBL), \
+ DEFINE_KEYCODE(BUTTON_THUMBR), \
+ DEFINE_KEYCODE(BUTTON_START), \
+ DEFINE_KEYCODE(BUTTON_SELECT), \
+ DEFINE_KEYCODE(BUTTON_MODE), \
+ DEFINE_KEYCODE(ESCAPE), \
+ DEFINE_KEYCODE(FORWARD_DEL), \
+ DEFINE_KEYCODE(CTRL_LEFT), \
+ DEFINE_KEYCODE(CTRL_RIGHT), \
+ DEFINE_KEYCODE(CAPS_LOCK), \
+ DEFINE_KEYCODE(SCROLL_LOCK), \
+ DEFINE_KEYCODE(META_LEFT), \
+ DEFINE_KEYCODE(META_RIGHT), \
+ DEFINE_KEYCODE(FUNCTION), \
+ DEFINE_KEYCODE(SYSRQ), \
+ DEFINE_KEYCODE(BREAK), \
+ DEFINE_KEYCODE(MOVE_HOME), \
+ DEFINE_KEYCODE(MOVE_END), \
+ DEFINE_KEYCODE(INSERT), \
+ DEFINE_KEYCODE(FORWARD), \
+ DEFINE_KEYCODE(MEDIA_PLAY), \
+ DEFINE_KEYCODE(MEDIA_PAUSE), \
+ DEFINE_KEYCODE(MEDIA_CLOSE), \
+ DEFINE_KEYCODE(MEDIA_EJECT), \
+ DEFINE_KEYCODE(MEDIA_RECORD), \
+ DEFINE_KEYCODE(F1), \
+ DEFINE_KEYCODE(F2), \
+ DEFINE_KEYCODE(F3), \
+ DEFINE_KEYCODE(F4), \
+ DEFINE_KEYCODE(F5), \
+ DEFINE_KEYCODE(F6), \
+ DEFINE_KEYCODE(F7), \
+ DEFINE_KEYCODE(F8), \
+ DEFINE_KEYCODE(F9), \
+ DEFINE_KEYCODE(F10), \
+ DEFINE_KEYCODE(F11), \
+ DEFINE_KEYCODE(F12), \
+ DEFINE_KEYCODE(NUM_LOCK), \
+ DEFINE_KEYCODE(NUMPAD_0), \
+ DEFINE_KEYCODE(NUMPAD_1), \
+ DEFINE_KEYCODE(NUMPAD_2), \
+ DEFINE_KEYCODE(NUMPAD_3), \
+ DEFINE_KEYCODE(NUMPAD_4), \
+ DEFINE_KEYCODE(NUMPAD_5), \
+ DEFINE_KEYCODE(NUMPAD_6), \
+ DEFINE_KEYCODE(NUMPAD_7), \
+ DEFINE_KEYCODE(NUMPAD_8), \
+ DEFINE_KEYCODE(NUMPAD_9), \
+ DEFINE_KEYCODE(NUMPAD_DIVIDE), \
+ DEFINE_KEYCODE(NUMPAD_MULTIPLY), \
+ DEFINE_KEYCODE(NUMPAD_SUBTRACT), \
+ DEFINE_KEYCODE(NUMPAD_ADD), \
+ DEFINE_KEYCODE(NUMPAD_DOT), \
+ DEFINE_KEYCODE(NUMPAD_COMMA), \
+ DEFINE_KEYCODE(NUMPAD_ENTER), \
+ DEFINE_KEYCODE(NUMPAD_EQUALS), \
+ DEFINE_KEYCODE(NUMPAD_LEFT_PAREN), \
+ DEFINE_KEYCODE(NUMPAD_RIGHT_PAREN), \
+ DEFINE_KEYCODE(VOLUME_MUTE), \
+ DEFINE_KEYCODE(INFO), \
+ DEFINE_KEYCODE(CHANNEL_UP), \
+ DEFINE_KEYCODE(CHANNEL_DOWN), \
+ DEFINE_KEYCODE(ZOOM_IN), \
+ DEFINE_KEYCODE(ZOOM_OUT), \
+ DEFINE_KEYCODE(TV), \
+ DEFINE_KEYCODE(WINDOW), \
+ DEFINE_KEYCODE(GUIDE), \
+ DEFINE_KEYCODE(DVR), \
+ DEFINE_KEYCODE(BOOKMARK), \
+ DEFINE_KEYCODE(CAPTIONS), \
+ DEFINE_KEYCODE(SETTINGS), \
+ DEFINE_KEYCODE(TV_POWER), \
+ DEFINE_KEYCODE(TV_INPUT), \
+ DEFINE_KEYCODE(STB_POWER), \
+ DEFINE_KEYCODE(STB_INPUT), \
+ DEFINE_KEYCODE(AVR_POWER), \
+ DEFINE_KEYCODE(AVR_INPUT), \
+ DEFINE_KEYCODE(PROG_RED), \
+ DEFINE_KEYCODE(PROG_GREEN), \
+ DEFINE_KEYCODE(PROG_YELLOW), \
+ DEFINE_KEYCODE(PROG_BLUE), \
+ DEFINE_KEYCODE(APP_SWITCH), \
+ DEFINE_KEYCODE(BUTTON_1), \
+ DEFINE_KEYCODE(BUTTON_2), \
+ DEFINE_KEYCODE(BUTTON_3), \
+ DEFINE_KEYCODE(BUTTON_4), \
+ DEFINE_KEYCODE(BUTTON_5), \
+ DEFINE_KEYCODE(BUTTON_6), \
+ DEFINE_KEYCODE(BUTTON_7), \
+ DEFINE_KEYCODE(BUTTON_8), \
+ DEFINE_KEYCODE(BUTTON_9), \
+ DEFINE_KEYCODE(BUTTON_10), \
+ DEFINE_KEYCODE(BUTTON_11), \
+ DEFINE_KEYCODE(BUTTON_12), \
+ DEFINE_KEYCODE(BUTTON_13), \
+ DEFINE_KEYCODE(BUTTON_14), \
+ DEFINE_KEYCODE(BUTTON_15), \
+ DEFINE_KEYCODE(BUTTON_16), \
+ DEFINE_KEYCODE(LANGUAGE_SWITCH), \
+ DEFINE_KEYCODE(MANNER_MODE), \
+ DEFINE_KEYCODE(3D_MODE), \
+ DEFINE_KEYCODE(CONTACTS), \
+ DEFINE_KEYCODE(CALENDAR), \
+ DEFINE_KEYCODE(MUSIC), \
+ DEFINE_KEYCODE(CALCULATOR), \
+ DEFINE_KEYCODE(ZENKAKU_HANKAKU), \
+ DEFINE_KEYCODE(EISU), \
+ DEFINE_KEYCODE(MUHENKAN), \
+ DEFINE_KEYCODE(HENKAN), \
+ DEFINE_KEYCODE(KATAKANA_HIRAGANA), \
+ DEFINE_KEYCODE(YEN), \
+ DEFINE_KEYCODE(RO), \
+ DEFINE_KEYCODE(KANA), \
+ DEFINE_KEYCODE(ASSIST), \
+ DEFINE_KEYCODE(BRIGHTNESS_DOWN), \
+ DEFINE_KEYCODE(BRIGHTNESS_UP), \
+ DEFINE_KEYCODE(MEDIA_AUDIO_TRACK), \
+ DEFINE_KEYCODE(SLEEP), \
+ DEFINE_KEYCODE(WAKEUP), \
+ DEFINE_KEYCODE(PAIRING), \
+ DEFINE_KEYCODE(MEDIA_TOP_MENU), \
+ DEFINE_KEYCODE(11), \
+ DEFINE_KEYCODE(12), \
+ DEFINE_KEYCODE(LAST_CHANNEL), \
+ DEFINE_KEYCODE(TV_DATA_SERVICE), \
+ DEFINE_KEYCODE(VOICE_ASSIST), \
+ DEFINE_KEYCODE(TV_RADIO_SERVICE), \
+ DEFINE_KEYCODE(TV_TELETEXT), \
+ DEFINE_KEYCODE(TV_NUMBER_ENTRY), \
+ DEFINE_KEYCODE(TV_TERRESTRIAL_ANALOG), \
+ DEFINE_KEYCODE(TV_TERRESTRIAL_DIGITAL), \
+ DEFINE_KEYCODE(TV_SATELLITE), \
+ DEFINE_KEYCODE(TV_SATELLITE_BS), \
+ DEFINE_KEYCODE(TV_SATELLITE_CS), \
+ DEFINE_KEYCODE(TV_SATELLITE_SERVICE), \
+ DEFINE_KEYCODE(TV_NETWORK), \
+ DEFINE_KEYCODE(TV_ANTENNA_CABLE), \
+ DEFINE_KEYCODE(TV_INPUT_HDMI_1), \
+ DEFINE_KEYCODE(TV_INPUT_HDMI_2), \
+ DEFINE_KEYCODE(TV_INPUT_HDMI_3), \
+ DEFINE_KEYCODE(TV_INPUT_HDMI_4), \
+ DEFINE_KEYCODE(TV_INPUT_COMPOSITE_1), \
+ DEFINE_KEYCODE(TV_INPUT_COMPOSITE_2), \
+ DEFINE_KEYCODE(TV_INPUT_COMPONENT_1), \
+ DEFINE_KEYCODE(TV_INPUT_COMPONENT_2), \
+ DEFINE_KEYCODE(TV_INPUT_VGA_1), \
+ DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION), \
+ DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION_MIX_UP), \
+ DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION_MIX_DOWN), \
+ DEFINE_KEYCODE(TV_ZOOM_MODE), \
+ DEFINE_KEYCODE(TV_CONTENTS_MENU), \
+ DEFINE_KEYCODE(TV_MEDIA_CONTEXT_MENU), \
+ DEFINE_KEYCODE(TV_TIMER_PROGRAMMING), \
+ DEFINE_KEYCODE(HELP), \
+ DEFINE_KEYCODE(NAVIGATE_PREVIOUS), \
+ DEFINE_KEYCODE(NAVIGATE_NEXT), \
+ DEFINE_KEYCODE(NAVIGATE_IN), \
+ DEFINE_KEYCODE(NAVIGATE_OUT), \
+ DEFINE_KEYCODE(STEM_PRIMARY), \
+ DEFINE_KEYCODE(STEM_1), \
+ DEFINE_KEYCODE(STEM_2), \
+ DEFINE_KEYCODE(STEM_3), \
+ DEFINE_KEYCODE(DPAD_UP_LEFT), \
+ DEFINE_KEYCODE(DPAD_DOWN_LEFT), \
+ DEFINE_KEYCODE(DPAD_UP_RIGHT), \
+ DEFINE_KEYCODE(DPAD_DOWN_RIGHT), \
+ DEFINE_KEYCODE(MEDIA_SKIP_FORWARD), \
+ DEFINE_KEYCODE(MEDIA_SKIP_BACKWARD), \
+ DEFINE_KEYCODE(MEDIA_STEP_FORWARD), \
+ DEFINE_KEYCODE(MEDIA_STEP_BACKWARD), \
+ DEFINE_KEYCODE(SOFT_SLEEP), \
+ DEFINE_KEYCODE(CUT), \
+ DEFINE_KEYCODE(COPY), \
+ DEFINE_KEYCODE(PASTE), \
+ DEFINE_KEYCODE(SYSTEM_NAVIGATION_UP), \
+ DEFINE_KEYCODE(SYSTEM_NAVIGATION_DOWN), \
+ DEFINE_KEYCODE(SYSTEM_NAVIGATION_LEFT), \
+ DEFINE_KEYCODE(SYSTEM_NAVIGATION_RIGHT), \
+ DEFINE_KEYCODE(ALL_APPS), \
+ DEFINE_KEYCODE(REFRESH), \
+ DEFINE_KEYCODE(THUMBS_UP), \
+ DEFINE_KEYCODE(THUMBS_DOWN), \
+ DEFINE_KEYCODE(PROFILE_SWITCH)
+
+// NOTE: If you add a new axis here you must also add it to several other files.
+// Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list.
+#define AXES_SEQUENCE \
+ DEFINE_AXIS(X), \
+ DEFINE_AXIS(Y), \
+ DEFINE_AXIS(PRESSURE), \
+ DEFINE_AXIS(SIZE), \
+ DEFINE_AXIS(TOUCH_MAJOR), \
+ DEFINE_AXIS(TOUCH_MINOR), \
+ DEFINE_AXIS(TOOL_MAJOR), \
+ DEFINE_AXIS(TOOL_MINOR), \
+ DEFINE_AXIS(ORIENTATION), \
+ DEFINE_AXIS(VSCROLL), \
+ DEFINE_AXIS(HSCROLL), \
+ DEFINE_AXIS(Z), \
+ DEFINE_AXIS(RX), \
+ DEFINE_AXIS(RY), \
+ DEFINE_AXIS(RZ), \
+ DEFINE_AXIS(HAT_X), \
+ DEFINE_AXIS(HAT_Y), \
+ DEFINE_AXIS(LTRIGGER), \
+ DEFINE_AXIS(RTRIGGER), \
+ DEFINE_AXIS(THROTTLE), \
+ DEFINE_AXIS(RUDDER), \
+ DEFINE_AXIS(WHEEL), \
+ DEFINE_AXIS(GAS), \
+ DEFINE_AXIS(BRAKE), \
+ DEFINE_AXIS(DISTANCE), \
+ DEFINE_AXIS(TILT), \
+ DEFINE_AXIS(SCROLL), \
+ DEFINE_AXIS(RELATIVE_X), \
+ DEFINE_AXIS(RELATIVE_Y), \
+ {"RESERVED_29", 29}, \
+ {"RESERVED_30", 30}, \
+ {"RESERVED_31", 31}, \
+ DEFINE_AXIS(GENERIC_1), \
+ DEFINE_AXIS(GENERIC_2), \
+ DEFINE_AXIS(GENERIC_3), \
+ DEFINE_AXIS(GENERIC_4), \
+ DEFINE_AXIS(GENERIC_5), \
+ DEFINE_AXIS(GENERIC_6), \
+ DEFINE_AXIS(GENERIC_7), \
+ DEFINE_AXIS(GENERIC_8), \
+ DEFINE_AXIS(GENERIC_9), \
+ DEFINE_AXIS(GENERIC_10), \
+ DEFINE_AXIS(GENERIC_11), \
+ DEFINE_AXIS(GENERIC_12), \
+ DEFINE_AXIS(GENERIC_13), \
+ DEFINE_AXIS(GENERIC_14), \
+ DEFINE_AXIS(GENERIC_15), \
+ DEFINE_AXIS(GENERIC_16)
+
+
+// NOTE: If you add new LEDs here, you must also add them to Input.h
+#define LEDS_SEQUENCE \
+ DEFINE_LED(NUM_LOCK), \
+ DEFINE_LED(CAPS_LOCK), \
+ DEFINE_LED(SCROLL_LOCK), \
+ DEFINE_LED(COMPOSE), \
+ DEFINE_LED(KANA), \
+ DEFINE_LED(SLEEP), \
+ DEFINE_LED(SUSPEND), \
+ DEFINE_LED(MUTE), \
+ DEFINE_LED(MISC), \
+ DEFINE_LED(MAIL), \
+ DEFINE_LED(CHARGING), \
+ DEFINE_LED(CONTROLLER_1), \
+ DEFINE_LED(CONTROLLER_2), \
+ DEFINE_LED(CONTROLLER_3), \
+ DEFINE_LED(CONTROLLER_4)
+
+#define FLAGS_SEQUENCE \
+ DEFINE_FLAG(VIRTUAL), \
+ DEFINE_FLAG(FUNCTION), \
+ DEFINE_FLAG(GESTURE), \
+ DEFINE_FLAG(WAKE)
+
+// --- InputEventLookup ---
+const std::unordered_map<std::string, int> InputEventLookup::KEYCODES = {KEYCODES_SEQUENCE};
+
+const std::vector<InputEventLabel> InputEventLookup::KEY_NAMES = {KEYCODES_SEQUENCE};
+
+const std::unordered_map<std::string, int> InputEventLookup::AXES = {AXES_SEQUENCE};
+
+const std::vector<InputEventLabel> InputEventLookup::AXES_NAMES = {AXES_SEQUENCE};
+
+const std::unordered_map<std::string, int> InputEventLookup::LEDS = {LEDS_SEQUENCE};
+
+const std::unordered_map<std::string, int> InputEventLookup::FLAGS = {FLAGS_SEQUENCE};
+
+int InputEventLookup::lookupValueByLabel(const std::unordered_map<std::string, int>& map,
+ const char* literal) {
+ std::string str(literal);
+ auto it = map.find(str);
+ return it != map.end() ? it->second : 0;
+}
+
+const char* InputEventLookup::lookupLabelByValue(const std::vector<InputEventLabel>& vec,
+ int value) {
+ if (static_cast<size_t>(value) < vec.size()) {
+ return vec[value].literal;
+ }
+ return nullptr;
+}
+
+int32_t InputEventLookup::getKeyCodeByLabel(const char* label) {
+ return int32_t(lookupValueByLabel(KEYCODES, label));
+}
+
+const char* InputEventLookup::getLabelByKeyCode(int32_t keyCode) {
+ if (keyCode >= 0 && static_cast<size_t>(keyCode) < KEYCODES.size()) {
+ return lookupLabelByValue(KEY_NAMES, keyCode);
+ }
+ return nullptr;
+}
+
+uint32_t InputEventLookup::getKeyFlagByLabel(const char* label) {
+ return uint32_t(lookupValueByLabel(FLAGS, label));
+}
+
+int32_t InputEventLookup::getAxisByLabel(const char* label) {
+ return int32_t(lookupValueByLabel(AXES, label));
+}
+
+const char* InputEventLookup::getAxisLabel(int32_t axisId) {
+ return lookupLabelByValue(AXES_NAMES, axisId);
+}
+
+int32_t InputEventLookup::getLedByLabel(const char* label) {
+ return int32_t(lookupValueByLabel(LEDS, label));
+}
+
+} // namespace android
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 11af23e..56a064b 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -34,6 +34,7 @@
#include <utils/Trace.h>
#include <input/InputTransport.h>
+#include <input/NamedEnum.h>
using android::base::StringPrintf;
@@ -105,6 +106,16 @@
return true;
case Type::FOCUS:
return true;
+ case Type::CAPTURE:
+ return true;
+ case Type::DRAG:
+ return true;
+ case Type::TIMELINE:
+ const nsecs_t gpuCompletedTime =
+ body.timeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME];
+ const nsecs_t presentTime =
+ body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME];
+ return presentTime > gpuCompletedTime;
}
}
return false;
@@ -120,6 +131,12 @@
return sizeof(Header) + body.finished.size();
case Type::FOCUS:
return sizeof(Header) + body.focus.size();
+ case Type::CAPTURE:
+ return sizeof(Header) + body.capture.size();
+ case Type::DRAG:
+ return sizeof(Header) + body.drag.size();
+ case Type::TIMELINE:
+ return sizeof(Header) + body.timeline.size();
}
return sizeof(Header);
}
@@ -133,12 +150,11 @@
// Write the header
msg->header.type = header.type;
+ msg->header.seq = header.seq;
// Write the body
switch(header.type) {
case InputMessage::Type::KEY: {
- // uint32_t seq
- msg->body.key.seq = body.key.seq;
// int32_t eventId
msg->body.key.eventId = body.key.eventId;
// nsecs_t eventTime
@@ -168,8 +184,6 @@
break;
}
case InputMessage::Type::MOTION: {
- // uint32_t seq
- msg->body.motion.seq = body.motion.seq;
// int32_t eventId
msg->body.motion.eventId = body.motion.eventId;
// nsecs_t eventTime
@@ -198,14 +212,14 @@
msg->body.motion.edgeFlags = body.motion.edgeFlags;
// nsecs_t downTime
msg->body.motion.downTime = body.motion.downTime;
- // float xScale
- msg->body.motion.xScale = body.motion.xScale;
- // float yScale
- msg->body.motion.yScale = body.motion.yScale;
- // float xOffset
- msg->body.motion.xOffset = body.motion.xOffset;
- // float yOffset
- msg->body.motion.yOffset = body.motion.yOffset;
+
+ msg->body.motion.dsdx = body.motion.dsdx;
+ msg->body.motion.dtdx = body.motion.dtdx;
+ msg->body.motion.dtdy = body.motion.dtdy;
+ msg->body.motion.dsdy = body.motion.dsdy;
+ msg->body.motion.tx = body.motion.tx;
+ msg->body.motion.ty = body.motion.ty;
+
// float xPrecision
msg->body.motion.xPrecision = body.motion.xPrecision;
// float yPrecision
@@ -232,55 +246,73 @@
break;
}
case InputMessage::Type::FINISHED: {
- msg->body.finished.seq = body.finished.seq;
msg->body.finished.handled = body.finished.handled;
+ msg->body.finished.consumeTime = body.finished.consumeTime;
break;
}
case InputMessage::Type::FOCUS: {
- msg->body.focus.seq = body.focus.seq;
msg->body.focus.eventId = body.focus.eventId;
msg->body.focus.hasFocus = body.focus.hasFocus;
msg->body.focus.inTouchMode = body.focus.inTouchMode;
break;
}
+ case InputMessage::Type::CAPTURE: {
+ msg->body.capture.eventId = body.capture.eventId;
+ msg->body.capture.pointerCaptureEnabled = body.capture.pointerCaptureEnabled;
+ break;
+ }
+ case InputMessage::Type::DRAG: {
+ msg->body.drag.eventId = body.drag.eventId;
+ msg->body.drag.x = body.drag.x;
+ msg->body.drag.y = body.drag.y;
+ msg->body.drag.isExiting = body.drag.isExiting;
+ break;
+ }
+ case InputMessage::Type::TIMELINE: {
+ msg->body.timeline.eventId = body.timeline.eventId;
+ msg->body.timeline.graphicsTimeline = body.timeline.graphicsTimeline;
+ break;
+ }
}
}
// --- InputChannel ---
-sp<InputChannel> InputChannel::create(const std::string& name, android::base::unique_fd fd,
- sp<IBinder> token) {
+std::unique_ptr<InputChannel> InputChannel::create(const std::string& name,
+ android::base::unique_fd fd, sp<IBinder> token) {
const int result = fcntl(fd, F_SETFL, O_NONBLOCK);
if (result != 0) {
LOG_ALWAYS_FATAL("channel '%s' ~ Could not make socket non-blocking: %s", name.c_str(),
strerror(errno));
return nullptr;
}
- return new InputChannel(name, std::move(fd), token);
+ // using 'new' to access a non-public constructor
+ return std::unique_ptr<InputChannel>(new InputChannel(name, std::move(fd), token));
}
-InputChannel::InputChannel(const std::string& name, android::base::unique_fd fd, sp<IBinder> token)
- : mName(name), mFd(std::move(fd)), mToken(token) {
+InputChannel::InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token)
+ : mName(std::move(name)), mFd(std::move(fd)), mToken(std::move(token)) {
if (DEBUG_CHANNEL_LIFECYCLE) {
- ALOGD("Input channel constructed: name='%s', fd=%d", mName.c_str(), mFd.get());
+ ALOGD("Input channel constructed: name='%s', fd=%d", getName().c_str(), getFd().get());
}
}
InputChannel::~InputChannel() {
if (DEBUG_CHANNEL_LIFECYCLE) {
- ALOGD("Input channel destroyed: name='%s', fd=%d", mName.c_str(), mFd.get());
+ ALOGD("Input channel destroyed: name='%s', fd=%d", getName().c_str(), getFd().get());
}
}
status_t InputChannel::openInputChannelPair(const std::string& name,
- sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
+ std::unique_ptr<InputChannel>& outServerChannel,
+ std::unique_ptr<InputChannel>& outClientChannel) {
int sockets[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
status_t result = -errno;
- ALOGE("channel '%s' ~ Could not create socket pair. errno=%d",
- name.c_str(), errno);
- outServerChannel.clear();
- outClientChannel.clear();
+ ALOGE("channel '%s' ~ Could not create socket pair. errno=%s(%d)", name.c_str(),
+ strerror(errno), errno);
+ outServerChannel.reset();
+ outClientChannel.reset();
return result;
}
@@ -308,7 +340,7 @@
msg->getSanitizedCopy(&cleanMsg);
ssize_t nWrite;
do {
- nWrite = ::send(mFd.get(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
+ nWrite = ::send(getFd(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
} while (nWrite == -1 && errno == EINTR);
if (nWrite < 0) {
@@ -343,7 +375,7 @@
status_t InputChannel::receiveMessage(InputMessage* msg) {
ssize_t nRead;
do {
- nRead = ::recv(mFd.get(), msg, sizeof(InputMessage), MSG_DONTWAIT);
+ nRead = ::recv(getFd(), msg, sizeof(InputMessage), MSG_DONTWAIT);
} while (nRead == -1 && errno == EINTR);
if (nRead < 0) {
@@ -380,10 +412,43 @@
return OK;
}
-sp<InputChannel> InputChannel::dup() const {
+std::unique_ptr<InputChannel> InputChannel::dup() const {
+ base::unique_fd newFd(dupFd());
+ return InputChannel::create(getName(), std::move(newFd), getConnectionToken());
+}
+
+void InputChannel::copyTo(InputChannel& outChannel) const {
+ outChannel.mName = getName();
+ outChannel.mFd = dupFd();
+ outChannel.mToken = getConnectionToken();
+}
+
+status_t InputChannel::writeToParcel(android::Parcel* parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+ return parcel->writeStrongBinder(mToken)
+ ?: parcel->writeUtf8AsUtf16(mName) ?: parcel->writeUniqueFileDescriptor(mFd);
+}
+
+status_t InputChannel::readFromParcel(const android::Parcel* parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+ mToken = parcel->readStrongBinder();
+ return parcel->readUtf8FromUtf16(&mName) ?: parcel->readUniqueFileDescriptor(&mFd);
+}
+
+sp<IBinder> InputChannel::getConnectionToken() const {
+ return mToken;
+}
+
+base::unique_fd InputChannel::dupFd() const {
android::base::unique_fd newFd(::dup(getFd()));
if (!newFd.ok()) {
- ALOGE("Could not duplicate fd %i for channel %s: %s", getFd(), mName.c_str(),
+ ALOGE("Could not duplicate fd %i for channel %s: %s", getFd().get(), getName().c_str(),
strerror(errno));
const bool hitFdLimit = errno == EMFILE || errno == ENFILE;
// If this process is out of file descriptors, then throwing that might end up exploding
@@ -392,47 +457,14 @@
// Other failures could be client errors, so we still propagate those back to the caller.
LOG_ALWAYS_FATAL_IF(hitFdLimit, "Too many open files, could not duplicate input channel %s",
getName().c_str());
- return nullptr;
+ return {};
}
- return InputChannel::create(mName, std::move(newFd), mToken);
-}
-
-status_t InputChannel::write(Parcel& out) const {
- status_t s = out.writeCString(getName().c_str());
- if (s != OK) {
- return s;
- }
-
- s = out.writeStrongBinder(mToken);
- if (s != OK) {
- return s;
- }
-
- s = out.writeUniqueFileDescriptor(mFd);
- return s;
-}
-
-sp<InputChannel> InputChannel::read(const Parcel& from) {
- std::string name = from.readCString();
- sp<IBinder> token = from.readStrongBinder();
- android::base::unique_fd rawFd;
- status_t fdResult = from.readUniqueFileDescriptor(&rawFd);
- if (fdResult != OK) {
- return nullptr;
- }
-
- return InputChannel::create(name, std::move(rawFd), token);
-}
-
-sp<IBinder> InputChannel::getConnectionToken() const {
- return mToken;
+ return newFd;
}
// --- InputPublisher ---
-InputPublisher::InputPublisher(const sp<InputChannel>& channel) :
- mChannel(channel) {
-}
+InputPublisher::InputPublisher(const std::shared_ptr<InputChannel>& channel) : mChannel(channel) {}
InputPublisher::~InputPublisher() {
}
@@ -463,7 +495,7 @@
InputMessage msg;
msg.header.type = InputMessage::Type::KEY;
- msg.body.key.seq = seq;
+ msg.header.seq = seq;
msg.body.key.eventId = eventId;
msg.body.key.deviceId = deviceId;
msg.body.key.source = source;
@@ -484,10 +516,10 @@
uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source, int32_t displayId,
std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags,
int32_t edgeFlags, int32_t metaState, int32_t buttonState,
- MotionClassification classification, float xScale, float yScale, float xOffset,
- float yOffset, float xPrecision, float yPrecision, float xCursorPosition,
- float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount,
- const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) {
+ MotionClassification classification, const ui::Transform& transform, float xPrecision,
+ float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime,
+ nsecs_t eventTime, uint32_t pointerCount, const PointerProperties* pointerProperties,
+ const PointerCoords* pointerCoords) {
if (ATRACE_ENABLED()) {
std::string message = StringPrintf(
"publishMotionEvent(inputChannel=%s, action=%" PRId32 ")",
@@ -495,17 +527,18 @@
ATRACE_NAME(message.c_str());
}
if (DEBUG_TRANSPORT_ACTIONS) {
+ std::string transformString;
+ transform.dump(transformString, "transform", " ");
ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, "
"displayId=%" PRId32 ", "
"action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, "
- "metaState=0x%x, buttonState=0x%x, classification=%s, xScale=%.1f, yScale=%.1f, "
- "xOffset=%.1f, yOffset=%.1f, "
+ "metaState=0x%x, buttonState=0x%x, classification=%s,"
"xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", "
- "pointerCount=%" PRIu32,
+ "pointerCount=%" PRIu32 " \n%s",
mChannel->getName().c_str(), seq, deviceId, source, displayId, action, actionButton,
flags, edgeFlags, metaState, buttonState,
- motionClassificationToString(classification), xScale, yScale, xOffset, yOffset,
- xPrecision, yPrecision, downTime, eventTime, pointerCount);
+ motionClassificationToString(classification), xPrecision, yPrecision, downTime,
+ eventTime, pointerCount, transformString.c_str());
}
if (!seq) {
@@ -521,7 +554,7 @@
InputMessage msg;
msg.header.type = InputMessage::Type::MOTION;
- msg.body.motion.seq = seq;
+ msg.header.seq = seq;
msg.body.motion.eventId = eventId;
msg.body.motion.deviceId = deviceId;
msg.body.motion.source = source;
@@ -534,10 +567,12 @@
msg.body.motion.metaState = metaState;
msg.body.motion.buttonState = buttonState;
msg.body.motion.classification = classification;
- msg.body.motion.xScale = xScale;
- msg.body.motion.yScale = yScale;
- msg.body.motion.xOffset = xOffset;
- msg.body.motion.yOffset = yOffset;
+ msg.body.motion.dsdx = transform.dsdx();
+ msg.body.motion.dtdx = transform.dtdx();
+ msg.body.motion.dtdy = transform.dtdy();
+ msg.body.motion.dsdy = transform.dsdy();
+ msg.body.motion.tx = transform.tx();
+ msg.body.motion.ty = transform.ty();
msg.body.motion.xPrecision = xPrecision;
msg.body.motion.yPrecision = yPrecision;
msg.body.motion.xCursorPosition = xCursorPosition;
@@ -565,41 +600,83 @@
InputMessage msg;
msg.header.type = InputMessage::Type::FOCUS;
- msg.body.focus.seq = seq;
+ msg.header.seq = seq;
msg.body.focus.eventId = eventId;
- msg.body.focus.hasFocus = hasFocus ? 1 : 0;
- msg.body.focus.inTouchMode = inTouchMode ? 1 : 0;
+ msg.body.focus.hasFocus = hasFocus;
+ msg.body.focus.inTouchMode = inTouchMode;
return mChannel->sendMessage(&msg);
}
-status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) {
+status_t InputPublisher::publishCaptureEvent(uint32_t seq, int32_t eventId,
+ bool pointerCaptureEnabled) {
+ if (ATRACE_ENABLED()) {
+ std::string message =
+ StringPrintf("publishCaptureEvent(inputChannel=%s, pointerCaptureEnabled=%s)",
+ mChannel->getName().c_str(), toString(pointerCaptureEnabled));
+ ATRACE_NAME(message.c_str());
+ }
+
+ InputMessage msg;
+ msg.header.type = InputMessage::Type::CAPTURE;
+ msg.header.seq = seq;
+ msg.body.capture.eventId = eventId;
+ msg.body.capture.pointerCaptureEnabled = pointerCaptureEnabled;
+ return mChannel->sendMessage(&msg);
+}
+
+status_t InputPublisher::publishDragEvent(uint32_t seq, int32_t eventId, float x, float y,
+ bool isExiting) {
+ if (ATRACE_ENABLED()) {
+ std::string message =
+ StringPrintf("publishDragEvent(inputChannel=%s, x=%f, y=%f, isExiting=%s)",
+ mChannel->getName().c_str(), x, y, toString(isExiting));
+ ATRACE_NAME(message.c_str());
+ }
+
+ InputMessage msg;
+ msg.header.type = InputMessage::Type::DRAG;
+ msg.header.seq = seq;
+ msg.body.drag.eventId = eventId;
+ msg.body.drag.isExiting = isExiting;
+ msg.body.drag.x = x;
+ msg.body.drag.y = y;
+ return mChannel->sendMessage(&msg);
+}
+
+android::base::Result<InputPublisher::ConsumerResponse> InputPublisher::receiveConsumerResponse() {
if (DEBUG_TRANSPORT_ACTIONS) {
- ALOGD("channel '%s' publisher ~ receiveFinishedSignal", mChannel->getName().c_str());
+ ALOGD("channel '%s' publisher ~ %s", mChannel->getName().c_str(), __func__);
}
InputMessage msg;
status_t result = mChannel->receiveMessage(&msg);
if (result) {
- *outSeq = 0;
- *outHandled = false;
- return result;
+ return android::base::Error(result);
}
- if (msg.header.type != InputMessage::Type::FINISHED) {
- ALOGE("channel '%s' publisher ~ Received unexpected message of type %d from consumer",
- mChannel->getName().c_str(), msg.header.type);
- return UNKNOWN_ERROR;
+ if (msg.header.type == InputMessage::Type::FINISHED) {
+ return Finished{
+ .seq = msg.header.seq,
+ .handled = msg.body.finished.handled,
+ .consumeTime = msg.body.finished.consumeTime,
+ };
}
- *outSeq = msg.body.finished.seq;
- *outHandled = msg.body.finished.handled == 1;
- return OK;
+
+ if (msg.header.type == InputMessage::Type::TIMELINE) {
+ return Timeline{
+ .inputEventId = msg.body.timeline.eventId,
+ .graphicsTimeline = msg.body.timeline.graphicsTimeline,
+ };
+ }
+
+ ALOGE("channel '%s' publisher ~ Received unexpected %s message from consumer",
+ mChannel->getName().c_str(), NamedEnum::string(msg.header.type).c_str());
+ return android::base::Error(UNKNOWN_ERROR);
}
// --- InputConsumer ---
-InputConsumer::InputConsumer(const sp<InputChannel>& channel) :
- mResampleTouch(isTouchResamplingEnabled()),
- mChannel(channel), mMsgDeferred(false) {
-}
+InputConsumer::InputConsumer(const std::shared_ptr<InputChannel>& channel)
+ : mResampleTouch(isTouchResamplingEnabled()), mChannel(channel), mMsgDeferred(false) {}
InputConsumer::~InputConsumer() {
}
@@ -628,6 +705,9 @@
} else {
// Receive a fresh message.
status_t result = mChannel->receiveMessage(&mMsg);
+ if (result == OK) {
+ mConsumeTimes.emplace(mMsg.header.seq, systemTime(SYSTEM_TIME_MONOTONIC));
+ }
if (result) {
// Consume the next batched event unless batches are being held for later.
if (consumeBatches || result != WOULD_BLOCK) {
@@ -650,7 +730,7 @@
if (!keyEvent) return NO_MEMORY;
initializeKeyEvent(keyEvent, &mMsg);
- *outSeq = mMsg.body.key.seq;
+ *outSeq = mMsg.header.seq;
*outEvent = keyEvent;
if (DEBUG_TRANSPORT_ACTIONS) {
ALOGD("channel '%s' consumer ~ consumed key event, seq=%u",
@@ -662,9 +742,9 @@
case InputMessage::Type::MOTION: {
ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source);
if (batchIndex >= 0) {
- Batch& batch = mBatches.editItemAt(batchIndex);
+ Batch& batch = mBatches[batchIndex];
if (canAddSample(batch, &mMsg)) {
- batch.samples.push(mMsg);
+ batch.samples.push_back(mMsg);
if (DEBUG_TRANSPORT_ACTIONS) {
ALOGD("channel '%s' consumer ~ appended to batch event",
mChannel->getName().c_str());
@@ -675,18 +755,18 @@
// No need to process events that we are going to cancel anyways
const size_t count = batch.samples.size();
for (size_t i = 0; i < count; i++) {
- const InputMessage& msg = batch.samples.itemAt(i);
- sendFinishedSignal(msg.body.motion.seq, false);
+ const InputMessage& msg = batch.samples[i];
+ sendFinishedSignal(msg.header.seq, false);
}
- batch.samples.removeItemsAt(0, count);
- mBatches.removeAt(batchIndex);
+ batch.samples.erase(batch.samples.begin(), batch.samples.begin() + count);
+ mBatches.erase(mBatches.begin() + batchIndex);
} else {
// We cannot append to the batch in progress, so we need to consume
// the previous batch right now and defer the new message until later.
mMsgDeferred = true;
status_t result = consumeSamples(factory, batch, batch.samples.size(),
outSeq, outEvent);
- mBatches.removeAt(batchIndex);
+ mBatches.erase(mBatches.begin() + batchIndex);
if (result) {
return result;
}
@@ -702,9 +782,9 @@
// Start a new batch if needed.
if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE ||
mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
- mBatches.push();
- Batch& batch = mBatches.editTop();
- batch.samples.push(mMsg);
+ Batch batch;
+ batch.samples.push_back(mMsg);
+ mBatches.push_back(batch);
if (DEBUG_TRANSPORT_ACTIONS) {
ALOGD("channel '%s' consumer ~ started batch event",
mChannel->getName().c_str());
@@ -717,7 +797,7 @@
updateTouchState(mMsg);
initializeMotionEvent(motionEvent, &mMsg);
- *outSeq = mMsg.body.motion.seq;
+ *outSeq = mMsg.header.seq;
*outEvent = motionEvent;
if (DEBUG_TRANSPORT_ACTIONS) {
@@ -727,9 +807,11 @@
break;
}
- case InputMessage::Type::FINISHED: {
- LOG_ALWAYS_FATAL("Consumed a FINISHED message, which should never be seen by "
- "InputConsumer!");
+ case InputMessage::Type::FINISHED:
+ case InputMessage::Type::TIMELINE: {
+ LOG_ALWAYS_FATAL("Consumed a %s message, which should never be seen by "
+ "InputConsumer!",
+ NamedEnum::string(mMsg.header.type).c_str());
break;
}
@@ -738,10 +820,30 @@
if (!focusEvent) return NO_MEMORY;
initializeFocusEvent(focusEvent, &mMsg);
- *outSeq = mMsg.body.focus.seq;
+ *outSeq = mMsg.header.seq;
*outEvent = focusEvent;
break;
}
+
+ case InputMessage::Type::CAPTURE: {
+ CaptureEvent* captureEvent = factory->createCaptureEvent();
+ if (!captureEvent) return NO_MEMORY;
+
+ initializeCaptureEvent(captureEvent, &mMsg);
+ *outSeq = mMsg.header.seq;
+ *outEvent = captureEvent;
+ break;
+ }
+
+ case InputMessage::Type::DRAG: {
+ DragEvent* dragEvent = factory->createDragEvent();
+ if (!dragEvent) return NO_MEMORY;
+
+ initializeDragEvent(dragEvent, &mMsg);
+ *outSeq = mMsg.header.seq;
+ *outEvent = dragEvent;
+ break;
+ }
}
}
return OK;
@@ -752,10 +854,10 @@
status_t result;
for (size_t i = mBatches.size(); i > 0; ) {
i--;
- Batch& batch = mBatches.editItemAt(i);
+ Batch& batch = mBatches[i];
if (frameTime < 0) {
result = consumeSamples(factory, batch, batch.samples.size(), outSeq, outEvent);
- mBatches.removeAt(i);
+ mBatches.erase(mBatches.begin() + i);
return result;
}
@@ -770,11 +872,11 @@
result = consumeSamples(factory, batch, split + 1, outSeq, outEvent);
const InputMessage* next;
- if (batch.samples.isEmpty()) {
- mBatches.removeAt(i);
+ if (batch.samples.empty()) {
+ mBatches.erase(mBatches.begin() + i);
next = nullptr;
} else {
- next = &batch.samples.itemAt(0);
+ next = &batch.samples[0];
}
if (!result && mResampleTouch) {
resampleTouchState(sampleTime, static_cast<MotionEvent*>(*outEvent), next);
@@ -792,20 +894,20 @@
uint32_t chain = 0;
for (size_t i = 0; i < count; i++) {
- InputMessage& msg = batch.samples.editItemAt(i);
+ InputMessage& msg = batch.samples[i];
updateTouchState(msg);
if (i) {
SeqChain seqChain;
- seqChain.seq = msg.body.motion.seq;
+ seqChain.seq = msg.header.seq;
seqChain.chain = chain;
- mSeqChains.push(seqChain);
+ mSeqChains.push_back(seqChain);
addSample(motionEvent, &msg);
} else {
initializeMotionEvent(motionEvent, &msg);
}
- chain = msg.body.motion.seq;
+ chain = msg.header.seq;
}
- batch.samples.removeItemsAt(0, count);
+ batch.samples.erase(batch.samples.begin(), batch.samples.begin() + count);
*outSeq = chain;
*outEvent = motionEvent;
@@ -827,10 +929,10 @@
case AMOTION_EVENT_ACTION_DOWN: {
ssize_t index = findTouchState(deviceId, source);
if (index < 0) {
- mTouchStates.push();
+ mTouchStates.push_back({});
index = mTouchStates.size() - 1;
}
- TouchState& touchState = mTouchStates.editItemAt(index);
+ TouchState& touchState = mTouchStates[index];
touchState.initialize(deviceId, source);
touchState.addHistory(msg);
break;
@@ -839,7 +941,7 @@
case AMOTION_EVENT_ACTION_MOVE: {
ssize_t index = findTouchState(deviceId, source);
if (index >= 0) {
- TouchState& touchState = mTouchStates.editItemAt(index);
+ TouchState& touchState = mTouchStates[index];
touchState.addHistory(msg);
rewriteMessage(touchState, msg);
}
@@ -849,7 +951,7 @@
case AMOTION_EVENT_ACTION_POINTER_DOWN: {
ssize_t index = findTouchState(deviceId, source);
if (index >= 0) {
- TouchState& touchState = mTouchStates.editItemAt(index);
+ TouchState& touchState = mTouchStates[index];
touchState.lastResample.idBits.clearBit(msg.body.motion.getActionId());
rewriteMessage(touchState, msg);
}
@@ -859,7 +961,7 @@
case AMOTION_EVENT_ACTION_POINTER_UP: {
ssize_t index = findTouchState(deviceId, source);
if (index >= 0) {
- TouchState& touchState = mTouchStates.editItemAt(index);
+ TouchState& touchState = mTouchStates[index];
rewriteMessage(touchState, msg);
touchState.lastResample.idBits.clearBit(msg.body.motion.getActionId());
}
@@ -869,7 +971,7 @@
case AMOTION_EVENT_ACTION_SCROLL: {
ssize_t index = findTouchState(deviceId, source);
if (index >= 0) {
- TouchState& touchState = mTouchStates.editItemAt(index);
+ TouchState& touchState = mTouchStates[index];
rewriteMessage(touchState, msg);
}
break;
@@ -879,9 +981,9 @@
case AMOTION_EVENT_ACTION_CANCEL: {
ssize_t index = findTouchState(deviceId, source);
if (index >= 0) {
- TouchState& touchState = mTouchStates.editItemAt(index);
+ TouchState& touchState = mTouchStates[index];
rewriteMessage(touchState, msg);
- mTouchStates.removeAt(index);
+ mTouchStates.erase(mTouchStates.begin() + index);
}
break;
}
@@ -938,7 +1040,7 @@
return;
}
- TouchState& touchState = mTouchStates.editItemAt(index);
+ TouchState& touchState = mTouchStates[index];
if (touchState.historySize < 1) {
#if DEBUG_RESAMPLING
ALOGD("Not resampled, no history for device.");
@@ -1084,11 +1186,11 @@
size_t chainIndex = 0;
for (size_t i = seqChainCount; i > 0; ) {
i--;
- const SeqChain& seqChain = mSeqChains.itemAt(i);
+ const SeqChain& seqChain = mSeqChains[i];
if (seqChain.seq == currentSeq) {
currentSeq = seqChain.chain;
chainSeqs[chainIndex++] = currentSeq;
- mSeqChains.removeAt(i);
+ mSeqChains.erase(mSeqChains.begin() + i);
}
}
status_t status = OK;
@@ -1102,7 +1204,7 @@
SeqChain seqChain;
seqChain.seq = chainIndex != 0 ? chainSeqs[chainIndex - 1] : seq;
seqChain.chain = chainSeqs[chainIndex];
- mSeqChains.push(seqChain);
+ mSeqChains.push_back(seqChain);
if (!chainIndex) break;
chainIndex--;
}
@@ -1114,12 +1216,51 @@
return sendUnchainedFinishedSignal(seq, handled);
}
+status_t InputConsumer::sendTimeline(int32_t inputEventId,
+ std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline) {
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' consumer ~ sendTimeline: inputEventId=%" PRId32
+ ", gpuCompletedTime=%" PRId64 ", presentTime=%" PRId64,
+ mChannel->getName().c_str(), inputEventId,
+ graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME],
+ graphicsTimeline[GraphicsTimeline::PRESENT_TIME]);
+ }
+
+ InputMessage msg;
+ msg.header.type = InputMessage::Type::TIMELINE;
+ msg.header.seq = 0;
+ msg.body.timeline.eventId = inputEventId;
+ msg.body.timeline.graphicsTimeline = std::move(graphicsTimeline);
+ return mChannel->sendMessage(&msg);
+}
+
+nsecs_t InputConsumer::getConsumeTime(uint32_t seq) const {
+ auto it = mConsumeTimes.find(seq);
+ // Consume time will be missing if either 'finishInputEvent' is called twice, or if it was
+ // called for the wrong (synthetic?) input event. Either way, it is a bug that should be fixed.
+ LOG_ALWAYS_FATAL_IF(it == mConsumeTimes.end(), "Could not find consume time for seq=%" PRIu32,
+ seq);
+ return it->second;
+}
+
+void InputConsumer::popConsumeTime(uint32_t seq) {
+ mConsumeTimes.erase(seq);
+}
+
status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) {
InputMessage msg;
msg.header.type = InputMessage::Type::FINISHED;
- msg.body.finished.seq = seq;
- msg.body.finished.handled = handled ? 1 : 0;
- return mChannel->sendMessage(&msg);
+ msg.header.seq = seq;
+ msg.body.finished.handled = handled;
+ msg.body.finished.consumeTime = getConsumeTime(seq);
+ status_t result = mChannel->sendMessage(&msg);
+ if (result == OK) {
+ // Remove the consume time if the socket write succeeded. We will not need to ack this
+ // message anymore. If the socket write did not succeed, we will try again and will still
+ // need consume time.
+ popConsumeTime(seq);
+ }
+ return result;
}
bool InputConsumer::hasDeferredEvent() const {
@@ -1127,23 +1268,23 @@
}
bool InputConsumer::hasPendingBatch() const {
- return !mBatches.isEmpty();
+ return !mBatches.empty();
}
int32_t InputConsumer::getPendingBatchSource() const {
- if (mBatches.isEmpty()) {
+ if (mBatches.empty()) {
return AINPUT_SOURCE_CLASS_NONE;
}
- const Batch& batch = mBatches.itemAt(0);
- const InputMessage& head = batch.samples.itemAt(0);
+ const Batch& batch = mBatches[0];
+ const InputMessage& head = batch.samples[0];
return head.body.motion.source;
}
ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const {
for (size_t i = 0; i < mBatches.size(); i++) {
- const Batch& batch = mBatches.itemAt(i);
- const InputMessage& head = batch.samples.itemAt(0);
+ const Batch& batch = mBatches[i];
+ const InputMessage& head = batch.samples[0];
if (head.body.motion.deviceId == deviceId && head.body.motion.source == source) {
return i;
}
@@ -1153,7 +1294,7 @@
ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const {
for (size_t i = 0; i < mTouchStates.size(); i++) {
- const TouchState& touchState = mTouchStates.itemAt(i);
+ const TouchState& touchState = mTouchStates[i];
if (touchState.deviceId == deviceId && touchState.source == source) {
return i;
}
@@ -1170,8 +1311,17 @@
}
void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) {
- event->initialize(msg->body.focus.eventId, msg->body.focus.hasFocus == 1,
- msg->body.focus.inTouchMode == 1);
+ event->initialize(msg->body.focus.eventId, msg->body.focus.hasFocus,
+ msg->body.focus.inTouchMode);
+}
+
+void InputConsumer::initializeCaptureEvent(CaptureEvent* event, const InputMessage* msg) {
+ event->initialize(msg->body.capture.eventId, msg->body.capture.pointerCaptureEnabled);
+}
+
+void InputConsumer::initializeDragEvent(DragEvent* event, const InputMessage* msg) {
+ event->initialize(msg->body.drag.eventId, msg->body.drag.x, msg->body.drag.y,
+ msg->body.drag.isExiting);
}
void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) {
@@ -1183,16 +1333,18 @@
pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords);
}
+ ui::Transform transform;
+ transform.set({msg->body.motion.dsdx, msg->body.motion.dtdx, msg->body.motion.tx,
+ msg->body.motion.dtdy, msg->body.motion.dsdy, msg->body.motion.ty, 0, 0, 1});
event->initialize(msg->body.motion.eventId, msg->body.motion.deviceId, msg->body.motion.source,
msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action,
msg->body.motion.actionButton, msg->body.motion.flags,
msg->body.motion.edgeFlags, msg->body.motion.metaState,
- msg->body.motion.buttonState, msg->body.motion.classification,
- msg->body.motion.xScale, msg->body.motion.yScale, msg->body.motion.xOffset,
- msg->body.motion.yOffset, msg->body.motion.xPrecision,
- msg->body.motion.yPrecision, msg->body.motion.xCursorPosition,
- msg->body.motion.yCursorPosition, msg->body.motion.downTime,
- msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords);
+ msg->body.motion.buttonState, msg->body.motion.classification, transform,
+ msg->body.motion.xPrecision, msg->body.motion.yPrecision,
+ msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition,
+ msg->body.motion.downTime, msg->body.motion.eventTime, pointerCount,
+ pointerProperties, pointerCoords);
}
void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) {
@@ -1207,7 +1359,7 @@
}
bool InputConsumer::canAddSample(const Batch& batch, const InputMessage *msg) {
- const InputMessage& head = batch.samples.itemAt(0);
+ const InputMessage& head = batch.samples[0];
uint32_t pointerCount = msg->body.motion.pointerCount;
if (head.body.motion.pointerCount != pointerCount
|| head.body.motion.action != msg->body.motion.action) {
@@ -1225,11 +1377,106 @@
ssize_t InputConsumer::findSampleNoLaterThan(const Batch& batch, nsecs_t time) {
size_t numSamples = batch.samples.size();
size_t index = 0;
- while (index < numSamples
- && batch.samples.itemAt(index).body.motion.eventTime <= time) {
+ while (index < numSamples && batch.samples[index].body.motion.eventTime <= time) {
index += 1;
}
return ssize_t(index) - 1;
}
+std::string InputConsumer::dump() const {
+ std::string out;
+ out = out + "mResampleTouch = " + toString(mResampleTouch) + "\n";
+ out = out + "mChannel = " + mChannel->getName() + "\n";
+ out = out + "mMsgDeferred: " + toString(mMsgDeferred) + "\n";
+ if (mMsgDeferred) {
+ out = out + "mMsg : " + NamedEnum::string(mMsg.header.type) + "\n";
+ }
+ out += "Batches:\n";
+ for (const Batch& batch : mBatches) {
+ out += " Batch:\n";
+ for (const InputMessage& msg : batch.samples) {
+ out += android::base::StringPrintf(" Message %" PRIu32 ": %s ", msg.header.seq,
+ NamedEnum::string(msg.header.type).c_str());
+ switch (msg.header.type) {
+ case InputMessage::Type::KEY: {
+ out += android::base::StringPrintf("action=%s keycode=%" PRId32,
+ KeyEvent::actionToString(
+ msg.body.key.action),
+ msg.body.key.keyCode);
+ break;
+ }
+ case InputMessage::Type::MOTION: {
+ out = out + "action=" + MotionEvent::actionToString(msg.body.motion.action);
+ for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) {
+ const float x = msg.body.motion.pointers[i].coords.getX();
+ const float y = msg.body.motion.pointers[i].coords.getY();
+ out += android::base::StringPrintf("\n Pointer %" PRIu32
+ " : x=%.1f y=%.1f",
+ i, x, y);
+ }
+ break;
+ }
+ case InputMessage::Type::FINISHED: {
+ out += android::base::StringPrintf("handled=%s, consumeTime=%" PRId64,
+ toString(msg.body.finished.handled),
+ msg.body.finished.consumeTime);
+ break;
+ }
+ case InputMessage::Type::FOCUS: {
+ out += android::base::StringPrintf("hasFocus=%s inTouchMode=%s",
+ toString(msg.body.focus.hasFocus),
+ toString(msg.body.focus.inTouchMode));
+ break;
+ }
+ case InputMessage::Type::CAPTURE: {
+ out += android::base::StringPrintf("hasCapture=%s",
+ toString(msg.body.capture
+ .pointerCaptureEnabled));
+ break;
+ }
+ case InputMessage::Type::DRAG: {
+ out += android::base::StringPrintf("x=%.1f y=%.1f, isExiting=%s",
+ msg.body.drag.x, msg.body.drag.y,
+ toString(msg.body.drag.isExiting));
+ break;
+ }
+ case InputMessage::Type::TIMELINE: {
+ const nsecs_t gpuCompletedTime =
+ msg.body.timeline
+ .graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME];
+ const nsecs_t presentTime =
+ msg.body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME];
+ out += android::base::StringPrintf("inputEventId=%" PRId32
+ ", gpuCompletedTime=%" PRId64
+ ", presentTime=%" PRId64,
+ msg.body.timeline.eventId, gpuCompletedTime,
+ presentTime);
+ break;
+ }
+ }
+ out += "\n";
+ }
+ }
+ if (mBatches.empty()) {
+ out += " <empty>\n";
+ }
+ out += "mSeqChains:\n";
+ for (const SeqChain& chain : mSeqChains) {
+ out += android::base::StringPrintf(" chain: seq = %" PRIu32 " chain=%" PRIu32, chain.seq,
+ chain.chain);
+ }
+ if (mSeqChains.empty()) {
+ out += " <empty>\n";
+ }
+ out += "mConsumeTimes:\n";
+ for (const auto& [seq, consumeTime] : mConsumeTimes) {
+ out += android::base::StringPrintf(" seq = %" PRIu32 " consumeTime = %" PRId64, seq,
+ consumeTime);
+ }
+ if (mConsumeTimes.empty()) {
+ out += " <empty>\n";
+ }
+ return out;
+}
+
} // namespace android
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
index 85a2015..8546bbb 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -14,20 +14,20 @@
* limitations under the License.
*/
+#include <type_traits>
#define LOG_TAG "InputWindow"
#define LOG_NDEBUG 0
+#include <android-base/stringprintf.h>
#include <binder/Parcel.h>
-#include <input/InputWindow.h>
#include <input/InputTransport.h>
+#include <input/InputWindow.h>
#include <log/log.h>
-#include <ui/Rect.h>
-#include <ui/Region.h>
-
namespace android {
+
// --- InputWindowInfo ---
void InputWindowInfo::addTouchableRegion(const Rect& region) {
touchableRegion.orSelf(region);
@@ -42,22 +42,8 @@
&& y >= frameTop && y < frameBottom;
}
-// TODO(b/155781676): Remove and replace call points with trustedOverlay when that is ready.
-bool InputWindowInfo::isTrustedOverlay() const {
- return layoutParamsType == TYPE_INPUT_METHOD || layoutParamsType == TYPE_INPUT_METHOD_DIALOG ||
- layoutParamsType == TYPE_MAGNIFICATION_OVERLAY || layoutParamsType == TYPE_STATUS_BAR ||
- layoutParamsType == TYPE_NOTIFICATION_SHADE ||
- layoutParamsType == TYPE_NAVIGATION_BAR ||
- layoutParamsType == TYPE_NAVIGATION_BAR_PANEL ||
- layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY ||
- layoutParamsType == TYPE_DOCK_DIVIDER ||
- layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY ||
- layoutParamsType == TYPE_INPUT_CONSUMER ||
- layoutParamsType == TYPE_TRUSTED_APPLICATION_OVERLAY;
-}
-
bool InputWindowInfo::supportsSplitTouch() const {
- return layoutParamsFlags & FLAG_SPLIT_TOUCH;
+ return flags.test(Flag::SPLIT_TOUCH);
}
bool InputWindowInfo::overlaps(const InputWindowInfo* other) const {
@@ -65,94 +51,158 @@
&& frameTop < other->frameBottom && frameBottom > other->frameTop;
}
-status_t InputWindowInfo::write(Parcel& output) const {
+bool InputWindowInfo::operator==(const InputWindowInfo& info) const {
+ return info.token == token && info.id == id && info.name == name && info.flags == flags &&
+ info.type == type && info.dispatchingTimeout == dispatchingTimeout &&
+ info.frameLeft == frameLeft && info.frameTop == frameTop &&
+ info.frameRight == frameRight && info.frameBottom == frameBottom &&
+ info.surfaceInset == surfaceInset && info.globalScaleFactor == globalScaleFactor &&
+ info.transform == transform && info.touchableRegion.hasSameRects(touchableRegion) &&
+ info.visible == visible && info.trustedOverlay == trustedOverlay &&
+ info.focusable == focusable && info.touchOcclusionMode == touchOcclusionMode &&
+ info.hasWallpaper == hasWallpaper && info.paused == paused &&
+ info.ownerPid == ownerPid && info.ownerUid == ownerUid &&
+ info.packageName == packageName && info.inputFeatures == inputFeatures &&
+ info.displayId == displayId && info.portalToDisplayId == portalToDisplayId &&
+ info.replaceTouchableRegionWithCrop == replaceTouchableRegionWithCrop &&
+ info.applicationInfo == applicationInfo;
+}
+
+status_t InputWindowInfo::writeToParcel(android::Parcel* parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
if (name.empty()) {
- output.writeInt32(0);
+ parcel->writeInt32(0);
return OK;
}
- output.writeInt32(1);
- status_t s = output.writeStrongBinder(token);
- if (s != OK) return s;
+ parcel->writeInt32(1);
- output.writeInt32(id);
- output.writeString8(String8(name.c_str()));
- output.writeInt32(layoutParamsFlags);
- output.writeInt32(layoutParamsType);
- output.writeInt64(dispatchingTimeout);
- output.writeInt32(frameLeft);
- output.writeInt32(frameTop);
- output.writeInt32(frameRight);
- output.writeInt32(frameBottom);
- output.writeInt32(surfaceInset);
- output.writeFloat(globalScaleFactor);
- output.writeFloat(windowXScale);
- output.writeFloat(windowYScale);
- output.writeBool(visible);
- output.writeBool(canReceiveKeys);
- output.writeBool(hasFocus);
- output.writeBool(hasWallpaper);
- output.writeBool(paused);
- output.writeInt32(ownerPid);
- output.writeInt32(ownerUid);
- output.writeInt32(inputFeatures);
- output.writeInt32(displayId);
- output.writeInt32(portalToDisplayId);
- applicationInfo.write(output);
- output.write(touchableRegion);
- output.writeBool(replaceTouchableRegionWithCrop);
- output.writeStrongBinder(touchableRegionCropHandle.promote());
- return OK;
+ // clang-format off
+ status_t status = parcel->writeStrongBinder(token) ?:
+ parcel->writeInt64(dispatchingTimeout.count()) ?:
+ parcel->writeInt32(id) ?:
+ parcel->writeUtf8AsUtf16(name) ?:
+ parcel->writeInt32(flags.get()) ?:
+ parcel->writeInt32(static_cast<std::underlying_type_t<InputWindowInfo::Type>>(type)) ?:
+ parcel->writeInt32(frameLeft) ?:
+ parcel->writeInt32(frameTop) ?:
+ parcel->writeInt32(frameRight) ?:
+ parcel->writeInt32(frameBottom) ?:
+ parcel->writeInt32(surfaceInset) ?:
+ parcel->writeFloat(globalScaleFactor) ?:
+ parcel->writeFloat(alpha) ?:
+ parcel->writeFloat(transform.dsdx()) ?:
+ parcel->writeFloat(transform.dtdx()) ?:
+ parcel->writeFloat(transform.tx()) ?:
+ parcel->writeFloat(transform.dtdy()) ?:
+ parcel->writeFloat(transform.dsdy()) ?:
+ parcel->writeFloat(transform.ty()) ?:
+ parcel->writeBool(visible) ?:
+ parcel->writeBool(focusable) ?:
+ parcel->writeBool(hasWallpaper) ?:
+ parcel->writeBool(paused) ?:
+ parcel->writeBool(trustedOverlay) ?:
+ parcel->writeInt32(static_cast<int32_t>(touchOcclusionMode)) ?:
+ parcel->writeInt32(ownerPid) ?:
+ parcel->writeInt32(ownerUid) ?:
+ parcel->writeUtf8AsUtf16(packageName) ?:
+ parcel->writeInt32(inputFeatures.get()) ?:
+ parcel->writeInt32(displayId) ?:
+ parcel->writeInt32(portalToDisplayId) ?:
+ applicationInfo.writeToParcel(parcel) ?:
+ parcel->write(touchableRegion) ?:
+ parcel->writeBool(replaceTouchableRegionWithCrop) ?:
+ parcel->writeStrongBinder(touchableRegionCropHandle.promote());
+ // clang-format on
+ return status;
}
-InputWindowInfo InputWindowInfo::read(const Parcel& from) {
- InputWindowInfo ret;
-
- if (from.readInt32() == 0) {
- return ret;
+status_t InputWindowInfo::readFromParcel(const android::Parcel* parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+ if (parcel->readInt32() == 0) {
+ return OK;
}
- ret.token = from.readStrongBinder();
- ret.id = from.readInt32();
- ret.name = from.readString8().c_str();
- ret.layoutParamsFlags = from.readInt32();
- ret.layoutParamsType = from.readInt32();
- ret.dispatchingTimeout = from.readInt64();
- ret.frameLeft = from.readInt32();
- ret.frameTop = from.readInt32();
- ret.frameRight = from.readInt32();
- ret.frameBottom = from.readInt32();
- ret.surfaceInset = from.readInt32();
- ret.globalScaleFactor = from.readFloat();
- ret.windowXScale = from.readFloat();
- ret.windowYScale = from.readFloat();
- ret.visible = from.readBool();
- ret.canReceiveKeys = from.readBool();
- ret.hasFocus = from.readBool();
- ret.hasWallpaper = from.readBool();
- ret.paused = from.readBool();
- ret.ownerPid = from.readInt32();
- ret.ownerUid = from.readInt32();
- ret.inputFeatures = from.readInt32();
- ret.displayId = from.readInt32();
- ret.portalToDisplayId = from.readInt32();
- ret.applicationInfo = InputApplicationInfo::read(from);
- from.read(ret.touchableRegion);
- ret.replaceTouchableRegionWithCrop = from.readBool();
- ret.touchableRegionCropHandle = from.readStrongBinder();
+ token = parcel->readStrongBinder();
+ dispatchingTimeout = static_cast<decltype(dispatchingTimeout)>(parcel->readInt64());
+ status_t status = parcel->readInt32(&id) ?: parcel->readUtf8FromUtf16(&name);
+ if (status != OK) {
+ return status;
+ }
- return ret;
-}
+ flags = Flags<Flag>(parcel->readInt32());
+ type = static_cast<Type>(parcel->readInt32());
+ float dsdx, dtdx, tx, dtdy, dsdy, ty;
+ int32_t touchOcclusionModeInt;
+ // clang-format off
+ status = parcel->readInt32(&frameLeft) ?:
+ parcel->readInt32(&frameTop) ?:
+ parcel->readInt32(&frameRight) ?:
+ parcel->readInt32(&frameBottom) ?:
+ parcel->readInt32(&surfaceInset) ?:
+ parcel->readFloat(&globalScaleFactor) ?:
+ parcel->readFloat(&alpha) ?:
+ parcel->readFloat(&dsdx) ?:
+ parcel->readFloat(&dtdx) ?:
+ parcel->readFloat(&tx) ?:
+ parcel->readFloat(&dtdy) ?:
+ parcel->readFloat(&dsdy) ?:
+ parcel->readFloat(&ty) ?:
+ parcel->readBool(&visible) ?:
+ parcel->readBool(&focusable) ?:
+ parcel->readBool(&hasWallpaper) ?:
+ parcel->readBool(&paused) ?:
+ parcel->readBool(&trustedOverlay) ?:
+ parcel->readInt32(&touchOcclusionModeInt) ?:
+ parcel->readInt32(&ownerPid) ?:
+ parcel->readInt32(&ownerUid) ?:
+ parcel->readUtf8FromUtf16(&packageName);
+ // clang-format on
-InputWindowInfo::InputWindowInfo(const Parcel& from) {
- *this = read(from);
+ if (status != OK) {
+ return status;
+ }
+
+ touchOcclusionMode = static_cast<TouchOcclusionMode>(touchOcclusionModeInt);
+
+ inputFeatures = Flags<Feature>(parcel->readInt32());
+ status = parcel->readInt32(&displayId) ?:
+ parcel->readInt32(&portalToDisplayId) ?:
+ applicationInfo.readFromParcel(parcel) ?:
+ parcel->read(touchableRegion) ?:
+ parcel->readBool(&replaceTouchableRegionWithCrop);
+
+ if (status != OK) {
+ return status;
+ }
+
+ touchableRegionCropHandle = parcel->readStrongBinder();
+ transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1});
+
+ return OK;
}
// --- InputWindowHandle ---
-InputWindowHandle::InputWindowHandle() {
+InputWindowHandle::InputWindowHandle() {}
+
+InputWindowHandle::~InputWindowHandle() {}
+
+InputWindowHandle::InputWindowHandle(const InputWindowHandle& other) : mInfo(other.mInfo) {}
+
+InputWindowHandle::InputWindowHandle(const InputWindowInfo& other) : mInfo(other) {}
+
+status_t InputWindowHandle::writeToParcel(android::Parcel* parcel) const {
+ return mInfo.writeToParcel(parcel);
}
-InputWindowHandle::~InputWindowHandle() {
+status_t InputWindowHandle::readFromParcel(const android::Parcel* parcel) {
+ return mInfo.readFromParcel(parcel);
}
void InputWindowHandle::releaseChannel() {
@@ -166,5 +216,4 @@
void InputWindowHandle::updateFrom(sp<InputWindowHandle> handle) {
mInfo = handle->mInfo;
}
-
} // namespace android
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index cb68165..44f3f34 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -19,14 +19,14 @@
#include <stdlib.h>
#include <string.h>
-#ifdef __ANDROID__
+#ifdef __linux__
#include <binder/Parcel.h>
#endif
-
#include <android/keycodes.h>
+#include <attestation/HmacKeyManager.h>
#include <input/InputEventLabels.h>
-#include <input/Keyboard.h>
#include <input/KeyCharacterMap.h>
+#include <input/Keyboard.h>
#include <utils/Log.h>
#include <utils/Errors.h>
@@ -85,15 +85,12 @@
// --- KeyCharacterMap ---
-sp<KeyCharacterMap> KeyCharacterMap::sEmpty = new KeyCharacterMap();
+KeyCharacterMap::KeyCharacterMap() : mType(KeyboardType::UNKNOWN) {}
-KeyCharacterMap::KeyCharacterMap() :
- mType(KEYBOARD_TYPE_UNKNOWN) {
-}
-
-KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other) :
- RefBase(), mType(other.mType), mKeysByScanCode(other.mKeysByScanCode),
- mKeysByUsageCode(other.mKeysByUsageCode) {
+KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other)
+ : mType(other.mType),
+ mKeysByScanCode(other.mKeysByScanCode),
+ mKeysByUsageCode(other.mKeysByUsageCode) {
for (size_t i = 0; i < other.mKeys.size(); i++) {
mKeys.add(other.mKeys.keyAt(i), new Key(*other.mKeys.valueAt(i)));
}
@@ -106,104 +103,137 @@
}
}
-status_t KeyCharacterMap::load(const std::string& filename,
- Format format, sp<KeyCharacterMap>* outMap) {
- outMap->clear();
+bool KeyCharacterMap::operator==(const KeyCharacterMap& other) const {
+ if (mType != other.mType) {
+ return false;
+ }
+ if (mKeys.size() != other.mKeys.size() ||
+ mKeysByScanCode.size() != other.mKeysByScanCode.size() ||
+ mKeysByUsageCode.size() != other.mKeysByUsageCode.size()) {
+ return false;
+ }
+ for (size_t i = 0; i < mKeys.size(); i++) {
+ if (mKeys.keyAt(i) != other.mKeys.keyAt(i)) {
+ return false;
+ }
+ const Key* key = mKeys.valueAt(i);
+ const Key* otherKey = other.mKeys.valueAt(i);
+ if (key->label != otherKey->label || key->number != otherKey->number) {
+ return false;
+ }
+ }
+
+ for (size_t i = 0; i < mKeysByScanCode.size(); i++) {
+ if (mKeysByScanCode.keyAt(i) != other.mKeysByScanCode.keyAt(i)) {
+ return false;
+ }
+ if (mKeysByScanCode.valueAt(i) != other.mKeysByScanCode.valueAt(i)) {
+ return false;
+ }
+ }
+
+ for (size_t i = 0; i < mKeysByUsageCode.size(); i++) {
+ if (mKeysByUsageCode.keyAt(i) != other.mKeysByUsageCode.keyAt(i)) {
+ return false;
+ }
+ if (mKeysByUsageCode.valueAt(i) != other.mKeysByUsageCode.valueAt(i)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::load(const std::string& filename,
+ Format format) {
Tokenizer* tokenizer;
status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer);
if (status) {
- ALOGE("Error %d opening key character map file %s.", status, filename.c_str());
- } else {
- status = load(tokenizer, format, outMap);
- delete tokenizer;
+ return Errorf("Error {} opening key character map file {}.", status, filename.c_str());
}
- return status;
+ std::unique_ptr<Tokenizer> t(tokenizer);
+ auto ret = load(t.get(), format);
+ if (ret.ok()) {
+ (*ret)->mLoadFileName = filename;
+ }
+ return ret;
}
-status_t KeyCharacterMap::loadContents(const std::string& filename, const char* contents,
- Format format, sp<KeyCharacterMap>* outMap) {
- outMap->clear();
-
+base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::loadContents(
+ const std::string& filename, const char* contents, Format format) {
Tokenizer* tokenizer;
status_t status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer);
if (status) {
ALOGE("Error %d opening key character map.", status);
- } else {
- status = load(tokenizer, format, outMap);
- delete tokenizer;
+ return Errorf("Error {} opening key character map.", status);
}
- return status;
+ std::unique_ptr<Tokenizer> t(tokenizer);
+ auto ret = load(t.get(), format);
+ if (ret.ok()) {
+ (*ret)->mLoadFileName = filename;
+ }
+ return ret;
}
-status_t KeyCharacterMap::load(Tokenizer* tokenizer,
- Format format, sp<KeyCharacterMap>* outMap) {
+base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::load(Tokenizer* tokenizer,
+ Format format) {
status_t status = OK;
- sp<KeyCharacterMap> map = new KeyCharacterMap();
+ std::shared_ptr<KeyCharacterMap> map = std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap());
if (!map.get()) {
ALOGE("Error allocating key character map.");
- status = NO_MEMORY;
- } else {
-#if DEBUG_PARSER_PERFORMANCE
- nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
-#endif
- Parser parser(map.get(), tokenizer, format);
- status = parser.parse();
-#if DEBUG_PARSER_PERFORMANCE
- nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
- ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
- tokenizer->getFilename().string(), tokenizer->getLineNumber(),
- elapsedTime / 1000000.0);
-#endif
- if (!status) {
- *outMap = map;
- }
+ return Errorf("Error allocating key character map.");
}
- return status;
+#if DEBUG_PARSER_PERFORMANCE
+ nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+#endif
+ Parser parser(map.get(), tokenizer, format);
+ status = parser.parse();
+#if DEBUG_PARSER_PERFORMANCE
+ nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
+ ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
+ tokenizer->getFilename().string(), tokenizer->getLineNumber(), elapsedTime / 1000000.0);
+#endif
+ if (status == OK) {
+ return map;
+ }
+
+ return Errorf("Load KeyCharacterMap failed {}.", status);
}
-sp<KeyCharacterMap> KeyCharacterMap::combine(const sp<KeyCharacterMap>& base,
- const sp<KeyCharacterMap>& overlay) {
- if (overlay == nullptr) {
- return base;
- }
- if (base == nullptr) {
- return overlay;
- }
-
- sp<KeyCharacterMap> map = new KeyCharacterMap(*base.get());
- for (size_t i = 0; i < overlay->mKeys.size(); i++) {
- int32_t keyCode = overlay->mKeys.keyAt(i);
- Key* key = overlay->mKeys.valueAt(i);
- ssize_t oldIndex = map->mKeys.indexOfKey(keyCode);
+void KeyCharacterMap::combine(const KeyCharacterMap& overlay) {
+ for (size_t i = 0; i < overlay.mKeys.size(); i++) {
+ int32_t keyCode = overlay.mKeys.keyAt(i);
+ Key* key = overlay.mKeys.valueAt(i);
+ ssize_t oldIndex = mKeys.indexOfKey(keyCode);
if (oldIndex >= 0) {
- delete map->mKeys.valueAt(oldIndex);
- map->mKeys.editValueAt(oldIndex) = new Key(*key);
+ delete mKeys.valueAt(oldIndex);
+ mKeys.editValueAt(oldIndex) = new Key(*key);
} else {
- map->mKeys.add(keyCode, new Key(*key));
+ mKeys.add(keyCode, new Key(*key));
}
}
- for (size_t i = 0; i < overlay->mKeysByScanCode.size(); i++) {
- map->mKeysByScanCode.replaceValueFor(overlay->mKeysByScanCode.keyAt(i),
- overlay->mKeysByScanCode.valueAt(i));
+ for (size_t i = 0; i < overlay.mKeysByScanCode.size(); i++) {
+ mKeysByScanCode.replaceValueFor(overlay.mKeysByScanCode.keyAt(i),
+ overlay.mKeysByScanCode.valueAt(i));
}
- for (size_t i = 0; i < overlay->mKeysByUsageCode.size(); i++) {
- map->mKeysByUsageCode.replaceValueFor(overlay->mKeysByUsageCode.keyAt(i),
- overlay->mKeysByUsageCode.valueAt(i));
+ for (size_t i = 0; i < overlay.mKeysByUsageCode.size(); i++) {
+ mKeysByUsageCode.replaceValueFor(overlay.mKeysByUsageCode.keyAt(i),
+ overlay.mKeysByUsageCode.valueAt(i));
}
- return map;
+ mLoadFileName = overlay.mLoadFileName;
}
-sp<KeyCharacterMap> KeyCharacterMap::empty() {
- return sEmpty;
-}
-
-int32_t KeyCharacterMap::getKeyboardType() const {
+KeyCharacterMap::KeyboardType KeyCharacterMap::getKeyboardType() const {
return mType;
}
+const std::string KeyCharacterMap::getLoadFileName() const {
+ return mLoadFileName;
+}
+
char16_t KeyCharacterMap::getDisplayLabel(int32_t keyCode) const {
char16_t result = 0;
const Key* key;
@@ -599,10 +629,14 @@
}
}
-#ifdef __ANDROID__
-sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) {
- sp<KeyCharacterMap> map = new KeyCharacterMap();
- map->mType = parcel->readInt32();
+#ifdef __linux__
+std::shared_ptr<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return nullptr;
+ }
+ std::shared_ptr<KeyCharacterMap> map = std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap());
+ map->mType = static_cast<KeyCharacterMap::KeyboardType>(parcel->readInt32());
size_t numKeys = parcel->readInt32();
if (parcel->errorCheck()) {
return nullptr;
@@ -656,7 +690,11 @@
}
void KeyCharacterMap::writeToParcel(Parcel* parcel) const {
- parcel->writeInt32(mType);
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return;
+ }
+ parcel->writeInt32(static_cast<int32_t>(mType));
size_t numKeys = mKeys.size();
parcel->writeInt32(numKeys);
@@ -677,8 +715,7 @@
parcel->writeInt32(0);
}
}
-#endif
-
+#endif // __linux__
// --- KeyCharacterMap::Key ---
@@ -782,20 +819,20 @@
return BAD_VALUE;
}
- if (mMap->mType == KEYBOARD_TYPE_UNKNOWN) {
+ if (mMap->mType == KeyboardType::UNKNOWN) {
ALOGE("%s: Keyboard layout missing required keyboard 'type' declaration.",
mTokenizer->getLocation().string());
return BAD_VALUE;
}
- if (mFormat == FORMAT_BASE) {
- if (mMap->mType == KEYBOARD_TYPE_OVERLAY) {
+ if (mFormat == Format::BASE) {
+ if (mMap->mType == KeyboardType::OVERLAY) {
ALOGE("%s: Base keyboard layout must specify a keyboard 'type' other than 'OVERLAY'.",
mTokenizer->getLocation().string());
return BAD_VALUE;
}
- } else if (mFormat == FORMAT_OVERLAY) {
- if (mMap->mType != KEYBOARD_TYPE_OVERLAY) {
+ } else if (mFormat == Format::OVERLAY) {
+ if (mMap->mType != KeyboardType::OVERLAY) {
ALOGE("%s: Overlay keyboard layout missing required keyboard "
"'type OVERLAY' declaration.",
mTokenizer->getLocation().string());
@@ -807,7 +844,7 @@
}
status_t KeyCharacterMap::Parser::parseType() {
- if (mMap->mType != KEYBOARD_TYPE_UNKNOWN) {
+ if (mMap->mType != KeyboardType::UNKNOWN) {
ALOGE("%s: Duplicate keyboard 'type' declaration.",
mTokenizer->getLocation().string());
return BAD_VALUE;
@@ -816,20 +853,20 @@
KeyboardType type;
String8 typeToken = mTokenizer->nextToken(WHITESPACE);
if (typeToken == "NUMERIC") {
- type = KEYBOARD_TYPE_NUMERIC;
+ type = KeyboardType::NUMERIC;
} else if (typeToken == "PREDICTIVE") {
- type = KEYBOARD_TYPE_PREDICTIVE;
+ type = KeyboardType::PREDICTIVE;
} else if (typeToken == "ALPHA") {
- type = KEYBOARD_TYPE_ALPHA;
+ type = KeyboardType::ALPHA;
} else if (typeToken == "FULL") {
- type = KEYBOARD_TYPE_FULL;
+ type = KeyboardType::FULL;
} else if (typeToken == "SPECIAL_FUNCTION") {
ALOGW("The SPECIAL_FUNCTION type is now declared in the device's IDC file, please set "
"the property 'keyboard.specialFunction' to '1' there instead.");
// TODO: return BAD_VALUE here in Q
- type = KEYBOARD_TYPE_SPECIAL_FUNCTION;
+ type = KeyboardType::SPECIAL_FUNCTION;
} else if (typeToken == "OVERLAY") {
- type = KEYBOARD_TYPE_OVERLAY;
+ type = KeyboardType::OVERLAY;
} else {
ALOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().string(),
typeToken.string());
@@ -880,7 +917,7 @@
mTokenizer->skipDelimiters(WHITESPACE);
String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
- int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string());
+ int32_t keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string());
if (!keyCode) {
ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
keyCodeToken.string());
@@ -897,7 +934,7 @@
status_t KeyCharacterMap::Parser::parseKey() {
String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
- int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string());
+ int32_t keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string());
if (!keyCode) {
ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
keyCodeToken.string());
@@ -1017,7 +1054,7 @@
} else if (token == "fallback") {
mTokenizer->skipDelimiters(WHITESPACE);
token = mTokenizer->nextToken(WHITESPACE);
- int32_t keyCode = getKeyCodeByLabel(token.string());
+ int32_t keyCode = InputEventLookup::getKeyCodeByLabel(token.string());
if (!keyCode) {
ALOGE("%s: Invalid key code label for fallback behavior, got '%s'.",
mTokenizer->getLocation().string(),
@@ -1034,7 +1071,7 @@
} else if (token == "replace") {
mTokenizer->skipDelimiters(WHITESPACE);
token = mTokenizer->nextToken(WHITESPACE);
- int32_t keyCode = getKeyCodeByLabel(token.string());
+ int32_t keyCode = InputEventLookup::getKeyCodeByLabel(token.string());
if (!keyCode) {
ALOGE("%s: Invalid key code label for replace, got '%s'.",
mTokenizer->getLocation().string(),
diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp
index efca68d..fa5a541 100644
--- a/libs/input/KeyLayoutMap.cpp
+++ b/libs/input/KeyLayoutMap.cpp
@@ -20,12 +20,13 @@
#include <android/keycodes.h>
#include <input/InputEventLabels.h>
-#include <input/Keyboard.h>
#include <input/KeyLayoutMap.h>
-#include <utils/Log.h>
+#include <input/Keyboard.h>
+#include <input/NamedEnum.h>
#include <utils/Errors.h>
-#include <utils/Tokenizer.h>
+#include <utils/Log.h>
#include <utils/Timers.h>
+#include <utils/Tokenizer.h>
// Enables debug output for the parser.
#define DEBUG_PARSER 0
@@ -41,6 +42,26 @@
static const char* WHITESPACE = " \t\r";
+#define SENSOR_ENTRY(type) NamedEnum::string(type), type
+static const std::unordered_map<std::string, InputDeviceSensorType> SENSOR_LIST =
+ {{SENSOR_ENTRY(InputDeviceSensorType::ACCELEROMETER)},
+ {SENSOR_ENTRY(InputDeviceSensorType::MAGNETIC_FIELD)},
+ {SENSOR_ENTRY(InputDeviceSensorType::ORIENTATION)},
+ {SENSOR_ENTRY(InputDeviceSensorType::GYROSCOPE)},
+ {SENSOR_ENTRY(InputDeviceSensorType::LIGHT)},
+ {SENSOR_ENTRY(InputDeviceSensorType::PRESSURE)},
+ {SENSOR_ENTRY(InputDeviceSensorType::TEMPERATURE)},
+ {SENSOR_ENTRY(InputDeviceSensorType::PROXIMITY)},
+ {SENSOR_ENTRY(InputDeviceSensorType::GRAVITY)},
+ {SENSOR_ENTRY(InputDeviceSensorType::LINEAR_ACCELERATION)},
+ {SENSOR_ENTRY(InputDeviceSensorType::ROTATION_VECTOR)},
+ {SENSOR_ENTRY(InputDeviceSensorType::RELATIVE_HUMIDITY)},
+ {SENSOR_ENTRY(InputDeviceSensorType::AMBIENT_TEMPERATURE)},
+ {SENSOR_ENTRY(InputDeviceSensorType::MAGNETIC_FIELD_UNCALIBRATED)},
+ {SENSOR_ENTRY(InputDeviceSensorType::GAME_ROTATION_VECTOR)},
+ {SENSOR_ENTRY(InputDeviceSensorType::GYROSCOPE_UNCALIBRATED)},
+ {SENSOR_ENTRY(InputDeviceSensorType::SIGNIFICANT_MOTION)}};
+
// --- KeyLayoutMap ---
KeyLayoutMap::KeyLayoutMap() {
@@ -49,37 +70,60 @@
KeyLayoutMap::~KeyLayoutMap() {
}
-status_t KeyLayoutMap::load(const std::string& filename, sp<KeyLayoutMap>* outMap) {
- outMap->clear();
+base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::loadContents(const std::string& filename,
+ const char* contents) {
+ Tokenizer* tokenizer;
+ status_t status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer);
+ if (status) {
+ ALOGE("Error %d opening key layout map.", status);
+ return Errorf("Error {} opening key layout map file {}.", status, filename.c_str());
+ }
+ std::unique_ptr<Tokenizer> t(tokenizer);
+ auto ret = load(t.get());
+ if (ret.ok()) {
+ (*ret)->mLoadFileName = filename;
+ }
+ return ret;
+}
+base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::load(const std::string& filename) {
Tokenizer* tokenizer;
status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer);
if (status) {
ALOGE("Error %d opening key layout map file %s.", status, filename.c_str());
- } else {
- sp<KeyLayoutMap> map = new KeyLayoutMap();
- if (!map.get()) {
- ALOGE("Error allocating key layout map.");
- status = NO_MEMORY;
- } else {
-#if DEBUG_PARSER_PERFORMANCE
- nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
-#endif
- Parser parser(map.get(), tokenizer);
- status = parser.parse();
-#if DEBUG_PARSER_PERFORMANCE
- nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
- ALOGD("Parsed key layout map file '%s' %d lines in %0.3fms.",
- tokenizer->getFilename().string(), tokenizer->getLineNumber(),
- elapsedTime / 1000000.0);
-#endif
- if (!status) {
- *outMap = map;
- }
- }
- delete tokenizer;
+ return Errorf("Error {} opening key layout map file {}.", status, filename.c_str());
}
- return status;
+ std::unique_ptr<Tokenizer> t(tokenizer);
+ auto ret = load(t.get());
+ if (ret.ok()) {
+ (*ret)->mLoadFileName = filename;
+ }
+ return ret;
+}
+
+base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::load(Tokenizer* tokenizer) {
+ std::shared_ptr<KeyLayoutMap> map = std::shared_ptr<KeyLayoutMap>(new KeyLayoutMap());
+ status_t status = OK;
+ if (!map.get()) {
+ ALOGE("Error allocating key layout map.");
+ return Errorf("Error allocating key layout map.");
+ } else {
+#if DEBUG_PARSER_PERFORMANCE
+ nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+#endif
+ Parser parser(map.get(), tokenizer);
+ status = parser.parse();
+#if DEBUG_PARSER_PERFORMANCE
+ nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
+ ALOGD("Parsed key layout map file '%s' %d lines in %0.3fms.",
+ tokenizer->getFilename().string(), tokenizer->getLineNumber(),
+ elapsedTime / 1000000.0);
+#endif
+ if (!status) {
+ return std::move(map);
+ }
+ }
+ return Errorf("Load KeyLayoutMap failed {}.", status);
}
status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode,
@@ -104,6 +148,24 @@
return NO_ERROR;
}
+// Return pair of sensor type and sensor data index, for the input device abs code
+base::Result<std::pair<InputDeviceSensorType, int32_t>> KeyLayoutMap::mapSensor(int32_t absCode) {
+ auto it = mSensorsByAbsCode.find(absCode);
+ if (it == mSensorsByAbsCode.end()) {
+#if DEBUG_MAPPING
+ ALOGD("mapSensor: absCode=%d, ~ Failed.", absCode);
+#endif
+ return Errorf("Can't find abs code {}.", absCode);
+ }
+ const Sensor& sensor = it->second;
+
+#if DEBUG_MAPPING
+ ALOGD("mapSensor: absCode=%d, sensorType=0x%0x, sensorDataIndex=0x%x.", absCode,
+ NamedEnum::string(sensor.sensorType), sensor.sensorDataIndex);
+#endif
+ return std::make_pair(sensor.sensorType, sensor.sensorDataIndex);
+}
+
const KeyLayoutMap::Key* KeyLayoutMap::getKey(int32_t scanCode, int32_t usageCode) const {
if (usageCode) {
ssize_t index = mKeysByUsageCode.indexOfKey(usageCode);
@@ -219,6 +281,10 @@
mTokenizer->skipDelimiters(WHITESPACE);
status_t status = parseLed();
if (status) return status;
+ } else if (keywordToken == "sensor") {
+ mTokenizer->skipDelimiters(WHITESPACE);
+ status_t status = parseSensor();
+ if (status) return status;
} else {
ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(),
keywordToken.string());
@@ -264,7 +330,7 @@
mTokenizer->skipDelimiters(WHITESPACE);
String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
- int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string());
+ int32_t keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string());
if (!keyCode) {
ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
keyCodeToken.string());
@@ -277,7 +343,7 @@
if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') break;
String8 flagToken = mTokenizer->nextToken(WHITESPACE);
- uint32_t flag = getKeyFlagByLabel(flagToken.string());
+ uint32_t flag = InputEventLookup::getKeyFlagByLabel(flagToken.string());
if (!flag) {
ALOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().string(),
flagToken.string());
@@ -326,7 +392,7 @@
mTokenizer->skipDelimiters(WHITESPACE);
String8 axisToken = mTokenizer->nextToken(WHITESPACE);
- axisInfo.axis = getAxisByLabel(axisToken.string());
+ axisInfo.axis = InputEventLookup::getAxisByLabel(axisToken.string());
if (axisInfo.axis < 0) {
ALOGE("%s: Expected inverted axis label, got '%s'.",
mTokenizer->getLocation().string(), axisToken.string());
@@ -346,7 +412,7 @@
mTokenizer->skipDelimiters(WHITESPACE);
String8 lowAxisToken = mTokenizer->nextToken(WHITESPACE);
- axisInfo.axis = getAxisByLabel(lowAxisToken.string());
+ axisInfo.axis = InputEventLookup::getAxisByLabel(lowAxisToken.string());
if (axisInfo.axis < 0) {
ALOGE("%s: Expected low axis label, got '%s'.",
mTokenizer->getLocation().string(), lowAxisToken.string());
@@ -355,14 +421,14 @@
mTokenizer->skipDelimiters(WHITESPACE);
String8 highAxisToken = mTokenizer->nextToken(WHITESPACE);
- axisInfo.highAxis = getAxisByLabel(highAxisToken.string());
+ axisInfo.highAxis = InputEventLookup::getAxisByLabel(highAxisToken.string());
if (axisInfo.highAxis < 0) {
ALOGE("%s: Expected high axis label, got '%s'.",
mTokenizer->getLocation().string(), highAxisToken.string());
return BAD_VALUE;
}
} else {
- axisInfo.axis = getAxisByLabel(token.string());
+ axisInfo.axis = InputEventLookup::getAxisByLabel(token.string());
if (axisInfo.axis < 0) {
ALOGE("%s: Expected axis label, 'split' or 'invert', got '%s'.",
mTokenizer->getLocation().string(), token.string());
@@ -428,7 +494,7 @@
mTokenizer->skipDelimiters(WHITESPACE);
String8 ledCodeToken = mTokenizer->nextToken(WHITESPACE);
- int32_t ledCode = getLedByLabel(ledCodeToken.string());
+ int32_t ledCode = InputEventLookup::getLedByLabel(ledCodeToken.string());
if (ledCode < 0) {
ALOGE("%s: Expected LED code label, got '%s'.", mTokenizer->getLocation().string(),
ledCodeToken.string());
@@ -445,4 +511,84 @@
map.add(code, led);
return NO_ERROR;
}
+
+static std::optional<InputDeviceSensorType> getSensorType(const char* token) {
+ auto it = SENSOR_LIST.find(std::string(token));
+ if (it == SENSOR_LIST.end()) {
+ return std::nullopt;
+ }
+ return it->second;
+}
+
+static std::optional<int32_t> getSensorDataIndex(String8 token) {
+ std::string tokenStr(token.string());
+ if (tokenStr == "X") {
+ return 0;
+ } else if (tokenStr == "Y") {
+ return 1;
+ } else if (tokenStr == "Z") {
+ return 2;
+ }
+ return std::nullopt;
+}
+
+// Parse sensor type and data index mapping, as below format
+// sensor <raw abs> <sensor type> <sensor data index>
+// raw abs : the linux abs code of the axis
+// sensor type : string name of InputDeviceSensorType
+// sensor data index : the data index of sensor, out of [X, Y, Z]
+// Examples:
+// sensor 0x00 ACCELEROMETER X
+// sensor 0x01 ACCELEROMETER Y
+// sensor 0x02 ACCELEROMETER Z
+// sensor 0x03 GYROSCOPE X
+// sensor 0x04 GYROSCOPE Y
+// sensor 0x05 GYROSCOPE Z
+status_t KeyLayoutMap::Parser::parseSensor() {
+ String8 codeToken = mTokenizer->nextToken(WHITESPACE);
+ char* end;
+ int32_t code = int32_t(strtol(codeToken.string(), &end, 0));
+ if (*end) {
+ ALOGE("%s: Expected sensor %s number, got '%s'.", mTokenizer->getLocation().string(),
+ "abs code", codeToken.string());
+ return BAD_VALUE;
+ }
+
+ std::unordered_map<int32_t, Sensor>& map = mMap->mSensorsByAbsCode;
+ if (map.find(code) != map.end()) {
+ ALOGE("%s: Duplicate entry for sensor %s '%s'.", mTokenizer->getLocation().string(),
+ "abs code", codeToken.string());
+ return BAD_VALUE;
+ }
+
+ mTokenizer->skipDelimiters(WHITESPACE);
+ String8 sensorTypeToken = mTokenizer->nextToken(WHITESPACE);
+ std::optional<InputDeviceSensorType> typeOpt = getSensorType(sensorTypeToken.string());
+ if (!typeOpt) {
+ ALOGE("%s: Expected sensor code label, got '%s'.", mTokenizer->getLocation().string(),
+ sensorTypeToken.string());
+ return BAD_VALUE;
+ }
+ InputDeviceSensorType sensorType = typeOpt.value();
+ mTokenizer->skipDelimiters(WHITESPACE);
+ String8 sensorDataIndexToken = mTokenizer->nextToken(WHITESPACE);
+ std::optional<int32_t> indexOpt = getSensorDataIndex(sensorDataIndexToken);
+ if (!indexOpt) {
+ ALOGE("%s: Expected sensor data index label, got '%s'.", mTokenizer->getLocation().string(),
+ sensorDataIndexToken.string());
+ return BAD_VALUE;
+ }
+ int32_t sensorDataIndex = indexOpt.value();
+
+#if DEBUG_PARSER
+ ALOGD("Parsed sensor: abs code=%d, sensorType=%d, sensorDataIndex=%d.", code,
+ NamedEnum::string(sensorType).c_str(), sensorDataIndex);
+#endif
+
+ Sensor sensor;
+ sensor.sensorType = sensorType;
+ sensor.sensorDataIndex = sensorDataIndex;
+ map.emplace(code, sensor);
+ return NO_ERROR;
+}
};
diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp
index 56900c1..f0895b3 100644
--- a/libs/input/Keyboard.cpp
+++ b/libs/input/Keyboard.cpp
@@ -105,35 +105,34 @@
status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier,
const std::string& name) {
- std::string path(getPath(deviceIdentifier, name,
- INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT));
+ std::string path(getPath(deviceIdentifier, name, InputDeviceConfigurationFileType::KEY_LAYOUT));
if (path.empty()) {
return NAME_NOT_FOUND;
}
- status_t status = KeyLayoutMap::load(path, &keyLayoutMap);
- if (status) {
- return status;
+ base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(path);
+ if (!ret.ok()) {
+ return ret.error().code();
}
-
+ keyLayoutMap = *ret;
keyLayoutFile = path;
return OK;
}
status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier,
const std::string& name) {
- std::string path = getPath(deviceIdentifier, name,
- INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP);
+ std::string path =
+ getPath(deviceIdentifier, name, InputDeviceConfigurationFileType::KEY_CHARACTER_MAP);
if (path.empty()) {
return NAME_NOT_FOUND;
}
- status_t status = KeyCharacterMap::load(path,
- KeyCharacterMap::FORMAT_BASE, &keyCharacterMap);
- if (status) {
- return status;
+ base::Result<std::shared_ptr<KeyCharacterMap>> ret =
+ KeyCharacterMap::load(path, KeyCharacterMap::Format::BASE);
+ if (!ret.ok()) {
+ return ret.error().code();
}
-
+ keyCharacterMap = *ret;
keyCharacterMapFile = path;
return OK;
}
@@ -160,9 +159,9 @@
bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier,
const PropertyMap* deviceConfiguration, const KeyMap* keyMap) {
// TODO: remove the third OR statement (SPECIAL_FUNCTION) in Q
- if (!keyMap->haveKeyCharacterMap() || isKeyboardSpecialFunction(deviceConfiguration)
- || keyMap->keyCharacterMap->getKeyboardType()
- == KeyCharacterMap::KEYBOARD_TYPE_SPECIAL_FUNCTION) {
+ if (!keyMap->haveKeyCharacterMap() || isKeyboardSpecialFunction(deviceConfiguration) ||
+ keyMap->keyCharacterMap->getKeyboardType() ==
+ KeyCharacterMap::KeyboardType::SPECIAL_FUNCTION) {
return false;
}
diff --git a/libs/input/PropertyMap.cpp b/libs/input/PropertyMap.cpp
index 4833eb9..a842166 100644
--- a/libs/input/PropertyMap.cpp
+++ b/libs/input/PropertyMap.cpp
@@ -107,23 +107,22 @@
}
}
-status_t PropertyMap::load(const String8& filename, PropertyMap** outMap) {
- *outMap = nullptr;
+android::base::Result<std::unique_ptr<PropertyMap>> PropertyMap::load(const char* filename) {
+ std::unique_ptr<PropertyMap> outMap = std::make_unique<PropertyMap>();
+ if (outMap == nullptr) {
+ return android::base::Error(NO_MEMORY) << "Error allocating property map.";
+ }
- Tokenizer* tokenizer;
- status_t status = Tokenizer::open(filename, &tokenizer);
+ Tokenizer* rawTokenizer;
+ status_t status = Tokenizer::open(String8(filename), &rawTokenizer);
+ std::unique_ptr<Tokenizer> tokenizer(rawTokenizer);
if (status) {
- ALOGE("Error %d opening property file %s.", status, filename.string());
+ ALOGE("Error %d opening property file %s.", status, filename);
} else {
- PropertyMap* map = new PropertyMap();
- if (!map) {
- ALOGE("Error allocating property map.");
- status = NO_MEMORY;
- } else {
#if DEBUG_PARSER_PERFORMANCE
nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
#endif
- Parser parser(map, tokenizer);
+ Parser parser(outMap.get(), tokenizer.get());
status = parser.parse();
#if DEBUG_PARSER_PERFORMANCE
nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
@@ -132,14 +131,10 @@
elapsedTime / 1000000.0);
#endif
if (status) {
- delete map;
- } else {
- *outMap = map;
+ return android::base::Error(BAD_VALUE) << "Could not parse " << filename;
}
- }
- delete tokenizer;
}
- return status;
+ return std::move(outMap);
}
// --- PropertyMap::Parser ---
diff --git a/libs/input/PropertyMap_fuzz.cpp b/libs/input/PropertyMap_fuzz.cpp
index 23ead0e..afb97a1 100755
--- a/libs/input/PropertyMap_fuzz.cpp
+++ b/libs/input/PropertyMap_fuzz.cpp
@@ -42,7 +42,7 @@
android::String8 out;
propertyMap.tryGetProperty(key, out);
},
- [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
+ [](FuzzedDataProvider* dataProvider, android::PropertyMap /*unused*/) -> void {
TemporaryFile tf;
// Generate file contents
std::string contents = dataProvider->ConsumeRandomLengthString(MAX_FILE_SIZE);
@@ -52,8 +52,7 @@
const char* bytes = contents.c_str();
android::base::WriteStringToFd(bytes, tf.fd);
}
- android::PropertyMap* mapPtr = &propertyMap;
- android::PropertyMap::load(android::String8(tf.path), &mapPtr);
+ android::PropertyMap::load(tf.path);
},
[](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
@@ -65,12 +64,12 @@
};
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
FuzzedDataProvider dataProvider(data, size);
- android::PropertyMap proprtyMap = android::PropertyMap();
+ android::PropertyMap propertyMap = android::PropertyMap();
int opsRun = 0;
while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {
uint8_t op = dataProvider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1);
- operations[op](&dataProvider, proprtyMap);
+ operations[op](&dataProvider, propertyMap);
}
return 0;
}
diff --git a/libs/input/TEST_MAPPING b/libs/input/TEST_MAPPING
new file mode 100644
index 0000000..9626d8d
--- /dev/null
+++ b/libs/input/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "imports": [
+ {
+ "path": "frameworks/native/services/inputflinger"
+ }
+ ]
+}
diff --git a/libs/input/VelocityControl.cpp b/libs/input/VelocityControl.cpp
index bcf55b0..2c04d42 100644
--- a/libs/input/VelocityControl.cpp
+++ b/libs/input/VelocityControl.cpp
@@ -66,7 +66,7 @@
if (deltaY) {
mRawPosition.y += *deltaY;
}
- mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), &mRawPosition);
+ mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), {mRawPosition});
float vx, vy;
float scale = mParameters.scale;
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index c6cc4fc..a44f0b7 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -104,107 +104,73 @@
// --- VelocityTracker ---
-// The default velocity tracker strategy.
-// Although other strategies are available for testing and comparison purposes,
-// this is the strategy that applications will actually use. Be very careful
-// when adjusting the default strategy because it can dramatically affect
-// (often in a bad way) the user experience.
-const char* VelocityTracker::DEFAULT_STRATEGY = "lsq2";
-
-VelocityTracker::VelocityTracker(const char* strategy) :
- mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) {
- char value[PROPERTY_VALUE_MAX];
-
- // Allow the default strategy to be overridden using a system property for debugging.
- if (!strategy) {
- int length = property_get("persist.input.velocitytracker.strategy", value, nullptr);
- if (length > 0) {
- strategy = value;
- } else {
- strategy = DEFAULT_STRATEGY;
- }
- }
-
+VelocityTracker::VelocityTracker(const Strategy strategy)
+ : mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) {
// Configure the strategy.
if (!configureStrategy(strategy)) {
- ALOGD("Unrecognized velocity tracker strategy name '%s'.", strategy);
- if (!configureStrategy(DEFAULT_STRATEGY)) {
- LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%s'!",
- strategy);
+ ALOGE("Unrecognized velocity tracker strategy %" PRId32 ".", strategy);
+ if (!configureStrategy(VelocityTracker::DEFAULT_STRATEGY)) {
+ LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%" PRId32
+ "'!",
+ strategy);
}
}
}
VelocityTracker::~VelocityTracker() {
- delete mStrategy;
}
-bool VelocityTracker::configureStrategy(const char* strategy) {
- mStrategy = createStrategy(strategy);
+bool VelocityTracker::configureStrategy(Strategy strategy) {
+ if (strategy == VelocityTracker::Strategy::DEFAULT) {
+ mStrategy = createStrategy(VelocityTracker::DEFAULT_STRATEGY);
+ } else {
+ mStrategy = createStrategy(strategy);
+ }
return mStrategy != nullptr;
}
-VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) {
- if (!strcmp("impulse", strategy)) {
- // Physical model of pushing an object. Quality: VERY GOOD.
- // Works with duplicate coordinates, unclean finger liftoff.
- return new ImpulseVelocityTrackerStrategy();
- }
- if (!strcmp("lsq1", strategy)) {
- // 1st order least squares. Quality: POOR.
- // Frequently underfits the touch data especially when the finger accelerates
- // or changes direction. Often underestimates velocity. The direction
- // is overly influenced by historical touch points.
- return new LeastSquaresVelocityTrackerStrategy(1);
- }
- if (!strcmp("lsq2", strategy)) {
- // 2nd order least squares. Quality: VERY GOOD.
- // Pretty much ideal, but can be confused by certain kinds of touch data,
- // particularly if the panel has a tendency to generate delayed,
- // duplicate or jittery touch coordinates when the finger is released.
- return new LeastSquaresVelocityTrackerStrategy(2);
- }
- if (!strcmp("lsq3", strategy)) {
- // 3rd order least squares. Quality: UNUSABLE.
- // Frequently overfits the touch data yielding wildly divergent estimates
- // of the velocity when the finger is released.
- return new LeastSquaresVelocityTrackerStrategy(3);
- }
- if (!strcmp("wlsq2-delta", strategy)) {
- // 2nd order weighted least squares, delta weighting. Quality: EXPERIMENTAL
- return new LeastSquaresVelocityTrackerStrategy(2,
- LeastSquaresVelocityTrackerStrategy::WEIGHTING_DELTA);
- }
- if (!strcmp("wlsq2-central", strategy)) {
- // 2nd order weighted least squares, central weighting. Quality: EXPERIMENTAL
- return new LeastSquaresVelocityTrackerStrategy(2,
- LeastSquaresVelocityTrackerStrategy::WEIGHTING_CENTRAL);
- }
- if (!strcmp("wlsq2-recent", strategy)) {
- // 2nd order weighted least squares, recent weighting. Quality: EXPERIMENTAL
- return new LeastSquaresVelocityTrackerStrategy(2,
- LeastSquaresVelocityTrackerStrategy::WEIGHTING_RECENT);
- }
- if (!strcmp("int1", strategy)) {
- // 1st order integrating filter. Quality: GOOD.
- // Not as good as 'lsq2' because it cannot estimate acceleration but it is
- // more tolerant of errors. Like 'lsq1', this strategy tends to underestimate
- // the velocity of a fling but this strategy tends to respond to changes in
- // direction more quickly and accurately.
- return new IntegratingVelocityTrackerStrategy(1);
- }
- if (!strcmp("int2", strategy)) {
- // 2nd order integrating filter. Quality: EXPERIMENTAL.
- // For comparison purposes only. Unlike 'int1' this strategy can compensate
- // for acceleration but it typically overestimates the effect.
- return new IntegratingVelocityTrackerStrategy(2);
- }
- if (!strcmp("legacy", strategy)) {
- // Legacy velocity tracker algorithm. Quality: POOR.
- // For comparison purposes only. This algorithm is strongly influenced by
- // old data points, consistently underestimates velocity and takes a very long
- // time to adjust to changes in direction.
- return new LegacyVelocityTrackerStrategy();
+std::unique_ptr<VelocityTrackerStrategy> VelocityTracker::createStrategy(
+ VelocityTracker::Strategy strategy) {
+ switch (strategy) {
+ case VelocityTracker::Strategy::IMPULSE:
+ return std::make_unique<ImpulseVelocityTrackerStrategy>();
+
+ case VelocityTracker::Strategy::LSQ1:
+ return std::make_unique<LeastSquaresVelocityTrackerStrategy>(1);
+
+ case VelocityTracker::Strategy::LSQ2:
+ return std::make_unique<LeastSquaresVelocityTrackerStrategy>(2);
+
+ case VelocityTracker::Strategy::LSQ3:
+ return std::make_unique<LeastSquaresVelocityTrackerStrategy>(3);
+
+ case VelocityTracker::Strategy::WLSQ2_DELTA:
+ return std::make_unique<
+ LeastSquaresVelocityTrackerStrategy>(2,
+ LeastSquaresVelocityTrackerStrategy::
+ WEIGHTING_DELTA);
+ case VelocityTracker::Strategy::WLSQ2_CENTRAL:
+ return std::make_unique<
+ LeastSquaresVelocityTrackerStrategy>(2,
+ LeastSquaresVelocityTrackerStrategy::
+ WEIGHTING_CENTRAL);
+ case VelocityTracker::Strategy::WLSQ2_RECENT:
+ return std::make_unique<
+ LeastSquaresVelocityTrackerStrategy>(2,
+ LeastSquaresVelocityTrackerStrategy::
+ WEIGHTING_RECENT);
+
+ case VelocityTracker::Strategy::INT1:
+ return std::make_unique<IntegratingVelocityTrackerStrategy>(1);
+
+ case VelocityTracker::Strategy::INT2:
+ return std::make_unique<IntegratingVelocityTrackerStrategy>(2);
+
+ case VelocityTracker::Strategy::LEGACY:
+ return std::make_unique<LegacyVelocityTrackerStrategy>();
+
+ default:
+ break;
}
return nullptr;
}
@@ -227,7 +193,11 @@
mStrategy->clearPointers(idBits);
}
-void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions) {
+void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits,
+ const std::vector<VelocityTracker::Position>& positions) {
+ LOG_ALWAYS_FATAL_IF(idBits.count() != positions.size(),
+ "Mismatching number of pointers, idBits=%" PRIu32 ", positions=%zu",
+ idBits.count(), positions.size());
while (idBits.count() > MAX_POINTERS) {
idBits.clearLastMarkedBit();
}
@@ -319,12 +289,12 @@
pointerIndex[i] = idBits.getIndexOfBit(event->getPointerId(i));
}
- nsecs_t eventTime;
- Position positions[pointerCount];
+ std::vector<Position> positions;
+ positions.resize(pointerCount);
size_t historySize = event->getHistorySize();
- for (size_t h = 0; h < historySize; h++) {
- eventTime = event->getHistoricalEventTime(h);
+ for (size_t h = 0; h <= historySize; h++) {
+ nsecs_t eventTime = event->getHistoricalEventTime(h);
for (size_t i = 0; i < pointerCount; i++) {
uint32_t index = pointerIndex[i];
positions[index].x = event->getHistoricalX(i, h);
@@ -332,14 +302,6 @@
}
addMovement(eventTime, idBits, positions);
}
-
- eventTime = event->getEventTime();
- for (size_t i = 0; i < pointerCount; i++) {
- uint32_t index = pointerIndex[i];
- positions[index].x = event->getX(i);
- positions[index].y = event->getY(i);
- }
- addMovement(eventTime, idBits, positions);
}
bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const {
@@ -380,8 +342,9 @@
mMovements[mIndex].idBits = remainingIdBits;
}
-void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
- const VelocityTracker::Position* positions) {
+void LeastSquaresVelocityTrackerStrategy::addMovement(
+ nsecs_t eventTime, BitSet32 idBits,
+ const std::vector<VelocityTracker::Position>& positions) {
if (mMovements[mIndex].eventTime != eventTime) {
// When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
// of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
@@ -453,13 +416,15 @@
* http://en.wikipedia.org/wiki/Numerical_methods_for_linear_least_squares
* http://en.wikipedia.org/wiki/Gram-Schmidt
*/
-static bool solveLeastSquares(const float* x, const float* y,
- const float* w, uint32_t m, uint32_t n, float* outB, float* outDet) {
+static bool solveLeastSquares(const std::vector<float>& x, const std::vector<float>& y,
+ const std::vector<float>& w, uint32_t n, float* outB, float* outDet) {
+ const size_t m = x.size();
#if DEBUG_STRATEGY
ALOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n),
vectorToString(x, m).c_str(), vectorToString(y, m).c_str(),
vectorToString(w, m).c_str());
#endif
+ LOG_ALWAYS_FATAL_IF(m != y.size() || m != w.size(), "Mismatched vector sizes");
// Expand the X vector to a matrix A, pre-multiplied by the weights.
float a[n][m]; // column-major order
@@ -576,7 +541,9 @@
* the default implementation
*/
static std::optional<std::array<float, 3>> solveUnweightedLeastSquaresDeg2(
- const float* x, const float* y, size_t count) {
+ const std::vector<float>& x, const std::vector<float>& y) {
+ const size_t count = x.size();
+ LOG_ALWAYS_FATAL_IF(count != y.size(), "Mismatching array sizes");
// Solving y = a*x^2 + b*x + c
float sxi = 0, sxiyi = 0, syi = 0, sxi2 = 0, sxi3 = 0, sxi2yi = 0, sxi4 = 0;
@@ -628,11 +595,11 @@
outEstimator->clear();
// Iterate over movement samples in reverse time order and collect samples.
- float x[HISTORY_SIZE];
- float y[HISTORY_SIZE];
- float w[HISTORY_SIZE];
- float time[HISTORY_SIZE];
- uint32_t m = 0;
+ std::vector<float> x;
+ std::vector<float> y;
+ std::vector<float> w;
+ std::vector<float> time;
+
uint32_t index = mIndex;
const Movement& newestMovement = mMovements[mIndex];
do {
@@ -647,13 +614,14 @@
}
const VelocityTracker::Position& position = movement.getPosition(id);
- x[m] = position.x;
- y[m] = position.y;
- w[m] = chooseWeight(index);
- time[m] = -age * 0.000000001f;
+ x.push_back(position.x);
+ y.push_back(position.y);
+ w.push_back(chooseWeight(index));
+ time.push_back(-age * 0.000000001f);
index = (index == 0 ? HISTORY_SIZE : index) - 1;
- } while (++m < HISTORY_SIZE);
+ } while (x.size() < HISTORY_SIZE);
+ const size_t m = x.size();
if (m == 0) {
return false; // no data
}
@@ -666,8 +634,8 @@
if (degree == 2 && mWeighting == WEIGHTING_NONE) {
// Optimize unweighted, quadratic polynomial fit
- std::optional<std::array<float, 3>> xCoeff = solveUnweightedLeastSquaresDeg2(time, x, m);
- std::optional<std::array<float, 3>> yCoeff = solveUnweightedLeastSquaresDeg2(time, y, m);
+ std::optional<std::array<float, 3>> xCoeff = solveUnweightedLeastSquaresDeg2(time, x);
+ std::optional<std::array<float, 3>> yCoeff = solveUnweightedLeastSquaresDeg2(time, y);
if (xCoeff && yCoeff) {
outEstimator->time = newestMovement.eventTime;
outEstimator->degree = 2;
@@ -682,8 +650,8 @@
// General case for an Nth degree polynomial fit
float xdet, ydet;
uint32_t n = degree + 1;
- if (solveLeastSquares(time, x, w, m, n, outEstimator->xCoeff, &xdet)
- && solveLeastSquares(time, y, w, m, n, outEstimator->yCoeff, &ydet)) {
+ if (solveLeastSquares(time, x, w, n, outEstimator->xCoeff, &xdet) &&
+ solveLeastSquares(time, y, w, n, outEstimator->yCoeff, &ydet)) {
outEstimator->time = newestMovement.eventTime;
outEstimator->degree = degree;
outEstimator->confidence = xdet * ydet;
@@ -792,8 +760,9 @@
mPointerIdBits.value &= ~idBits.value;
}
-void IntegratingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
- const VelocityTracker::Position* positions) {
+void IntegratingVelocityTrackerStrategy::addMovement(
+ nsecs_t eventTime, BitSet32 idBits,
+ const std::vector<VelocityTracker::Position>& positions) {
uint32_t index = 0;
for (BitSet32 iterIdBits(idBits); !iterIdBits.isEmpty();) {
uint32_t id = iterIdBits.clearFirstMarkedBit();
@@ -910,8 +879,9 @@
mMovements[mIndex].idBits = remainingIdBits;
}
-void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
- const VelocityTracker::Position* positions) {
+void LegacyVelocityTrackerStrategy::addMovement(
+ nsecs_t eventTime, BitSet32 idBits,
+ const std::vector<VelocityTracker::Position>& positions) {
if (++mIndex == HISTORY_SIZE) {
mIndex = 0;
}
@@ -1024,8 +994,9 @@
mMovements[mIndex].idBits = remainingIdBits;
}
-void ImpulseVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
- const VelocityTracker::Position* positions) {
+void ImpulseVelocityTrackerStrategy::addMovement(
+ nsecs_t eventTime, BitSet32 idBits,
+ const std::vector<VelocityTracker::Position>& positions) {
if (mMovements[mIndex].eventTime != eventTime) {
// When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
// of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
diff --git a/libs/input/android/FocusRequest.aidl b/libs/input/android/FocusRequest.aidl
new file mode 100644
index 0000000..8812d34
--- /dev/null
+++ b/libs/input/android/FocusRequest.aidl
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2020, 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 android;
+
+/** @hide */
+parcelable FocusRequest {
+ /**
+ * Input channel token used to identify the window that should gain focus.
+ */
+ IBinder token;
+ @utf8InCpp String windowName;
+ /**
+ * The token that the caller expects currently to be focused. If the
+ * specified token does not match the currently focused window, this request will be dropped.
+ * If the specified focused token matches the currently focused window, the call will succeed.
+ * Set this to "null" if this call should succeed no matter what the currently focused token
+ * is.
+ */
+ @nullable IBinder focusedToken;
+ @utf8InCpp String focusedWindowName;
+ /**
+ * SYSTEM_TIME_MONOTONIC timestamp in nanos set by the client (wm) when requesting the focus
+ * change. This determines which request gets precedence if there is a focus change request
+ * from another source such as pointer down.
+ */
+ long timestamp;
+ /**
+ * Display id associated with this request.
+ */
+ int displayId;
+}
diff --git a/libs/ui/UiConfig.cpp b/libs/input/android/InputApplicationInfo.aidl
similarity index 61%
copy from libs/ui/UiConfig.cpp
copy to libs/input/android/InputApplicationInfo.aidl
index 0ac863d..9336039 100644
--- a/libs/ui/UiConfig.cpp
+++ b/libs/input/android/InputApplicationInfo.aidl
@@ -1,11 +1,11 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
+/**
+ * Copyright (c) 2020, 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
+ * 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,
@@ -14,15 +14,10 @@
* limitations under the License.
*/
-#include <ui/UiConfig.h>
+package android;
-namespace android {
-
-void appendUiConfigString(std::string& configStr) {
- static const char* config =
- " [libui]";
- configStr.append(config);
+parcelable InputApplicationInfo {
+ @nullable IBinder token;
+ @utf8InCpp String name;
+ long dispatchingTimeoutMillis;
}
-
-
-}; // namespace android
diff --git a/libs/input/android/InputChannel.aidl b/libs/input/android/InputChannel.aidl
new file mode 100644
index 0000000..c2d1112
--- /dev/null
+++ b/libs/input/android/InputChannel.aidl
@@ -0,0 +1,20 @@
+/* //device/java/android/android/view/InputChannel.aidl
+**
+** Copyright 2020, 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 android;
+
+parcelable InputChannel cpp_header "input/InputTransport.h";
diff --git a/libs/input/android/InputWindowInfo.aidl b/libs/input/android/InputWindowInfo.aidl
new file mode 100644
index 0000000..eeaf400
--- /dev/null
+++ b/libs/input/android/InputWindowInfo.aidl
@@ -0,0 +1,20 @@
+/* //device/java/android/android/view/InputChannel.aidl
+**
+** Copyright 2020, 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 android;
+
+parcelable InputWindowInfo cpp_header "input/InputWindow.h";
diff --git a/libs/input/android/os/BlockUntrustedTouchesMode.aidl b/libs/input/android/os/BlockUntrustedTouchesMode.aidl
new file mode 100644
index 0000000..9504e99
--- /dev/null
+++ b/libs/input/android/os/BlockUntrustedTouchesMode.aidl
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2020, 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 android.os;
+
+
+/**
+ * Block untrusted touches feature mode.
+ *
+ * @hide
+ */
+@Backing(type="int")
+enum BlockUntrustedTouchesMode {
+ /** Feature is off. */
+ DISABLED,
+
+ /** Untrusted touches are flagged but not blocked. */
+ PERMISSIVE,
+
+ /** Untrusted touches are blocked. */
+ BLOCK
+}
diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl
new file mode 100644
index 0000000..4b90844
--- /dev/null
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2020, 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 android.os;
+
+
+/** @hide */
+interface IInputConstants
+{
+ // This should be multiplied by the value of the system property ro.hw_timeout_multiplier before
+ // use. A pre-multiplied constant is available in Java in
+ // android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS.
+ const int UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS = 5000; // 5 seconds
+
+ // Compatibility changes.
+ /**
+ * TODO(b/157929241): remove this before closing the bug. This is needed temporarily
+ * to identify apps that are using this flag.
+ */
+ const long BLOCK_FLAG_SLIPPERY = 157929241;
+
+ // Indicate invalid battery capacity
+ const int INVALID_BATTERY_CAPACITY = -1;
+
+ /**
+ * Every input event has an id. This constant value is used when a valid input event id is not
+ * available.
+ */
+ const int INVALID_INPUT_EVENT_ID = 0;
+}
diff --git a/libs/input/android/os/IInputFlinger.aidl b/libs/input/android/os/IInputFlinger.aidl
new file mode 100644
index 0000000..1771d19
--- /dev/null
+++ b/libs/input/android/os/IInputFlinger.aidl
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2020, 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 android.os;
+
+import android.FocusRequest;
+import android.InputChannel;
+import android.InputWindowInfo;
+import android.os.ISetInputWindowsListener;
+
+/** @hide */
+interface IInputFlinger
+{
+ // SurfaceFlinger is the caller of this method, it uses the listener callback to ensure the
+ // ordering when needed.
+ // SurfaceFlinger calls this only every VSync, so overflow of binder's oneway buffer
+ // shouldn't be a concern.
+ oneway void setInputWindows(in InputWindowInfo[] inputHandles,
+ in @nullable ISetInputWindowsListener setInputWindowsListener);
+ InputChannel createInputChannel(in @utf8InCpp String name);
+ void removeInputChannel(in IBinder connectionToken);
+ /**
+ * Sets focus to the window identified by the token. This must be called
+ * after updating any input window handles.
+ */
+ oneway void setFocusedWindow(in FocusRequest request);
+}
diff --git a/libs/ui/UiConfig.cpp b/libs/input/android/os/ISetInputWindowsListener.aidl
similarity index 61%
copy from libs/ui/UiConfig.cpp
copy to libs/input/android/os/ISetInputWindowsListener.aidl
index 0ac863d..bb58fb6 100644
--- a/libs/ui/UiConfig.cpp
+++ b/libs/input/android/os/ISetInputWindowsListener.aidl
@@ -1,11 +1,11 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
+/**
+ * Copyright (c) 2020, 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
+ * 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,
@@ -14,15 +14,10 @@
* limitations under the License.
*/
-#include <ui/UiConfig.h>
+package android.os;
-namespace android {
-
-void appendUiConfigString(std::string& configStr) {
- static const char* config =
- " [libui]";
- configStr.append(config);
+/** @hide */
+oneway interface ISetInputWindowsListener
+{
+ void onSetInputWindowsFinished();
}
-
-
-}; // namespace android
diff --git a/libs/input/android/os/InputEventInjectionResult.aidl b/libs/input/android/os/InputEventInjectionResult.aidl
new file mode 100644
index 0000000..34f10ec
--- /dev/null
+++ b/libs/input/android/os/InputEventInjectionResult.aidl
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2020, 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 android.os;
+
+/**
+ * Constants used to report the outcome of input event injection.
+ *
+ * @hide
+ */
+@Backing(type="int")
+enum InputEventInjectionResult {
+ /* (INTERNAL USE ONLY) Specifies that injection is pending and its outcome is unknown. */
+ PENDING = -1,
+
+ /* Injection succeeded. */
+ SUCCEEDED = 0,
+
+ /* Injection failed because the injector did not have permission to inject
+ * into the application with input focus. */
+ PERMISSION_DENIED = 1,
+
+ /* Injection failed because there were no available input targets. */
+ FAILED = 2,
+
+ /* Injection failed due to a timeout. */
+ TIMED_OUT = 3,
+}
diff --git a/libs/input/android/os/InputEventInjectionSync.aidl b/libs/input/android/os/InputEventInjectionSync.aidl
new file mode 100644
index 0000000..95d24cb
--- /dev/null
+++ b/libs/input/android/os/InputEventInjectionSync.aidl
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2020, 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 android.os;
+
+/**
+ * Constants used to specify the input event injection synchronization mode.
+ *
+ * @hide
+ */
+@Backing(type="int")
+enum InputEventInjectionSync {
+ /* Injection is asynchronous and is assumed always to be successful. */
+ NONE = 0,
+
+ /* Waits for previous events to be dispatched so that the input dispatcher can determine
+ * whether input event injection willbe permitted based on the current input focus.
+ * Does not wait for the input event to finish processing. */
+ WAIT_FOR_RESULT = 1,
+
+ /* Waits for the input event to be completely processed. */
+ WAIT_FOR_FINISHED = 2,
+}
diff --git a/libs/input/android/os/TouchOcclusionMode.aidl b/libs/input/android/os/TouchOcclusionMode.aidl
new file mode 100644
index 0000000..106f159
--- /dev/null
+++ b/libs/input/android/os/TouchOcclusionMode.aidl
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2020, 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 android.os;
+
+
+/**
+ * Touch occlusion modes: These modes represent how windows are taken into
+ * consideration in order to decide whether to block obscured touches or
+ * not.
+ *
+ * @hide
+ */
+@Backing(type="int")
+enum TouchOcclusionMode {
+ /**
+ * Touches that pass through this window will be blocked if they are
+ * consumed by a different UID and this window is not trusted.
+ */
+ BLOCK_UNTRUSTED,
+
+ /**
+ * The window's opacity will be taken into consideration for touch
+ * occlusion rules if the touch passes through it and the window is not
+ * trusted.
+ */
+ USE_OPACITY,
+
+ /**
+ * The window won't count for touch occlusion rules if the touch passes
+ * through it.
+ */
+ ALLOW
+}
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index cacce92..fe8a567 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -11,6 +11,8 @@
cc_test {
name: "libinput_tests",
srcs: [
+ "NamedEnum_test.cpp",
+ "Flags_test.cpp",
"IdGenerator_test.cpp",
"InputChannel_test.cpp",
"InputDevice_test.cpp",
@@ -27,14 +29,18 @@
"-Wextra",
"-Werror",
],
- shared_libs: [
+ static_libs: [
"libinput",
- "libcutils",
- "libutils",
- "libbinder",
- "libui",
+ ],
+ shared_libs: [
"libbase",
- ]
+ "libbinder",
+ "libcutils",
+ "liblog",
+ "libui",
+ "libutils",
+ ],
+ test_suites: ["device-tests"],
}
// NOTE: This is a compile time test, and does not need to be
diff --git a/libs/input/tests/Flags_test.cpp b/libs/input/tests/Flags_test.cpp
new file mode 100644
index 0000000..6de030f
--- /dev/null
+++ b/libs/input/tests/Flags_test.cpp
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2020 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 <gtest/gtest.h>
+#include <input/Flags.h>
+
+#include <type_traits>
+
+namespace android::test {
+
+using namespace android::flag_operators;
+
+enum class TestFlags { ONE = 0x1, TWO = 0x2, THREE = 0x4 };
+
+TEST(Flags, Test) {
+ Flags<TestFlags> flags = TestFlags::ONE;
+ ASSERT_TRUE(flags.test(TestFlags::ONE));
+ ASSERT_FALSE(flags.test(TestFlags::TWO));
+ ASSERT_FALSE(flags.test(TestFlags::THREE));
+}
+
+TEST(Flags, Any) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO;
+ ASSERT_TRUE(flags.any(TestFlags::ONE));
+ ASSERT_TRUE(flags.any(TestFlags::TWO));
+ ASSERT_FALSE(flags.any(TestFlags::THREE));
+ ASSERT_TRUE(flags.any(TestFlags::ONE | TestFlags::TWO));
+ ASSERT_TRUE(flags.any(TestFlags::TWO | TestFlags::THREE));
+ ASSERT_TRUE(flags.any(TestFlags::ONE | TestFlags::THREE));
+ ASSERT_TRUE(flags.any(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE));
+}
+
+TEST(Flags, All) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO;
+ ASSERT_TRUE(flags.all(TestFlags::ONE));
+ ASSERT_TRUE(flags.all(TestFlags::TWO));
+ ASSERT_FALSE(flags.all(TestFlags::THREE));
+ ASSERT_TRUE(flags.all(TestFlags::ONE | TestFlags::TWO));
+ ASSERT_FALSE(flags.all(TestFlags::TWO | TestFlags::THREE));
+ ASSERT_FALSE(flags.all(TestFlags::ONE | TestFlags::THREE));
+ ASSERT_FALSE(flags.all(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE));
+}
+
+TEST(Flags, DefaultConstructor_hasNoFlagsSet) {
+ Flags<TestFlags> flags;
+ ASSERT_FALSE(flags.any(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE));
+}
+
+TEST(Flags, NotOperator_onEmptyFlagsSetsAllFlags) {
+ Flags<TestFlags> flags;
+ flags = ~flags;
+ ASSERT_TRUE(flags.all(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE));
+}
+
+TEST(Flags, NotOperator_onNonEmptyFlagsInvertsFlags) {
+ Flags<TestFlags> flags = TestFlags::TWO;
+ flags = ~flags;
+ ASSERT_TRUE(flags.all(TestFlags::ONE | TestFlags::THREE));
+ ASSERT_FALSE(flags.test(TestFlags::TWO));
+}
+
+TEST(Flags, OrOperator_withNewFlag) {
+ Flags<TestFlags> flags = TestFlags::ONE;
+ Flags<TestFlags> flags2 = flags | TestFlags::TWO;
+ ASSERT_FALSE(flags2.test(TestFlags::THREE));
+ ASSERT_TRUE(flags2.all(TestFlags::ONE | TestFlags::TWO));
+}
+
+TEST(Flags, OrOperator_withExistingFlag) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE;
+ Flags<TestFlags> flags2 = flags | TestFlags::THREE;
+ ASSERT_FALSE(flags2.test(TestFlags::TWO));
+ ASSERT_TRUE(flags2.all(TestFlags::ONE | TestFlags::THREE));
+}
+
+TEST(Flags, OrEqualsOperator_withNewFlag) {
+ Flags<TestFlags> flags;
+ flags |= TestFlags::THREE;
+ ASSERT_TRUE(flags.test(TestFlags::THREE));
+ ASSERT_FALSE(flags.any(TestFlags::ONE | TestFlags::TWO));
+}
+
+TEST(Flags, OrEqualsOperator_withExistingFlag) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE;
+ flags |= TestFlags::THREE;
+ ASSERT_TRUE(flags.all(TestFlags::ONE | TestFlags::THREE));
+ ASSERT_FALSE(flags.test(TestFlags::TWO));
+}
+
+TEST(Flags, AndOperator_withOneSetFlag) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE;
+ Flags<TestFlags> andFlags = flags & TestFlags::THREE;
+ ASSERT_TRUE(andFlags.test(TestFlags::THREE));
+ ASSERT_FALSE(andFlags.any(TestFlags::ONE | TestFlags::TWO));
+}
+
+TEST(Flags, AndOperator_withMultipleSetFlags) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE;
+ Flags<TestFlags> andFlags = flags & (TestFlags::ONE | TestFlags::THREE);
+ ASSERT_TRUE(andFlags.all(TestFlags::ONE | TestFlags::THREE));
+ ASSERT_FALSE(andFlags.test(TestFlags::TWO));
+}
+
+TEST(Flags, AndOperator_withNoSetFlags) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE;
+ Flags<TestFlags> andFlags = flags & TestFlags::TWO;
+ ASSERT_FALSE(andFlags.any(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE));
+}
+
+TEST(Flags, Equality) {
+ Flags<TestFlags> flags1 = TestFlags::ONE | TestFlags::TWO;
+ Flags<TestFlags> flags2 = TestFlags::ONE | TestFlags::TWO;
+ ASSERT_EQ(flags1, flags2);
+}
+
+TEST(Flags, Inequality) {
+ Flags<TestFlags> flags1 = TestFlags::ONE | TestFlags::TWO;
+ Flags<TestFlags> flags2 = TestFlags::ONE | TestFlags::THREE;
+ ASSERT_NE(flags1, flags2);
+}
+
+TEST(Flags, EqualsOperator) {
+ Flags<TestFlags> flags;
+ flags = TestFlags::ONE;
+ ASSERT_TRUE(flags.test(TestFlags::ONE));
+ ASSERT_FALSE(flags.any(TestFlags::TWO | TestFlags::THREE));
+}
+
+TEST(Flags, EqualsOperator_DontShareState) {
+ Flags<TestFlags> flags1 = TestFlags::ONE | TestFlags::TWO;
+ Flags<TestFlags> flags2 = flags1;
+ ASSERT_EQ(flags1, flags2);
+
+ flags1 &= TestFlags::TWO;
+ ASSERT_NE(flags1, flags2);
+}
+
+TEST(Flags, GetValue) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO;
+ ASSERT_EQ(flags.get(), 0x3);
+}
+
+TEST(Flags, String_NoFlags) {
+ Flags<TestFlags> flags;
+ ASSERT_EQ(flags.string(), "0x0");
+}
+
+TEST(Flags, String_KnownValues) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO;
+ ASSERT_EQ(flags.string(), "ONE | TWO");
+}
+
+TEST(Flags, String_UnknownValues) {
+ auto flags = Flags<TestFlags>(0b1011);
+ ASSERT_EQ(flags.string(), "ONE | TWO | 0x00000008");
+}
+
+TEST(FlagsIterator, IteratesOverAllFlags) {
+ Flags<TestFlags> flags1 = TestFlags::ONE | TestFlags::TWO;
+ Flags<TestFlags> flags2;
+ for (TestFlags f : flags1) {
+ flags2 |= f;
+ }
+ ASSERT_EQ(flags2, flags1);
+}
+
+TEST(FlagsIterator, IteratesInExpectedOrder) {
+ const std::vector<TestFlags> flagOrder = {TestFlags::ONE, TestFlags::TWO};
+ Flags<TestFlags> flags;
+ for (TestFlags f : flagOrder) {
+ flags |= f;
+ }
+
+ size_t idx = 0;
+ auto iter = flags.begin();
+ while (iter != flags.end() && idx < flagOrder.size()) {
+ // Make sure the order is what we expect
+ ASSERT_EQ(*iter, flagOrder[idx]);
+ iter++;
+ idx++;
+ }
+ ASSERT_EQ(iter, flags.end());
+}
+TEST(FlagsIterator, PostFixIncrement) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO;
+ auto iter = flags.begin();
+ ASSERT_EQ(*(iter++), TestFlags::ONE);
+ ASSERT_EQ(*iter, TestFlags::TWO);
+ ASSERT_EQ(*(iter++), TestFlags::TWO);
+ ASSERT_EQ(iter, flags.end());
+}
+
+TEST(FlagsIterator, PreFixIncrement) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO;
+ auto iter = flags.begin();
+ ASSERT_EQ(*++iter, TestFlags::TWO);
+ ASSERT_EQ(++iter, flags.end());
+}
+
+TEST(FlagNames, RuntimeFlagName) {
+ TestFlags f = TestFlags::ONE;
+ ASSERT_EQ(flag_name(f), "ONE");
+}
+
+TEST(FlagNames, RuntimeUnknownFlagName) {
+ TestFlags f = static_cast<TestFlags>(0x8);
+ ASSERT_EQ(flag_name(f), std::nullopt);
+}
+
+TEST(FlagNames, CompileTimeFlagName) {
+ static_assert(flag_name<TestFlags::TWO>() == "TWO");
+}
+
+} // namespace android::test
\ No newline at end of file
diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp
index ada275d..0661261 100644
--- a/libs/input/tests/InputChannel_test.cpp
+++ b/libs/input/tests/InputChannel_test.cpp
@@ -23,6 +23,7 @@
#include <errno.h>
#include <binder/Binder.h>
+#include <binder/Parcel.h>
#include <gtest/gtest.h>
#include <input/InputTransport.h>
#include <utils/StopWatch.h>
@@ -32,9 +33,6 @@
namespace android {
class InputChannelTest : public testing::Test {
-protected:
- virtual void SetUp() { }
- virtual void TearDown() { }
};
@@ -46,7 +44,7 @@
android::base::unique_fd sendFd(pipe.sendFd);
- sp<InputChannel> inputChannel =
+ std::unique_ptr<InputChannel> inputChannel =
InputChannel::create("channel name", std::move(sendFd), new BBinder());
EXPECT_NE(inputChannel, nullptr) << "channel should be successfully created";
@@ -61,14 +59,14 @@
TEST_F(InputChannelTest, SetAndGetToken) {
Pipe pipe;
sp<IBinder> token = new BBinder();
- sp<InputChannel> channel =
+ std::unique_ptr<InputChannel> channel =
InputChannel::create("test channel", android::base::unique_fd(pipe.sendFd), token);
EXPECT_EQ(token, channel->getConnectionToken());
}
TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) {
- sp<InputChannel> serverChannel, clientChannel;
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
status_t result = InputChannel::openInputChannelPair("channel name",
serverChannel, clientChannel);
@@ -102,7 +100,7 @@
InputMessage clientReply;
memset(&clientReply, 0, sizeof(InputMessage));
clientReply.header.type = InputMessage::Type::FINISHED;
- clientReply.body.finished.seq = 0x11223344;
+ clientReply.header.seq = 0x11223344;
clientReply.body.finished.handled = true;
EXPECT_EQ(OK, clientChannel->sendMessage(&clientReply))
<< "client channel should be able to send message to server channel";
@@ -112,14 +110,14 @@
<< "server channel should be able to receive message from client channel";
EXPECT_EQ(clientReply.header.type, serverReply.header.type)
<< "server channel should receive the correct message from client channel";
- EXPECT_EQ(clientReply.body.finished.seq, serverReply.body.finished.seq)
+ EXPECT_EQ(clientReply.header.seq, serverReply.header.seq)
<< "server channel should receive the correct message from client channel";
EXPECT_EQ(clientReply.body.finished.handled, serverReply.body.finished.handled)
<< "server channel should receive the correct message from client channel";
}
TEST_F(InputChannelTest, ReceiveSignal_WhenNoSignalPresent_ReturnsAnError) {
- sp<InputChannel> serverChannel, clientChannel;
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
status_t result = InputChannel::openInputChannelPair("channel name",
serverChannel, clientChannel);
@@ -133,7 +131,7 @@
}
TEST_F(InputChannelTest, ReceiveSignal_WhenPeerClosed_ReturnsAnError) {
- sp<InputChannel> serverChannel, clientChannel;
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
status_t result = InputChannel::openInputChannelPair("channel name",
serverChannel, clientChannel);
@@ -141,7 +139,7 @@
ASSERT_EQ(OK, result)
<< "should have successfully opened a channel pair";
- serverChannel.clear(); // close server channel
+ serverChannel.reset(); // close server channel
InputMessage msg;
EXPECT_EQ(DEAD_OBJECT, clientChannel->receiveMessage(&msg))
@@ -149,7 +147,7 @@
}
TEST_F(InputChannelTest, SendSignal_WhenPeerClosed_ReturnsAnError) {
- sp<InputChannel> serverChannel, clientChannel;
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
status_t result = InputChannel::openInputChannelPair("channel name",
serverChannel, clientChannel);
@@ -157,7 +155,7 @@
ASSERT_EQ(OK, result)
<< "should have successfully opened a channel pair";
- serverChannel.clear(); // close server channel
+ serverChannel.reset(); // close server channel
InputMessage msg;
msg.header.type = InputMessage::Type::KEY;
@@ -166,7 +164,7 @@
}
TEST_F(InputChannelTest, SendAndReceive_MotionClassification) {
- sp<InputChannel> serverChannel, clientChannel;
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
status_t result = InputChannel::openInputChannelPair("channel name",
serverChannel, clientChannel);
ASSERT_EQ(OK, result)
@@ -180,7 +178,7 @@
InputMessage serverMsg = {}, clientMsg;
serverMsg.header.type = InputMessage::Type::MOTION;
- serverMsg.body.motion.seq = 1;
+ serverMsg.header.seq = 1;
serverMsg.body.motion.pointerCount = 1;
for (MotionClassification classification : classifications) {
@@ -197,5 +195,36 @@
}
}
+TEST_F(InputChannelTest, InputChannelParcelAndUnparcel) {
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
+
+ status_t result =
+ InputChannel::openInputChannelPair("channel parceling", serverChannel, clientChannel);
+
+ ASSERT_EQ(OK, result) << "should have successfully opened a channel pair";
+
+ InputChannel chan;
+ Parcel parcel;
+ ASSERT_EQ(OK, serverChannel->writeToParcel(&parcel));
+ parcel.setDataPosition(0);
+ chan.readFromParcel(&parcel);
+
+ EXPECT_EQ(chan == *serverChannel, true)
+ << "inputchannel should be equal after parceling and unparceling.\n"
+ << "name " << chan.getName() << " name " << serverChannel->getName();
+}
+
+TEST_F(InputChannelTest, DuplicateChannelAndAssertEqual) {
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
+
+ status_t result =
+ InputChannel::openInputChannelPair("channel dup", serverChannel, clientChannel);
+
+ ASSERT_EQ(OK, result) << "should have successfully opened a channel pair";
+
+ std::unique_ptr<InputChannel> dupChan = serverChannel->dup();
+
+ EXPECT_EQ(*serverChannel == *dupChan, true) << "inputchannel should be equal after duplication";
+}
} // namespace android
diff --git a/libs/input/tests/InputDevice_test.cpp b/libs/input/tests/InputDevice_test.cpp
index c174ae9..f8f2f4e 100644
--- a/libs/input/tests/InputDevice_test.cpp
+++ b/libs/input/tests/InputDevice_test.cpp
@@ -14,9 +14,12 @@
* limitations under the License.
*/
-
+#include <binder/Binder.h>
+#include <binder/Parcel.h>
#include <gtest/gtest.h>
#include <input/InputDevice.h>
+#include <input/KeyLayoutMap.h>
+#include <input/Keyboard.h>
namespace android {
@@ -31,4 +34,52 @@
ASSERT_EQ(std::string("deviceName-123_version_C_"), identifier.getCanonicalName());
}
-} // namespace android
\ No newline at end of file
+class InputDeviceKeyMapTest : public testing::Test {
+protected:
+ void loadKeyLayout(const char* name) {
+ std::string path =
+ getInputDeviceConfigurationFilePathByName(name,
+ InputDeviceConfigurationFileType::
+ KEY_LAYOUT);
+ ASSERT_FALSE(path.empty());
+ base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(path);
+ ASSERT_TRUE(ret.ok()) << "Cannot load KeyLayout at " << path;
+ mKeyMap.keyLayoutMap = std::move(*ret);
+ mKeyMap.keyLayoutFile = path;
+ }
+
+ void loadKeyCharacterMap(const char* name) {
+ InputDeviceIdentifier identifier;
+ identifier.name = name;
+ std::string path =
+ getInputDeviceConfigurationFilePathByName(identifier.getCanonicalName(),
+ InputDeviceConfigurationFileType::
+ KEY_CHARACTER_MAP);
+ ASSERT_FALSE(path.empty()) << "KeyCharacterMap for " << name << " not found";
+ base::Result<std::shared_ptr<KeyCharacterMap>> ret =
+ KeyCharacterMap::load(path, KeyCharacterMap::Format::BASE);
+ ASSERT_TRUE(ret.ok()) << "Cannot load KeyCharacterMap at " << path;
+ mKeyMap.keyCharacterMap = *ret;
+ mKeyMap.keyCharacterMapFile = path;
+ }
+
+ virtual void SetUp() override {
+ loadKeyLayout("Generic");
+ loadKeyCharacterMap("Generic");
+ }
+
+ virtual void TearDown() override {}
+
+ KeyMap mKeyMap;
+};
+
+TEST_F(InputDeviceKeyMapTest, keyCharacterMapParcelingTest) {
+ Parcel parcel;
+ mKeyMap.keyCharacterMap->writeToParcel(&parcel);
+ parcel.setDataPosition(0);
+ std::shared_ptr<KeyCharacterMap> map = KeyCharacterMap::readFromParcel(&parcel);
+ // Verify the key character map is the same as original
+ ASSERT_EQ(*map, *mKeyMap.keyCharacterMap);
+}
+
+} // namespace android
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index 553dc4c..601d8da 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -17,6 +17,7 @@
#include <array>
#include <math.h>
+#include <attestation/HmacKeyManager.h>
#include <binder/Parcel.h>
#include <gtest/gtest.h>
#include <input/Input.h>
@@ -225,6 +226,7 @@
static constexpr float Y_OFFSET = 1.1;
int32_t mId;
+ ui::Transform mTransform;
void initializeEventWithHistory(MotionEvent* event);
void assertEqualsEventWithHistory(const MotionEvent* event);
@@ -233,6 +235,7 @@
void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
mId = InputEvent::nextId();
+ mTransform.set({X_SCALE, 0, X_OFFSET, 0, Y_SCALE, Y_OFFSET, 0, 0, 1});
PointerProperties pointerProperties[2];
pointerProperties[0].clear();
@@ -266,7 +269,7 @@
event->initialize(mId, 2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC,
AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED,
AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY,
- MotionClassification::NONE, X_SCALE, Y_SCALE, X_OFFSET, Y_OFFSET, 2.0f, 2.1f,
+ MotionClassification::NONE, mTransform, 2.0f, 2.1f,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties,
pointerCoords);
@@ -326,8 +329,7 @@
ASSERT_EQ(AMETA_ALT_ON, event->getMetaState());
ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, event->getButtonState());
ASSERT_EQ(MotionClassification::NONE, event->getClassification());
- EXPECT_EQ(X_SCALE, event->getXScale());
- EXPECT_EQ(Y_SCALE, event->getYScale());
+ EXPECT_EQ(mTransform, event->getTransform());
ASSERT_EQ(X_OFFSET, event->getXOffset());
ASSERT_EQ(Y_OFFSET, event->getYOffset());
ASSERT_EQ(2.0f, event->getXPrecision());
@@ -545,7 +547,7 @@
ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&outEvent));
}
-static void setRotationMatrix(float matrix[9], float angle) {
+static void setRotationMatrix(std::array<float, 9>& matrix, float angle) {
float sin = sinf(angle);
float cos = cosf(angle);
matrix[0] = cos;
@@ -584,13 +586,14 @@
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle);
}
MotionEvent event;
+ ui::Transform identityTransform;
event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID,
INVALID_HMAC, AMOTION_EVENT_ACTION_MOVE, 0 /*actionButton*/, 0 /*flags*/,
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
- MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0 /*xOffset*/,
- 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/,
- 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/, 0 /*downTime*/,
- 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
+ MotionClassification::NONE, identityTransform, 0 /*xPrecision*/,
+ 0 /*yPrecision*/, 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/,
+ 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties,
+ pointerCoords);
float originalRawX = 0 + 3;
float originalRawY = -RADIUS + 2;
@@ -606,7 +609,7 @@
ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
// Apply a rotation about the origin by ROTATION degrees clockwise.
- float matrix[9];
+ std::array<float, 9> matrix;
setRotationMatrix(matrix, ROTATION * PI_180);
event.transform(matrix);
@@ -648,11 +651,12 @@
pointerCoords[i].clear();
}
+ ui::Transform identityTransform;
for (MotionClassification classification : classifications) {
event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN,
DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
- AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification, 1 /*xScale*/,
- 1 /*yScale*/, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification,
+ identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, 0 /*eventTime*/,
pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(classification, event.getClassification());
@@ -670,10 +674,11 @@
pointerCoords[i].clear();
}
+ ui::Transform identityTransform;
event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID,
INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE,
- AMETA_NONE, 0, MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0, 0, 0,
- 0, 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/,
+ AMETA_NONE, 0, MotionClassification::NONE, identityTransform, 0, 0,
+ 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/,
0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
event.offsetLocation(20, 60);
ASSERT_EQ(280, event.getRawXCursorPosition());
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 8e2eec8..088e00b 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -20,53 +20,50 @@
#include <sys/mman.h>
#include <time.h>
+#include <attestation/HmacKeyManager.h>
#include <cutils/ashmem.h>
#include <gtest/gtest.h>
#include <input/InputTransport.h>
-#include <utils/Timers.h>
#include <utils/StopWatch.h>
+#include <utils/Timers.h>
+
+using android::base::Result;
namespace android {
class InputPublisherAndConsumerTest : public testing::Test {
protected:
- sp<InputChannel> serverChannel, clientChannel;
- InputPublisher* mPublisher;
- InputConsumer* mConsumer;
+ std::shared_ptr<InputChannel> mServerChannel, mClientChannel;
+ std::unique_ptr<InputPublisher> mPublisher;
+ std::unique_ptr<InputConsumer> mConsumer;
PreallocatedInputEventFactory mEventFactory;
- virtual void SetUp() {
+ void SetUp() override {
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
status_t result = InputChannel::openInputChannelPair("channel name",
serverChannel, clientChannel);
ASSERT_EQ(OK, result);
+ mServerChannel = std::move(serverChannel);
+ mClientChannel = std::move(clientChannel);
- mPublisher = new InputPublisher(serverChannel);
- mConsumer = new InputConsumer(clientChannel);
- }
-
- virtual void TearDown() {
- if (mPublisher) {
- delete mPublisher;
- mPublisher = nullptr;
- }
-
- if (mConsumer) {
- delete mConsumer;
- mConsumer = nullptr;
- }
-
- serverChannel.clear();
- clientChannel.clear();
+ mPublisher = std::make_unique<InputPublisher>(mServerChannel);
+ mConsumer = std::make_unique<InputConsumer>(mClientChannel);
}
void PublishAndConsumeKeyEvent();
void PublishAndConsumeMotionEvent();
void PublishAndConsumeFocusEvent();
+ void PublishAndConsumeCaptureEvent();
+ void PublishAndConsumeDragEvent();
};
TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
- EXPECT_EQ(serverChannel.get(), mPublisher->getChannel().get());
- EXPECT_EQ(clientChannel.get(), mConsumer->getChannel().get());
+ ASSERT_NE(nullptr, mPublisher->getChannel());
+ ASSERT_NE(nullptr, mConsumer->getChannel());
+ EXPECT_EQ(mServerChannel.get(), mPublisher->getChannel().get());
+ EXPECT_EQ(mClientChannel.get(), mConsumer->getChannel().get());
+ ASSERT_EQ(mPublisher->getChannel()->getConnectionToken(),
+ mConsumer->getChannel()->getConnectionToken());
}
void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() {
@@ -88,6 +85,7 @@
constexpr int32_t repeatCount = 1;
constexpr nsecs_t downTime = 3;
constexpr nsecs_t eventTime = 4;
+ const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
status = mPublisher->publishKeyEvent(seq, eventId, deviceId, source, displayId, hmac, action,
flags, keyCode, scanCode, metaState, repeatCount, downTime,
@@ -126,15 +124,16 @@
ASSERT_EQ(OK, status)
<< "consumer sendFinishedSignal should return OK";
- uint32_t finishedSeq = 0;
- bool handled = false;
- status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled);
- ASSERT_EQ(OK, status)
- << "publisher receiveFinishedSignal should return OK";
- ASSERT_EQ(seq, finishedSeq)
- << "publisher receiveFinishedSignal should have returned the original sequence number";
- ASSERT_TRUE(handled)
- << "publisher receiveFinishedSignal should have set handled to consumer's reply";
+ Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+ ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+ ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+ const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
+ ASSERT_EQ(seq, finish.seq)
+ << "receiveConsumerResponse should have returned the original sequence number";
+ ASSERT_TRUE(finish.handled)
+ << "receiveConsumerResponse should have set handled to consumer's reply";
+ ASSERT_GE(finish.consumeTime, publishTime)
+ << "finished signal's consume time should be greater than publish time";
}
void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() {
@@ -166,6 +165,7 @@
constexpr nsecs_t downTime = 3;
constexpr size_t pointerCount = 3;
constexpr nsecs_t eventTime = 4;
+ const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
PointerProperties pointerProperties[pointerCount];
PointerCoords pointerCoords[pointerCount];
for (size_t i = 0; i < pointerCount; i++) {
@@ -185,12 +185,13 @@
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i);
}
+ ui::Transform transform;
+ transform.set({xScale, 0, xOffset, 0, yScale, yOffset, 0, 0, 1});
status = mPublisher->publishMotionEvent(seq, eventId, deviceId, source, displayId, hmac, action,
actionButton, flags, edgeFlags, metaState, buttonState,
- classification, xScale, yScale, xOffset, yOffset,
- xPrecision, yPrecision, xCursorPosition,
- yCursorPosition, downTime, eventTime, pointerCount,
- pointerProperties, pointerCoords);
+ classification, transform, xPrecision, yPrecision,
+ xCursorPosition, yCursorPosition, downTime, eventTime,
+ pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(OK, status)
<< "publisher publishMotionEvent should return OK";
@@ -218,8 +219,7 @@
EXPECT_EQ(metaState, motionEvent->getMetaState());
EXPECT_EQ(buttonState, motionEvent->getButtonState());
EXPECT_EQ(classification, motionEvent->getClassification());
- EXPECT_EQ(xScale, motionEvent->getXScale());
- EXPECT_EQ(yScale, motionEvent->getYScale());
+ EXPECT_EQ(transform, motionEvent->getTransform());
EXPECT_EQ(xOffset, motionEvent->getXOffset());
EXPECT_EQ(yOffset, motionEvent->getYOffset());
EXPECT_EQ(xPrecision, motionEvent->getXPrecision());
@@ -266,15 +266,16 @@
ASSERT_EQ(OK, status)
<< "consumer sendFinishedSignal should return OK";
- uint32_t finishedSeq = 0;
- bool handled = true;
- status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled);
- ASSERT_EQ(OK, status)
- << "publisher receiveFinishedSignal should return OK";
- ASSERT_EQ(seq, finishedSeq)
- << "publisher receiveFinishedSignal should have returned the original sequence number";
- ASSERT_FALSE(handled)
- << "publisher receiveFinishedSignal should have set handled to consumer's reply";
+ Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+ ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+ ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+ const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
+ ASSERT_EQ(seq, finish.seq)
+ << "receiveConsumerResponse should have returned the original sequence number";
+ ASSERT_FALSE(finish.handled)
+ << "receiveConsumerResponse should have set handled to consumer's reply";
+ ASSERT_GE(finish.consumeTime, publishTime)
+ << "finished signal's consume time should be greater than publish time";
}
void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() {
@@ -284,9 +285,10 @@
int32_t eventId = InputEvent::nextId();
constexpr bool hasFocus = true;
constexpr bool inTouchMode = true;
+ const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
status = mPublisher->publishFocusEvent(seq, eventId, hasFocus, inTouchMode);
- ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK";
+ ASSERT_EQ(OK, status) << "publisher publishFocusEvent should return OK";
uint32_t consumeSeq;
InputEvent* event;
@@ -306,14 +308,117 @@
status = mConsumer->sendFinishedSignal(seq, true);
ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
- uint32_t finishedSeq = 0;
- bool handled = false;
- status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled);
- ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK";
- ASSERT_EQ(seq, finishedSeq)
- << "publisher receiveFinishedSignal should have returned the original sequence number";
- ASSERT_TRUE(handled)
- << "publisher receiveFinishedSignal should have set handled to consumer's reply";
+ Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+ ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+ ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+ const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
+
+ ASSERT_EQ(seq, finish.seq)
+ << "receiveConsumerResponse should have returned the original sequence number";
+ ASSERT_TRUE(finish.handled)
+ << "receiveConsumerResponse should have set handled to consumer's reply";
+ ASSERT_GE(finish.consumeTime, publishTime)
+ << "finished signal's consume time should be greater than publish time";
+}
+
+void InputPublisherAndConsumerTest::PublishAndConsumeCaptureEvent() {
+ status_t status;
+
+ constexpr uint32_t seq = 42;
+ int32_t eventId = InputEvent::nextId();
+ constexpr bool captureEnabled = true;
+ const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
+ status = mPublisher->publishCaptureEvent(seq, eventId, captureEnabled);
+ ASSERT_EQ(OK, status) << "publisher publishCaptureEvent should return OK";
+
+ uint32_t consumeSeq;
+ InputEvent* event;
+ status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
+ ASSERT_EQ(OK, status) << "consumer consume should return OK";
+
+ ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event";
+ ASSERT_EQ(AINPUT_EVENT_TYPE_CAPTURE, event->getType())
+ << "consumer should have returned a capture event";
+
+ const CaptureEvent* captureEvent = static_cast<CaptureEvent*>(event);
+ EXPECT_EQ(seq, consumeSeq);
+ EXPECT_EQ(eventId, captureEvent->getId());
+ EXPECT_EQ(captureEnabled, captureEvent->getPointerCaptureEnabled());
+
+ status = mConsumer->sendFinishedSignal(seq, true);
+ ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
+
+ Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+ ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+ ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+ const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
+ ASSERT_EQ(seq, finish.seq)
+ << "receiveConsumerResponse should have returned the original sequence number";
+ ASSERT_TRUE(finish.handled)
+ << "receiveConsumerResponse should have set handled to consumer's reply";
+ ASSERT_GE(finish.consumeTime, publishTime)
+ << "finished signal's consume time should be greater than publish time";
+}
+
+void InputPublisherAndConsumerTest::PublishAndConsumeDragEvent() {
+ status_t status;
+
+ constexpr uint32_t seq = 15;
+ int32_t eventId = InputEvent::nextId();
+ constexpr bool isExiting = false;
+ constexpr float x = 10;
+ constexpr float y = 15;
+ const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
+ status = mPublisher->publishDragEvent(seq, eventId, x, y, isExiting);
+ ASSERT_EQ(OK, status) << "publisher publishDragEvent should return OK";
+
+ uint32_t consumeSeq;
+ InputEvent* event;
+ status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
+ ASSERT_EQ(OK, status) << "consumer consume should return OK";
+
+ ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event";
+ ASSERT_EQ(AINPUT_EVENT_TYPE_DRAG, event->getType())
+ << "consumer should have returned a drag event";
+
+ const DragEvent& dragEvent = static_cast<const DragEvent&>(*event);
+ EXPECT_EQ(seq, consumeSeq);
+ EXPECT_EQ(eventId, dragEvent.getId());
+ EXPECT_EQ(isExiting, dragEvent.isExiting());
+ EXPECT_EQ(x, dragEvent.getX());
+ EXPECT_EQ(y, dragEvent.getY());
+
+ status = mConsumer->sendFinishedSignal(seq, true);
+ ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
+
+ Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+ ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+ ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+ const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
+ ASSERT_EQ(seq, finish.seq)
+ << "receiveConsumerResponse should have returned the original sequence number";
+ ASSERT_TRUE(finish.handled)
+ << "receiveConsumerResponse should have set handled to consumer's reply";
+ ASSERT_GE(finish.consumeTime, publishTime)
+ << "finished signal's consume time should be greater than publish time";
+}
+
+TEST_F(InputPublisherAndConsumerTest, SendTimeline) {
+ const int32_t inputEventId = 20;
+ std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
+ graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 30;
+ graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = 40;
+ status_t status = mConsumer->sendTimeline(inputEventId, graphicsTimeline);
+ ASSERT_EQ(OK, status);
+
+ Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+ ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+ ASSERT_TRUE(std::holds_alternative<InputPublisher::Timeline>(*result));
+ const InputPublisher::Timeline& timeline = std::get<InputPublisher::Timeline>(*result);
+ ASSERT_EQ(inputEventId, timeline.inputEventId);
+ ASSERT_EQ(graphicsTimeline, timeline.graphicsTimeline);
}
TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) {
@@ -328,6 +433,14 @@
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeFocusEvent());
}
+TEST_F(InputPublisherAndConsumerTest, PublishCaptureEvent_EndToEnd) {
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeCaptureEvent());
+}
+
+TEST_F(InputPublisherAndConsumerTest, PublishDragEvent_EndToEnd) {
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeDragEvent());
+}
+
TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZero_ReturnsError) {
status_t status;
const size_t pointerCount = 1;
@@ -338,10 +451,10 @@
pointerCoords[i].clear();
}
+ ui::Transform identityTransform;
status = mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
- 0, 0, 0, MotionClassification::NONE, 1 /* xScale */,
- 1 /* yScale */, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ 0, 0, 0, MotionClassification::NONE, identityTransform,
+ 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
@@ -354,10 +467,10 @@
PointerProperties pointerProperties[pointerCount];
PointerCoords pointerCoords[pointerCount];
+ ui::Transform identityTransform;
status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
- 0, 0, 0, MotionClassification::NONE, 1 /* xScale */,
- 1 /* yScale */, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ 0, 0, 0, MotionClassification::NONE, identityTransform,
+ 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
@@ -375,10 +488,10 @@
pointerCoords[i].clear();
}
+ ui::Transform identityTransform;
status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
- 0, 0, 0, MotionClassification::NONE, 1 /* xScale */,
- 1 /* yScale */, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ 0, 0, 0, MotionClassification::NONE, identityTransform,
+ 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
@@ -392,6 +505,10 @@
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeFocusEvent());
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeCaptureEvent());
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeDragEvent());
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
}
} // namespace android
diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp
index d1cb527..c18a17f 100644
--- a/libs/input/tests/InputWindow_test.cpp
+++ b/libs/input/tests/InputWindow_test.cpp
@@ -22,17 +22,19 @@
#include <input/InputWindow.h>
#include <input/InputTransport.h>
+using std::chrono_literals::operator""s;
+
namespace android {
namespace test {
TEST(InputWindowInfo, ParcellingWithoutToken) {
- InputWindowInfo i;
+ InputWindowInfo i, i2;
i.token = nullptr;
Parcel p;
- ASSERT_EQ(OK, i.write(p));
+ ASSERT_EQ(OK, i.writeToParcel(&p));
p.setDataPosition(0);
- InputWindowInfo i2 = InputWindowInfo::read(p);
+ i2.readFromParcel(&p);
ASSERT_TRUE(i2.token == nullptr);
}
@@ -42,40 +44,44 @@
i.token = new BBinder();
i.id = 1;
i.name = "Foobar";
- i.layoutParamsFlags = 7;
- i.layoutParamsType = 39;
- i.dispatchingTimeout = 12;
+ i.flags = InputWindowInfo::Flag::SLIPPERY;
+ i.type = InputWindowInfo::Type::INPUT_METHOD;
+ i.dispatchingTimeout = 12s;
i.frameLeft = 93;
i.frameTop = 34;
i.frameRight = 16;
i.frameBottom = 19;
i.surfaceInset = 17;
i.globalScaleFactor = 0.3;
- i.windowXScale = 0.4;
- i.windowYScale = 0.5;
+ i.alpha = 0.7;
+ i.transform.set({0.4, -1, 100, 0.5, 0, 40, 0, 0, 1});
i.visible = false;
- i.canReceiveKeys = false;
- i.hasFocus = false;
+ i.focusable = false;
i.hasWallpaper = false;
i.paused = false;
+ i.touchOcclusionMode = TouchOcclusionMode::ALLOW;
i.ownerPid = 19;
i.ownerUid = 24;
- i.inputFeatures = 29;
+ i.packageName = "com.example.package";
+ i.inputFeatures = InputWindowInfo::Feature::DISABLE_USER_ACTIVITY;
i.displayId = 34;
i.portalToDisplayId = 2;
i.replaceTouchableRegionWithCrop = true;
i.touchableRegionCropHandle = touchableRegionCropHandle;
+ i.applicationInfo.name = "ApplicationFooBar";
+ i.applicationInfo.token = new BBinder();
+ i.applicationInfo.dispatchingTimeoutMillis = 0x12345678ABCD;
Parcel p;
- i.write(p);
-
+ i.writeToParcel(&p);
p.setDataPosition(0);
- InputWindowInfo i2 = InputWindowInfo::read(p);
+ InputWindowInfo i2;
+ i2.readFromParcel(&p);
ASSERT_EQ(i.token, i2.token);
ASSERT_EQ(i.id, i2.id);
ASSERT_EQ(i.name, i2.name);
- ASSERT_EQ(i.layoutParamsFlags, i2.layoutParamsFlags);
- ASSERT_EQ(i.layoutParamsType, i2.layoutParamsType);
+ ASSERT_EQ(i.flags, i2.flags);
+ ASSERT_EQ(i.type, i2.type);
ASSERT_EQ(i.dispatchingTimeout, i2.dispatchingTimeout);
ASSERT_EQ(i.frameLeft, i2.frameLeft);
ASSERT_EQ(i.frameTop, i2.frameTop);
@@ -83,20 +89,36 @@
ASSERT_EQ(i.frameBottom, i2.frameBottom);
ASSERT_EQ(i.surfaceInset, i2.surfaceInset);
ASSERT_EQ(i.globalScaleFactor, i2.globalScaleFactor);
- ASSERT_EQ(i.windowXScale, i2.windowXScale);
- ASSERT_EQ(i.windowYScale, i2.windowYScale);
+ ASSERT_EQ(i.alpha, i2.alpha);
+ ASSERT_EQ(i.transform, i2.transform);
ASSERT_EQ(i.visible, i2.visible);
- ASSERT_EQ(i.canReceiveKeys, i2.canReceiveKeys);
- ASSERT_EQ(i.hasFocus, i2.hasFocus);
+ ASSERT_EQ(i.focusable, i2.focusable);
ASSERT_EQ(i.hasWallpaper, i2.hasWallpaper);
ASSERT_EQ(i.paused, i2.paused);
+ ASSERT_EQ(i.touchOcclusionMode, i2.touchOcclusionMode);
ASSERT_EQ(i.ownerPid, i2.ownerPid);
ASSERT_EQ(i.ownerUid, i2.ownerUid);
+ ASSERT_EQ(i.packageName, i2.packageName);
ASSERT_EQ(i.inputFeatures, i2.inputFeatures);
ASSERT_EQ(i.displayId, i2.displayId);
ASSERT_EQ(i.portalToDisplayId, i2.portalToDisplayId);
ASSERT_EQ(i.replaceTouchableRegionWithCrop, i2.replaceTouchableRegionWithCrop);
ASSERT_EQ(i.touchableRegionCropHandle, i2.touchableRegionCropHandle);
+ ASSERT_EQ(i.applicationInfo, i2.applicationInfo);
+}
+
+TEST(InputApplicationInfo, Parcelling) {
+ InputApplicationInfo i;
+ i.token = new BBinder();
+ i.name = "ApplicationFooBar";
+ i.dispatchingTimeoutMillis = 0x12345678ABCD;
+
+ Parcel p;
+ ASSERT_EQ(i.writeToParcel(&p), OK);
+ p.setDataPosition(0);
+ InputApplicationInfo i2;
+ ASSERT_EQ(i2.readFromParcel(&p), OK);
+ ASSERT_EQ(i, i2);
}
} // namespace test
diff --git a/libs/input/tests/NamedEnum_test.cpp b/libs/input/tests/NamedEnum_test.cpp
new file mode 100644
index 0000000..74a0044
--- /dev/null
+++ b/libs/input/tests/NamedEnum_test.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2020 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 <gtest/gtest.h>
+#include <input/NamedEnum.h>
+
+namespace android {
+
+// Test enum class maximum enum value smaller than default maximum of 8.
+enum class TestEnums { ZERO = 0x0, ONE = 0x1, TWO = 0x2, THREE = 0x3, SEVEN = 0x7 };
+// Big enum contains enum values greater than default maximum of 8.
+enum class TestBigEnums { ZERO = 0x0, FIFTEEN = 0xF };
+
+// Declared to specialize the maximum enum since the enum size exceeds 8 by default.
+template <>
+constexpr size_t NamedEnum::max<TestBigEnums> = 16;
+
+namespace test {
+using android::TestBigEnums;
+using android::TestEnums;
+
+TEST(NamedEnum, RuntimeNamedEnum) {
+ TestEnums e = TestEnums::ZERO;
+ ASSERT_EQ(NamedEnum::enum_name(e), "ZERO");
+
+ e = TestEnums::ONE;
+ ASSERT_EQ(NamedEnum::enum_name(e), "ONE");
+
+ e = TestEnums::THREE;
+ ASSERT_EQ(NamedEnum::enum_name(e), "THREE");
+
+ e = TestEnums::SEVEN;
+ ASSERT_EQ(NamedEnum::enum_name(e), "SEVEN");
+}
+
+// Test big enum
+TEST(NamedEnum, RuntimeBigNamedEnum) {
+ TestBigEnums e = TestBigEnums::ZERO;
+ ASSERT_EQ(NamedEnum::enum_name(e), "ZERO");
+
+ e = TestBigEnums::FIFTEEN;
+ ASSERT_EQ(NamedEnum::enum_name(e), "FIFTEEN");
+}
+
+TEST(NamedEnum, RuntimeNamedEnumAsString) {
+ TestEnums e = TestEnums::ZERO;
+ ASSERT_EQ(NamedEnum::string(e), "ZERO");
+
+ e = TestEnums::ONE;
+ ASSERT_EQ(NamedEnum::string(e), "ONE");
+
+ e = TestEnums::THREE;
+ ASSERT_EQ(NamedEnum::string(e), "THREE");
+
+ e = TestEnums::SEVEN;
+ ASSERT_EQ(NamedEnum::string(e), "SEVEN");
+}
+
+TEST(NamedEnum, RuntimeBigNamedEnumAsString) {
+ TestBigEnums e = TestBigEnums::ZERO;
+ ASSERT_EQ(NamedEnum::string(e), "ZERO");
+
+ e = TestBigEnums::FIFTEEN;
+ ASSERT_EQ(NamedEnum::string(e), "FIFTEEN");
+}
+
+TEST(NamedEnum, RuntimeUnknownNamedEnum) {
+ TestEnums e = static_cast<TestEnums>(0x5);
+ ASSERT_EQ(NamedEnum::enum_name(e), std::nullopt);
+ e = static_cast<TestEnums>(0x9);
+ ASSERT_EQ(NamedEnum::enum_name(e), std::nullopt);
+}
+
+TEST(NamedEnum, RuntimeUnknownNamedEnumAsString) {
+ TestEnums e = static_cast<TestEnums>(0x5);
+ ASSERT_EQ(NamedEnum::string(e), "05");
+ e = static_cast<TestEnums>(0x9);
+ ASSERT_EQ(NamedEnum::string(e, "0x%08x"), "0x00000009");
+}
+
+TEST(NamedEnum, CompileTimeFlagName) {
+ static_assert(NamedEnum::enum_name<TestEnums::TWO>() == "TWO");
+ static_assert(NamedEnum::enum_name<TestEnums::THREE>() == "THREE");
+}
+
+} // namespace test
+
+} // namespace android
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 1fe7bb9..585779e 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -34,8 +34,7 @@
void TestInputMessageAlignment() {
CHECK_OFFSET(InputMessage, body, 8);
- CHECK_OFFSET(InputMessage::Body::Key, seq, 0);
- CHECK_OFFSET(InputMessage::Body::Key, eventId, 4);
+ CHECK_OFFSET(InputMessage::Body::Key, eventId, 0);
CHECK_OFFSET(InputMessage::Body::Key, eventTime, 8);
CHECK_OFFSET(InputMessage::Body::Key, deviceId, 16);
CHECK_OFFSET(InputMessage::Body::Key, source, 20);
@@ -49,8 +48,8 @@
CHECK_OFFSET(InputMessage::Body::Key, repeatCount, 80);
CHECK_OFFSET(InputMessage::Body::Key, downTime, 88);
- CHECK_OFFSET(InputMessage::Body::Motion, seq, 0);
- CHECK_OFFSET(InputMessage::Body::Motion, eventId, 4);
+ CHECK_OFFSET(InputMessage::Body::Motion, eventId, 0);
+ CHECK_OFFSET(InputMessage::Body::Motion, empty1, 4);
CHECK_OFFSET(InputMessage::Body::Motion, eventTime, 8);
CHECK_OFFSET(InputMessage::Body::Motion, deviceId, 16);
CHECK_OFFSET(InputMessage::Body::Motion, source, 20);
@@ -62,29 +61,50 @@
CHECK_OFFSET(InputMessage::Body::Motion, metaState, 72);
CHECK_OFFSET(InputMessage::Body::Motion, buttonState, 76);
CHECK_OFFSET(InputMessage::Body::Motion, classification, 80);
+ CHECK_OFFSET(InputMessage::Body::Motion, empty2, 81);
CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 84);
CHECK_OFFSET(InputMessage::Body::Motion, downTime, 88);
- CHECK_OFFSET(InputMessage::Body::Motion, xScale, 96);
- CHECK_OFFSET(InputMessage::Body::Motion, yScale, 100);
- CHECK_OFFSET(InputMessage::Body::Motion, xOffset, 104);
- CHECK_OFFSET(InputMessage::Body::Motion, yOffset, 108);
- CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 112);
- CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 116);
- CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 120);
- CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 124);
- CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 128);
- CHECK_OFFSET(InputMessage::Body::Motion, pointers, 136);
+ CHECK_OFFSET(InputMessage::Body::Motion, dsdx, 96);
+ CHECK_OFFSET(InputMessage::Body::Motion, dtdx, 100);
+ CHECK_OFFSET(InputMessage::Body::Motion, dtdy, 104);
+ CHECK_OFFSET(InputMessage::Body::Motion, dsdy, 108);
+ CHECK_OFFSET(InputMessage::Body::Motion, tx, 112);
+ CHECK_OFFSET(InputMessage::Body::Motion, ty, 116);
+ CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 120);
+ CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 124);
+ CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 128);
+ CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 132);
+ CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 136);
+ CHECK_OFFSET(InputMessage::Body::Motion, empty3, 140);
+ CHECK_OFFSET(InputMessage::Body::Motion, pointers, 144);
- CHECK_OFFSET(InputMessage::Body::Focus, seq, 0);
- CHECK_OFFSET(InputMessage::Body::Focus, eventId, 4);
- CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 12);
- CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 14);
+ CHECK_OFFSET(InputMessage::Body::Focus, eventId, 0);
+ CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4);
+ CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 5);
+ CHECK_OFFSET(InputMessage::Body::Focus, empty, 6);
- CHECK_OFFSET(InputMessage::Body::Finished, seq, 0);
- CHECK_OFFSET(InputMessage::Body::Finished, handled, 4);
+ CHECK_OFFSET(InputMessage::Body::Capture, eventId, 0);
+ CHECK_OFFSET(InputMessage::Body::Capture, pointerCaptureEnabled, 4);
+ CHECK_OFFSET(InputMessage::Body::Capture, empty, 5);
+
+ CHECK_OFFSET(InputMessage::Body::Drag, eventId, 0);
+ CHECK_OFFSET(InputMessage::Body::Drag, x, 4);
+ CHECK_OFFSET(InputMessage::Body::Drag, y, 8);
+ CHECK_OFFSET(InputMessage::Body::Drag, isExiting, 12);
+ CHECK_OFFSET(InputMessage::Body::Drag, empty, 13);
+
+ CHECK_OFFSET(InputMessage::Body::Finished, handled, 0);
+ CHECK_OFFSET(InputMessage::Body::Finished, empty, 1);
+ CHECK_OFFSET(InputMessage::Body::Finished, consumeTime, 8);
+
+ CHECK_OFFSET(InputMessage::Body::Timeline, eventId, 0);
+ CHECK_OFFSET(InputMessage::Body::Timeline, empty, 4);
+ CHECK_OFFSET(InputMessage::Body::Timeline, graphicsTimeline, 8);
}
void TestHeaderSize() {
+ CHECK_OFFSET(InputMessage::Header, type, 0);
+ CHECK_OFFSET(InputMessage::Header, seq, 4);
static_assert(sizeof(InputMessage::Header) == 8);
}
@@ -97,8 +117,13 @@
static_assert(sizeof(InputMessage::Body::Motion) ==
offsetof(InputMessage::Body::Motion, pointers) +
sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS);
- static_assert(sizeof(InputMessage::Body::Finished) == 8);
- static_assert(sizeof(InputMessage::Body::Focus) == 16);
+ static_assert(sizeof(InputMessage::Body::Finished) == 16);
+ static_assert(sizeof(InputMessage::Body::Focus) == 8);
+ static_assert(sizeof(InputMessage::Body::Capture) == 8);
+ static_assert(sizeof(InputMessage::Body::Drag) == 16);
+ // Timeline
+ static_assert(GraphicsTimeline::SIZE == 2);
+ static_assert(sizeof(InputMessage::Body::Timeline) == 24);
}
// --- VerifiedInputEvent ---
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index bf452c0..d049d05 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -21,6 +21,7 @@
#include <math.h>
#include <android-base/stringprintf.h>
+#include <attestation/HmacKeyManager.h>
#include <gtest/gtest.h>
#include <input/VelocityTracker.h>
@@ -176,12 +177,12 @@
EXPECT_EQ(pointerIndex, pointerCount);
MotionEvent event;
+ ui::Transform identityTransform;
event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN,
DISPLAY_ID, INVALID_HMAC, action, 0 /*actionButton*/, 0 /*flags*/,
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
- MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0 /*xOffset*/,
- 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ MotionClassification::NONE, identityTransform, 0 /*xPrecision*/,
+ 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/,
entry.eventTime.count(), pointerCount, properties, coords);
@@ -191,8 +192,9 @@
return events;
}
-static void computeAndCheckVelocity(const char* strategy,
- const std::vector<MotionEventEntry>& motions, int32_t axis, float targetVelocity) {
+static void computeAndCheckVelocity(const VelocityTracker::Strategy strategy,
+ const std::vector<MotionEventEntry>& motions, int32_t axis,
+ float targetVelocity) {
VelocityTracker vt(strategy);
float Vx, Vy;
@@ -217,7 +219,7 @@
static void computeAndCheckQuadraticEstimate(const std::vector<MotionEventEntry>& motions,
const std::array<float, 3>& coefficients) {
- VelocityTracker vt("lsq2");
+ VelocityTracker vt(VelocityTracker::Strategy::LSQ2);
std::vector<MotionEvent> events = createMotionEventStream(motions);
for (MotionEvent event : events) {
vt.addMovement(&event);
@@ -238,36 +240,34 @@
// It is difficult to determine the correct answer here, but at least the direction
// of the reported velocity should be positive.
std::vector<MotionEventEntry> motions = {
- {0ms, {{ 273, NAN}}},
- {12585us, {{293, NAN}}},
- {14730us, {{293, NAN}}},
- {14730us, {{293, NAN}}}, // ACTION_UP
+ {0ms, {{273, 0}}},
+ {12585us, {{293, 0}}},
+ {14730us, {{293, 0}}},
+ {14730us, {{293, 0}}}, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 1600);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ 1600);
}
TEST_F(VelocityTrackerTest, ThreePointsZeroVelocityTest) {
// Same coordinate is reported 3 times in a row
std::vector<MotionEventEntry> motions = {
- { 0ms, {{293, NAN}} },
- { 6132us, {{293, NAN}} },
- { 11283us, {{293, NAN}} },
- { 11283us, {{293, NAN}} }, // ACTION_UP
+ {0ms, {{293, 0}}},
+ {6132us, {{293, 0}}},
+ {11283us, {{293, 0}}},
+ {11283us, {{293, 0}}}, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 0);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 0);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 0);
}
TEST_F(VelocityTrackerTest, ThreePointsLinearVelocityTest) {
// Fixed velocity at 5 points per 10 milliseconds
std::vector<MotionEventEntry> motions = {
- { 0ms, {{0, NAN}} },
- { 10ms, {{5, NAN}} },
- { 20ms, {{10, NAN}} },
- { 20ms, {{10, NAN}} }, // ACTION_UP
+ {0ms, {{0, 0}}}, {10ms, {{5, 0}}}, {20ms, {{10, 0}}}, {20ms, {{10, 0}}}, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 500);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 500);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 500);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 500);
}
@@ -297,8 +297,10 @@
{ 96948871ns, {{274.79245, 428.113159}} },
{ 96948871ns, {{274.79245, 428.113159}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 623.577637);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 5970.7309);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ 623.577637);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 5970.7309);
}
// --------------- Recorded by hand on sailfish, generated by a script -----------------------------
@@ -339,10 +341,14 @@
{ 235089162955851ns, {{560.66, 843.82}} },
{ 235089162955851ns, {{560.66, 843.82}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 872.794617);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 951.698181);
- computeAndCheckVelocity("impulse",motions, AMOTION_EVENT_AXIS_Y, -3604.819336);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3044.966064);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ 872.794617);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ 951.698181);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -3604.819336);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -3044.966064);
}
@@ -368,8 +374,10 @@
{ 235110660368000ns, {{530.00, 980.00}} },
{ 235110660368000ns, {{530.00, 980.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -4096.583008);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3455.094238);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -4096.583008);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -3455.094238);
}
@@ -396,10 +404,14 @@
{ 792629200000ns, {{619.00, 1115.00}} },
{ 792629200000ns, {{619.00, 1115.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 574.33429);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 617.40564);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -2361.982666);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -2500.055664);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ 574.33429);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ 617.40564);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -2361.982666);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -2500.055664);
}
@@ -426,10 +438,14 @@
{ 235160520366000ns, {{679.00, 814.00}} },
{ 235160520366000ns, {{679.00, 814.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 1274.141724);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 1438.53186);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -3001.4348);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3695.859619);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ 1274.141724);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ 1438.53186);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -3001.4348);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -3695.859619);
}
@@ -452,8 +468,10 @@
{ 847237986000ns, {{610.00, 1095.00}} },
{ 847237986000ns, {{610.00, 1095.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -4280.07959);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -4241.004395);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -4280.07959);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -4241.004395);
}
@@ -476,8 +494,10 @@
{ 235200616933000ns, {{590.00, 844.00}} },
{ 235200616933000ns, {{590.00, 844.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -8715.686523);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -7639.026367);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -8715.686523);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -7639.026367);
}
@@ -499,10 +519,14 @@
{ 920989261000ns, {{715.00, 903.00}} },
{ 920989261000ns, {{715.00, 903.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 5670.329102);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 5991.866699);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -13021.101562);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -15093.995117);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ 5670.329102);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ 5991.866699);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -13021.101562);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -15093.995117);
}
@@ -522,8 +546,10 @@
{ 235247220736000ns, {{620.00, 641.00}} },
{ 235247220736000ns, {{620.00, 641.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -20286.958984);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -20494.587891);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -20286.958984);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -20494.587891);
}
@@ -541,8 +567,10 @@
{ 235302613019881ns, {{679.26, 526.73}} },
{ 235302613019881ns, {{679.26, 526.73}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -39295.941406);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -36461.421875);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -39295.941406);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -36461.421875);
}
@@ -569,10 +597,14 @@
{ 235655842893000ns, {{563.00, 649.00}} },
{ 235655842893000ns, {{563.00, 649.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -419.749695);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -398.303894);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 3309.016357);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 3969.099854);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ -419.749695);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ -398.303894);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 3309.016357);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 3969.099854);
}
@@ -599,10 +631,14 @@
{ 235671246532000ns, {{470.00, 799.00}} },
{ 235671246532000ns, {{470.00, 799.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -262.80426);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -243.665344);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4215.682129);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4587.986816);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ -262.80426);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ -243.665344);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 4215.682129);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 4587.986816);
}
@@ -622,10 +658,14 @@
{ 171051052000ns, {{536.00, 586.00}} },
{ 171051052000ns, {{536.00, 586.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -723.413513);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -651.038452);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 2091.502441);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 1934.517456);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ -723.413513);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ -651.038452);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 2091.502441);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 1934.517456);
}
@@ -652,8 +692,10 @@
{ 235695373403000ns, {{564.00, 744.00}} },
{ 235695373403000ns, {{564.00, 744.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4254.639648);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4698.415039);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 4254.639648);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 4698.415039);
}
@@ -677,10 +719,14 @@
{ 235709710626776ns, {{511.72, 741.85}} },
{ 235709710626776ns, {{511.72, 741.85}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -430.440247);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -447.600311);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 3953.859375);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4316.155273);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ -430.440247);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ -447.600311);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 3953.859375);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 4316.155273);
}
@@ -706,8 +752,10 @@
{ 235727721580000ns, {{516.00, 658.00}} },
{ 235727721580000ns, {{516.00, 658.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4484.617676);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4927.92627);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 4484.617676);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 4927.92627);
}
@@ -725,8 +773,10 @@
{ 235762396429369ns, {{404.37, 680.67}} },
{ 235762396429369ns, {{404.37, 680.67}} }, //ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 14227.0224);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 16064.685547);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 14227.0224);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 16064.685547);
}
@@ -744,8 +794,10 @@
{ 235772537635000ns, {{484.00, 589.00}} },
{ 235772537635000ns, {{484.00, 589.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 18660.048828);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 16918.439453);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 18660.048828);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 16918.439453);
}
@@ -764,10 +816,14 @@
{ 507703352649ns, {{443.71, 857.77}} },
{ 507703352649ns, {{443.71, 857.77}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -4111.8173);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -6388.48877);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 29765.908203);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 28354.796875);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ -4111.8173);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ -6388.48877);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 29765.908203);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 28354.796875);
}
/**
@@ -789,10 +845,10 @@
// Velocity should actually be zero, but we expect 0.016 here instead.
// This is close enough to zero, and is likely caused by division by a very small number.
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -0.016);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -0.016);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 0);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 0);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, -0.016);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, -0.016);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, 0);
}
/**
diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp
index 4e8e840..36f87b8 100644
--- a/libs/input/tests/VerifiedInputEvent_test.cpp
+++ b/libs/input/tests/VerifiedInputEvent_test.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <attestation/HmacKeyManager.h>
#include <gtest/gtest.h>
#include <input/Input.h>
@@ -39,13 +40,14 @@
pointerCoords[i].clear();
}
+ ui::Transform transform;
+ transform.set({2, 0, 4, 0, 3, 5, 0, 0, 1});
event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_DEFAULT,
INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags,
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
- MotionClassification::NONE, 2 /*xScale*/, 3 /*yScale*/, 4 /*xOffset*/,
- 5 /*yOffset*/, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/, 280 /*xCursorPosition*/,
- 540 /*yCursorPosition*/, 100 /*downTime*/, 200 /*eventTime*/, pointerCount,
- pointerProperties, pointerCoords);
+ MotionClassification::NONE, transform, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/,
+ 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 100 /*downTime*/,
+ 200 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
return event;
}
diff --git a/libs/math/Android.bp b/libs/math/Android.bp
index 5595587..907eb67 100644
--- a/libs/math/Android.bp
+++ b/libs/math/Android.bp
@@ -39,9 +39,34 @@
"com.android.media.swcodec",
"com.android.neuralnetworks",
],
+
min_sdk_version: "29",
export_include_dirs: ["include"],
+ target: {
+ windows: {
+ enabled: true,
+ }
+ }
+}
+
+cc_library_headers {
+ name: "libmath_headers",
+ export_include_dirs: ["include"],
+ host_supported: true,
+ vendor_available: true,
+
+ apex_available: [
+ "//apex_available:anyapex",
+ "//apex_available:platform",
+ ],
+ min_sdk_version: "apex_inherit",
+
+ target: {
+ windows: {
+ enabled: true,
+ }
+ }
}
subdirs = ["tests"]
diff --git a/libs/math/include/math/HashCombine.h b/libs/math/include/math/HashCombine.h
new file mode 100644
index 0000000..e91b52b
--- /dev/null
+++ b/libs/math/include/math/HashCombine.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#pragma once
+
+#include <functional>
+
+namespace android {
+static inline void hashCombineSingleHashed(size_t& combinedHash, size_t hash) {
+ combinedHash = 31 * combinedHash + hash;
+}
+
+template<typename T>
+static inline void hashCombineSingle(size_t& combinedHash, const T& val) {
+ hashCombineSingleHashed(combinedHash, std::hash<T>{}(val));
+}
+
+template<typename... Types>
+static inline size_t hashCombine(const Types& ... args) {
+ size_t hash = 0;
+ ( hashCombineSingle(hash, args), ... );
+ return hash;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/math/include/math/TVecHelpers.h b/libs/math/include/math/TVecHelpers.h
index 20f852f..0dac662 100644
--- a/libs/math/include/math/TVecHelpers.h
+++ b/libs/math/include/math/TVecHelpers.h
@@ -19,9 +19,11 @@
#include <math.h>
#include <stdint.h>
+#include <math/HashCombine.h>
#include <sys/types.h>
#include <cmath>
+#include <functional>
#include <limits>
#include <iostream>
@@ -250,6 +252,17 @@
}
return r;
}
+
+ // This isn't strictly a unary operator, but it is a common place shared between both
+ // matrix and vector classes
+ size_t hash() const {
+ VECTOR<T> const& rv(static_cast<VECTOR<T> const&>(*this));
+ size_t hashed = 0;
+ for (size_t i = 0; i < rv.size(); i++) {
+ android::hashCombineSingle(hashed, rv[i]);
+ }
+ return hashed;
+ }
};
/*
@@ -606,3 +619,16 @@
// -------------------------------------------------------------------------------------
} // namespace details
} // namespace android
+
+namespace std {
+ template<template<typename T> class VECTOR, typename T>
+ struct hash<VECTOR<T>> {
+ static constexpr bool IS_VECTOR =
+ std::is_base_of<android::details::TVecUnaryOperators<VECTOR, T>, VECTOR<T>>::value;
+
+ typename std::enable_if<IS_VECTOR, size_t>::type
+ operator()(const VECTOR<T>& v) const {
+ return v.hash();
+ }
+ };
+}
diff --git a/libs/math/include/math/half.h b/libs/math/include/math/half.h
index 7682973..5ec9bf7 100644
--- a/libs/math/include/math/half.h
+++ b/libs/math/include/math/half.h
@@ -17,6 +17,7 @@
#pragma once
#include <stdint.h>
+#include <functional>
#include <iosfwd>
#include <limits>
#include <type_traits>
@@ -82,6 +83,7 @@
};
public:
+ CONSTEXPR half() noexcept { }
CONSTEXPR half(float v) noexcept : mBits(ftoh(v)) { }
CONSTEXPR operator float() const noexcept { return htof(mBits); }
@@ -201,6 +203,12 @@
inline static constexpr type signaling_NaN() noexcept { return android::half(android::half::binary, 0x7dff); }
};
+template<> struct hash<android::half> {
+ size_t operator()(const android::half& half) {
+ return std::hash<float>{}(half);
+ }
+};
+
} // namespace std
#ifdef LIKELY_DEFINED_LOCAL
diff --git a/libs/math/tests/Android.bp b/libs/math/tests/Android.bp
index 4a7c4dd..14fb72a 100644
--- a/libs/math/tests/Android.bp
+++ b/libs/math/tests/Android.bp
@@ -50,3 +50,10 @@
static_libs: ["libmath"],
cflags: ["-Wall", "-Werror"],
}
+
+cc_test {
+ name: "hashcombine_test",
+ srcs: ["hashcombine_test.cpp"],
+ static_libs: ["libmath"],
+ cflags: ["-Wall", "-Werror"],
+}
diff --git a/libs/math/tests/half_test.cpp b/libs/math/tests/half_test.cpp
index 496a7ef..a514d98 100644
--- a/libs/math/tests/half_test.cpp
+++ b/libs/math/tests/half_test.cpp
@@ -35,6 +35,7 @@
EXPECT_EQ(2UL, sizeof(half));
// test +/- zero
+ EXPECT_EQ(0x0000, half().getBits());
EXPECT_EQ(0x0000, half( 0.0f).getBits());
EXPECT_EQ(0x8000, half(-0.0f).getBits());
@@ -93,4 +94,13 @@
EXPECT_EQ(f4.xy, h2);
}
+
+TEST_F(HalfTest, Hash) {
+ float4 f4a(1,2,3,4);
+ float4 f4b(2,2,3,4);
+ half4 h4a(f4a), h4b(f4b);
+
+ EXPECT_NE(std::hash<half4>{}(h4a), std::hash<half4>{}(h4b));
+}
+
}; // namespace android
diff --git a/libs/math/tests/hashcombine_test.cpp b/libs/math/tests/hashcombine_test.cpp
new file mode 100644
index 0000000..96c6e81
--- /dev/null
+++ b/libs/math/tests/hashcombine_test.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#define LOG_TAG "HashCombineTest"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include <math/half.h>
+#include <math/vec4.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+class HashCombineTest : public testing::Test {
+protected:
+};
+
+TEST_F(HashCombineTest, Basics) {
+ char a = 40;
+ int b = 32;
+ int c = 55;
+ float d = 42.f;
+ float d_ = 42.1f;
+
+ EXPECT_NE(hashCombine(a, b, c, d), hashCombine(a, b, c, d_));
+}
+
+}; // namespace android
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 62a4d7f..0edb213 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -128,15 +128,21 @@
static Choreographer* getForThread();
virtual ~Choreographer() override EXCLUDES(gChoreographers.lock);
+ int64_t getVsyncId() const;
+ int64_t getFrameDeadline() const;
+ int64_t getFrameInterval() const;
private:
Choreographer(const Choreographer&) = delete;
- void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override;
+ void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
+ VsyncEventData vsyncEventData) override;
void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
- void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t configId,
- nsecs_t vsyncPeriod) override;
+ void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId,
+ nsecs_t vsyncPeriod) override;
void dispatchNullEvent(nsecs_t, PhysicalDisplayId) override;
+ void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId,
+ std::vector<FrameRateOverride> overrides) override;
void scheduleCallbacks();
@@ -146,6 +152,7 @@
std::vector<RefreshRateCallback> mRefreshRateCallbacks;
nsecs_t mLatestVsyncPeriod = -1;
+ VsyncEventData mLastVsyncEventData;
const sp<Looper> mLooper;
const std::thread::id mThreadId;
@@ -170,8 +177,7 @@
}
Choreographer::Choreographer(const sp<Looper>& looper)
- : DisplayEventDispatcher(looper, ISurfaceComposer::VsyncSource::eVsyncSourceApp,
- ISurfaceComposer::ConfigChanged::eConfigChangedSuppress),
+ : DisplayEventDispatcher(looper, ISurfaceComposer::VsyncSource::eVsyncSourceApp),
mLooper(looper),
mThreadId(std::this_thread::get_id()) {
std::lock_guard<std::mutex> _l(gChoreographers.lock);
@@ -350,7 +356,8 @@
// TODO(b/74619554): The PhysicalDisplayId is ignored because SF only emits VSYNC events for the
// internal display and DisplayEventReceiver::requestNextVsync only allows requesting VSYNC for
// the internal display implicitly.
-void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t) {
+void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t,
+ VsyncEventData vsyncEventData) {
std::vector<FrameCallback> callbacks{};
{
std::lock_guard<std::mutex> _l{mLock};
@@ -360,6 +367,7 @@
mFrameCallbacks.pop();
}
}
+ mLastVsyncEventData = vsyncEventData;
for (const auto& cb : callbacks) {
if (cb.callback64 != nullptr) {
cb.callback64(timestamp, cb.data);
@@ -370,21 +378,17 @@
}
void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) {
- ALOGV("choreographer %p ~ received hotplug event (displayId=%"
- ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", connected=%s), ignoring.",
- this, displayId, toString(connected));
+ ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.",
+ this, to_string(displayId).c_str(), toString(connected));
}
-// TODO(b/74619554): The PhysicalDisplayId is ignored because currently
-// Choreographer only supports dispatching VSYNC events for the internal
-// display, so as such Choreographer does not support the notion of multiple
-// displays. When multi-display choreographer is properly supported, then
-// PhysicalDisplayId should no longer be ignored.
-void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId displayId, int32_t configId,
- nsecs_t) {
- ALOGV("choreographer %p ~ received config change event "
- "(displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", configId=%d).",
- this, displayId, configId);
+void Choreographer::dispatchModeChanged(nsecs_t, PhysicalDisplayId, int32_t, nsecs_t) {
+ LOG_ALWAYS_FATAL("dispatchModeChanged was called but was never registered");
+}
+
+void Choreographer::dispatchFrameRateOverrides(nsecs_t, PhysicalDisplayId,
+ std::vector<FrameRateOverride>) {
+ LOG_ALWAYS_FATAL("dispatchFrameRateOverrides was called but was never registered");
}
void Choreographer::dispatchNullEvent(nsecs_t, PhysicalDisplayId) {
@@ -406,6 +410,18 @@
}
}
+int64_t Choreographer::getVsyncId() const {
+ return mLastVsyncEventData.id;
+}
+
+int64_t Choreographer::getFrameDeadline() const {
+ return mLastVsyncEventData.deadlineTimestamp;
+}
+
+int64_t Choreographer::getFrameInterval() const {
+ return mLastVsyncEventData.frameInterval;
+}
+
} // namespace android
using namespace android;
@@ -413,6 +429,11 @@
return reinterpret_cast<Choreographer*>(choreographer);
}
+static inline const Choreographer* AChoreographer_to_Choreographer(
+ const AChoreographer* choreographer) {
+ return reinterpret_cast<const Choreographer*>(choreographer);
+}
+
// Glue for private C api
namespace android {
void AChoreographer_signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock) {
@@ -476,15 +497,22 @@
return AChoreographer_unregisterRefreshRateCallback(choreographer, callback, data);
}
+int64_t AChoreographer_getVsyncId(const AChoreographer* choreographer) {
+ return AChoreographer_to_Choreographer(choreographer)->getVsyncId();
+}
+
+int64_t AChoreographer_getFrameDeadline(const AChoreographer* choreographer) {
+ return AChoreographer_to_Choreographer(choreographer)->getFrameDeadline();
+}
+
+int64_t AChoreographer_getFrameInterval(const AChoreographer* choreographer) {
+ return AChoreographer_to_Choreographer(choreographer)->getFrameInterval();
+}
+
} // namespace android
/* Glue for the NDK interface */
-static inline const Choreographer* AChoreographer_to_Choreographer(
- const AChoreographer* choreographer) {
- return reinterpret_cast<const Choreographer*>(choreographer);
-}
-
static inline AChoreographer* Choreographer_to_AChoreographer(Choreographer* choreographer) {
return reinterpret_cast<AChoreographer*>(choreographer);
}
diff --git a/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp
index 277635c..6288194 100644
--- a/libs/nativedisplay/ADisplay.cpp
+++ b/libs/nativedisplay/ADisplay.cpp
@@ -16,10 +16,11 @@
#include <apex/display.h>
#include <gui/SurfaceComposerClient.h>
-#include <ui/DisplayConfig.h>
-#include <ui/DisplayInfo.h>
+#include <ui/DisplayMode.h>
+#include <ui/DynamicDisplayInfo.h>
#include <ui/GraphicTypes.h>
#include <ui/PixelFormat.h>
+#include <ui/StaticDisplayInfo.h>
#include <algorithm>
#include <optional>
@@ -33,6 +34,11 @@
*/
struct DisplayConfigImpl {
/**
+ * The ID of the display configuration.
+ */
+ size_t id;
+
+ /**
* The width in pixels of the display configuration.
*/
int32_t width{0};
@@ -134,34 +140,36 @@
return NO_INIT;
}
- std::vector<DisplayConfigImpl> configsPerDisplay[size];
- int numConfigs = 0;
+ std::vector<DisplayConfigImpl> modesPerDisplay[size];
+ int numModes = 0;
for (int i = 0; i < size; ++i) {
const sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(ids[i]);
- DisplayInfo info;
- if (const status_t status = SurfaceComposerClient::getDisplayInfo(token, &info);
+ ui::StaticDisplayInfo staticInfo;
+ if (const status_t status = SurfaceComposerClient::getStaticDisplayInfo(token, &staticInfo);
status != OK) {
return status;
}
- Vector<DisplayConfig> configs;
- if (const status_t status = SurfaceComposerClient::getDisplayConfigs(token, &configs);
+ ui::DynamicDisplayInfo dynamicInfo;
+ if (const status_t status =
+ SurfaceComposerClient::getDynamicDisplayInfo(token, &dynamicInfo);
status != OK) {
return status;
}
- if (configs.empty()) {
+ const auto& modes = dynamicInfo.supportedDisplayModes;
+ if (modes.empty()) {
return NO_INIT;
}
- numConfigs += configs.size();
- configsPerDisplay[i].reserve(configs.size());
- for (int j = 0; j < configs.size(); ++j) {
- const DisplayConfig& config = configs[j];
- configsPerDisplay[i].emplace_back(
- DisplayConfigImpl{config.resolution.getWidth(), config.resolution.getHeight(),
- info.density, config.refreshRate, config.sfVsyncOffset,
- config.appVsyncOffset});
+ numModes += modes.size();
+ modesPerDisplay[i].reserve(modes.size());
+ for (int j = 0; j < modes.size(); ++j) {
+ const ui::DisplayMode& mode = modes[j];
+ modesPerDisplay[i].emplace_back(
+ DisplayConfigImpl{static_cast<size_t>(mode.id), mode.resolution.getWidth(),
+ mode.resolution.getHeight(), staticInfo.density,
+ mode.refreshRate, mode.sfVsyncOffset, mode.appVsyncOffset});
}
}
@@ -192,7 +200,7 @@
// contiguous block of DisplayConfigImpls specific to that display.
DisplayImpl** const impls = reinterpret_cast<DisplayImpl**>(
malloc((sizeof(DisplayImpl) + sizeof(DisplayImpl*)) * size +
- sizeof(DisplayConfigImpl) * numConfigs));
+ sizeof(DisplayConfigImpl) * numModes));
DisplayImpl* const displayData = reinterpret_cast<DisplayImpl*>(impls + size);
DisplayConfigImpl* configData = reinterpret_cast<DisplayConfigImpl*>(displayData + size);
@@ -200,7 +208,7 @@
const PhysicalDisplayId id = ids[i];
const ADisplayType type = (internalId == id) ? ADisplayType::DISPLAY_TYPE_INTERNAL
: ADisplayType::DISPLAY_TYPE_EXTERNAL;
- const std::vector<DisplayConfigImpl>& configs = configsPerDisplay[i];
+ const std::vector<DisplayConfigImpl>& configs = modesPerDisplay[i];
memcpy(configData, configs.data(), sizeof(DisplayConfigImpl) * configs.size());
displayData[i] = DisplayImpl{id,
@@ -257,15 +265,22 @@
CHECK_NOT_NULL(display);
sp<IBinder> token = getToken(display);
- const int index = SurfaceComposerClient::getActiveConfig(token);
- if (index < 0) {
- return index;
+ ui::DynamicDisplayInfo info;
+ if (const auto status = SurfaceComposerClient::getDynamicDisplayInfo(token, &info);
+ status != OK) {
+ return status;
}
DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
+ for (size_t i = 0; i < impl->numConfigs; i++) {
+ auto* config = impl->configs + i;
+ if (config->id == info.activeDisplayModeId) {
+ *outConfig = reinterpret_cast<ADisplayConfig*>(config);
+ return OK;
+ }
+ }
- *outConfig = reinterpret_cast<ADisplayConfig*>(impl->configs + index);
- return OK;
+ return NAME_NOT_FOUND;
}
float ADisplayConfig_getDensity(ADisplayConfig* config) {
diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp
index 6e9c999..ed728dc 100644
--- a/libs/nativedisplay/Android.bp
+++ b/libs/nativedisplay/Android.bp
@@ -74,10 +74,11 @@
"libGLESv2",
],
+ export_header_lib_headers: ["jni_headers"],
+
header_libs: [
+ "jni_headers",
"libnativedisplay_headers",
"libnativehelper_header_only",
],
-
- export_header_lib_headers: ["libnativehelper_header_only"],
}
diff --git a/libs/nativedisplay/include-private/private/android/choreographer.h b/libs/nativedisplay/include-private/private/android/choreographer.h
index c00ce4e..7d25ce8 100644
--- a/libs/nativedisplay/include-private/private/android/choreographer.h
+++ b/libs/nativedisplay/include-private/private/android/choreographer.h
@@ -29,6 +29,24 @@
// for consumption by callbacks.
void AChoreographer_signalRefreshRateCallbacks(int64_t vsyncPeriod);
+// Returns the vsync id of the last frame callback. Client are expected to call
+// this function from their frame callback function to get the vsyncId and pass
+// it together with a buffer or transaction to the Surface Composer. Calling
+// this function from anywhere else will return an undefined value.
+int64_t AChoreographer_getVsyncId(const AChoreographer* choreographer);
+
+// Returns the deadline timestamp (in CLOCK_MONOTONIC) of the last frame callback.
+// Client are expected to call this function from their frame callback function
+// to get the deadline and use it to know whether a frame is likely to miss
+// presentation. Calling this function from anywhere else will return an undefined
+// value.
+int64_t AChoreographer_getFrameDeadline(const AChoreographer* choreographer);
+
+// Returns the current interval in ns between frames.
+// Client are expected to call this function from their frame callback function.
+// Calling this function from anywhere else will return an undefined value.
+int64_t AChoreographer_getFrameInterval(const AChoreographer* choreographer);
+
// Trampoline functions allowing libandroid.so to define the NDK symbols without including
// the entirety of libnativedisplay as a whole static lib. As libnativedisplay
// maintains global state, libnativedisplay can never be directly statically
diff --git a/libs/nativedisplay/include/apex/display.h b/libs/nativedisplay/include/apex/display.h
index a7eaf87..bd94b55 100644
--- a/libs/nativedisplay/include/apex/display.h
+++ b/libs/nativedisplay/include/apex/display.h
@@ -97,6 +97,11 @@
* such an update is observed, then this method should be recalled to get the
* new current configuration.
*
+ * After a subsequent hotplug "connected" event the supported display configs
+ * may change. Then the preloaded display configs will be stale and the
+ * call for current config may return NAME_NOT_FOUND. In this case the client
+ * should release and re-acquire the display handle.
+ *
* Returns OK on success, -errno on failure.
*/
int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig);
diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt
index fc59431..9ed4915 100644
--- a/libs/nativedisplay/libnativedisplay.map.txt
+++ b/libs/nativedisplay/libnativedisplay.map.txt
@@ -29,6 +29,9 @@
android::AChoreographer_routeRegisterRefreshRateCallback*;
android::AChoreographer_routeUnregisterRefreshRateCallback*;
android::AChoreographer_signalRefreshRateCallbacks*;
+ android::AChoreographer_getVsyncId*;
+ android::AChoreographer_getFrameDeadline*;
+ android::AChoreographer_getFrameInterval*;
android::ADisplay_acquirePhysicalDisplays*;
android::ADisplay_release*;
android::ADisplay_getMaxSupportedFps*;
diff --git a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
index 16afc68..365e788 100644
--- a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
+++ b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
@@ -51,7 +51,15 @@
}
int slot = item.mSlot;
+ *outQueueEmpty = false;
if (item.mFence->isValid()) {
+ // If fence is not signaled, that means frame is not ready and
+ // outQueueEmpty is set to true. By the time the fence is signaled,
+ // there may be a new buffer queued. This is a proper detection for an
+ // empty queue and it is needed to avoid infinite loop in
+ // ASurfaceTexture_dequeueBuffer (see b/159921224).
+ *outQueueEmpty = item.mFence->getStatus() == Fence::Status::Unsignaled;
+
// Wait on the producer fence for the buffer to be ready.
err = fenceWait(item.mFence->get(), fencePassThroughHandle);
if (err != OK) {
@@ -112,7 +120,6 @@
st.mCurrentFrameNumber = item.mFrameNumber;
st.computeCurrentTransformMatrixLocked();
- *outQueueEmpty = false;
*outDataspace = item.mDataSpace;
*outSlotid = slot;
return st.mSlots[slot].mGraphicBuffer;
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index 3030068..79b47a1 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -397,6 +397,16 @@
return 0;
}
+int AHardwareBuffer_getId(const AHardwareBuffer* buffer, uint64_t* outId) {
+ if (!buffer || !outId) return BAD_VALUE;
+
+ const GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer);
+ if (!gb) return BAD_VALUE;
+
+ *outId = gb->getId();
+
+ return OK;
+}
// ----------------------------------------------------------------------------
// VNDK functions
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index fd1793b..ada689a 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -159,10 +159,8 @@
}
int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate, int8_t compatibility) {
- if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
- return -EINVAL;
- }
- return native_window_set_frame_rate(window, frameRate, compatibility);
+ return ANativeWindow_setFrameRateWithChangeStrategy(window, frameRate, compatibility,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
}
void ANativeWindow_tryAllocateBuffers(ANativeWindow* window) {
@@ -172,6 +170,13 @@
window->perform(window, NATIVE_WINDOW_ALLOCATE_BUFFERS);
}
+int32_t ANativeWindow_setFrameRateWithChangeStrategy(ANativeWindow* window, float frameRate,
+ int8_t compatibility, int8_t changeFrameRateStrategy) {
+ if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
+ return -EINVAL;
+ }
+ return native_window_set_frame_rate(window, frameRate, compatibility, changeFrameRateStrategy);
+}
/**************************************************************************************************
* vndk-stable
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
index dcb05b5..d93a84c 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -45,14 +45,14 @@
#ifndef ANDROID_HARDWARE_BUFFER_H
#define ANDROID_HARDWARE_BUFFER_H
+#include <android/rect.h>
#include <inttypes.h>
-
#include <sys/cdefs.h>
-#include <android/rect.h>
-
__BEGIN_DECLS
+// clang-format off
+
/**
* Buffer pixel formats.
*/
@@ -164,46 +164,57 @@
* Buffer usage flags, specifying how the buffer will be accessed.
*/
enum AHardwareBuffer_UsageFlags {
- /// The buffer will never be locked for direct CPU reads using the
- /// AHardwareBuffer_lock() function. Note that reading the buffer
- /// using OpenGL or Vulkan functions or memory mappings is still
- /// allowed.
+ /**
+ * The buffer will never be locked for direct CPU reads using the
+ * AHardwareBuffer_lock() function. Note that reading the buffer
+ * using OpenGL or Vulkan functions or memory mappings is still
+ * allowed.
+ */
AHARDWAREBUFFER_USAGE_CPU_READ_NEVER = 0UL,
- /// The buffer will sometimes be locked for direct CPU reads using
- /// the AHardwareBuffer_lock() function. Note that reading the
- /// buffer using OpenGL or Vulkan functions or memory mappings
- /// does not require the presence of this flag.
+ /**
+ * The buffer will sometimes be locked for direct CPU reads using
+ * the AHardwareBuffer_lock() function. Note that reading the
+ * buffer using OpenGL or Vulkan functions or memory mappings
+ * does not require the presence of this flag.
+ */
AHARDWAREBUFFER_USAGE_CPU_READ_RARELY = 2UL,
- /// The buffer will often be locked for direct CPU reads using
- /// the AHardwareBuffer_lock() function. Note that reading the
- /// buffer using OpenGL or Vulkan functions or memory mappings
- /// does not require the presence of this flag.
+ /**
+ * The buffer will often be locked for direct CPU reads using
+ * the AHardwareBuffer_lock() function. Note that reading the
+ * buffer using OpenGL or Vulkan functions or memory mappings
+ * does not require the presence of this flag.
+ */
AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN = 3UL,
- /// CPU read value mask.
+
+ /** CPU read value mask. */
AHARDWAREBUFFER_USAGE_CPU_READ_MASK = 0xFUL,
-
- /// The buffer will never be locked for direct CPU writes using the
- /// AHardwareBuffer_lock() function. Note that writing the buffer
- /// using OpenGL or Vulkan functions or memory mappings is still
- /// allowed.
+ /**
+ * The buffer will never be locked for direct CPU writes using the
+ * AHardwareBuffer_lock() function. Note that writing the buffer
+ * using OpenGL or Vulkan functions or memory mappings is still
+ * allowed.
+ */
AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER = 0UL << 4,
- /// The buffer will sometimes be locked for direct CPU writes using
- /// the AHardwareBuffer_lock() function. Note that writing the
- /// buffer using OpenGL or Vulkan functions or memory mappings
- /// does not require the presence of this flag.
+ /**
+ * The buffer will sometimes be locked for direct CPU writes using
+ * the AHardwareBuffer_lock() function. Note that writing the
+ * buffer using OpenGL or Vulkan functions or memory mappings
+ * does not require the presence of this flag.
+ */
AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY = 2UL << 4,
- /// The buffer will often be locked for direct CPU writes using
- /// the AHardwareBuffer_lock() function. Note that writing the
- /// buffer using OpenGL or Vulkan functions or memory mappings
- /// does not require the presence of this flag.
+ /**
+ * The buffer will often be locked for direct CPU writes using
+ * the AHardwareBuffer_lock() function. Note that writing the
+ * buffer using OpenGL or Vulkan functions or memory mappings
+ * does not require the presence of this flag.
+ */
AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN = 3UL << 4,
- /// CPU write value mask.
+ /** CPU write value mask. */
AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK = 0xFUL << 4,
-
- /// The buffer will be read from by the GPU as a texture.
- AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE = 1UL << 8,
- /// The buffer will be written to by the GPU as a framebuffer attachment.
- AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER = 1UL << 9,
+ /** The buffer will be read from by the GPU as a texture. */
+ AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE = 1UL << 8,
+ /** The buffer will be written to by the GPU as a framebuffer attachment.*/
+ AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER = 1UL << 9,
/**
* The buffer will be written to by the GPU as a framebuffer
* attachment.
@@ -214,7 +225,7 @@
* attachment should also have this flag. Use the equivalent flag
* AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER to avoid this confusion.
*/
- AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER,
+ AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER,
/**
* The buffer will be used as a composer HAL overlay layer.
*
@@ -225,7 +236,7 @@
* directly through AHardwareBuffer_allocate instead of buffers allocated
* by the framework.
*/
- AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY = 1ULL << 11,
+ AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY = 1ULL << 11,
/**
* The buffer is protected from direct CPU access or being read by
* non-secure hardware, such as video encoders.
@@ -236,19 +247,19 @@
* GL_EXT_protected_textures for more information on how these
* buffers are expected to behave.
*/
- AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT = 1UL << 14,
- /// The buffer will be read by a hardware video encoder.
- AHARDWAREBUFFER_USAGE_VIDEO_ENCODE = 1UL << 16,
+ AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT = 1UL << 14,
+ /** The buffer will be read by a hardware video encoder. */
+ AHARDWAREBUFFER_USAGE_VIDEO_ENCODE = 1UL << 16,
/**
* The buffer will be used for direct writes from sensors.
* When this flag is present, the format must be AHARDWAREBUFFER_FORMAT_BLOB.
*/
- AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA = 1UL << 23,
+ AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA = 1UL << 23,
/**
* The buffer will be used as a shader storage or uniform buffer object.
* When this flag is present, the format must be AHARDWAREBUFFER_FORMAT_BLOB.
*/
- AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER = 1UL << 24,
+ AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER = 1UL << 24,
/**
* The buffer will be used as a cube map texture.
* When this flag is present, the buffer must have a layer count
@@ -256,13 +267,13 @@
* bound to OpenGL textures using the extension
* GL_EXT_EGL_image_storage instead of GL_KHR_EGL_image.
*/
- AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP = 1UL << 25,
+ AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP = 1UL << 25,
/**
* The buffer contains a complete mipmap hierarchy.
* Note that buffers with this flag must be bound to OpenGL textures using
* the extension GL_EXT_EGL_image_storage instead of GL_KHR_EGL_image.
*/
- AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE = 1UL << 26,
+ AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE = 1UL << 26,
AHARDWAREBUFFER_USAGE_VENDOR_0 = 1ULL << 28,
AHARDWAREBUFFER_USAGE_VENDOR_1 = 1ULL << 29,
@@ -291,8 +302,8 @@
* parameters of existing ones.
*/
typedef struct AHardwareBuffer_Desc {
- uint32_t width; ///< Width in pixels.
- uint32_t height; ///< Height in pixels.
+ uint32_t width; ///< Width in pixels.
+ uint32_t height; ///< Height in pixels.
/**
* Number of images in an image array. AHardwareBuffers with one
* layer correspond to regular 2D textures. AHardwareBuffers with
@@ -301,21 +312,21 @@
* AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP is present, the buffer is
* a cube map or a cube map array.
*/
- uint32_t layers;
- uint32_t format; ///< One of AHardwareBuffer_Format.
- uint64_t usage; ///< Combination of AHardwareBuffer_UsageFlags.
- uint32_t stride; ///< Row stride in pixels, ignored for AHardwareBuffer_allocate()
- uint32_t rfu0; ///< Initialize to zero, reserved for future use.
- uint64_t rfu1; ///< Initialize to zero, reserved for future use.
+ uint32_t layers;
+ uint32_t format; ///< One of AHardwareBuffer_Format.
+ uint64_t usage; ///< Combination of AHardwareBuffer_UsageFlags.
+ uint32_t stride; ///< Row stride in pixels, ignored for AHardwareBuffer_allocate()
+ uint32_t rfu0; ///< Initialize to zero, reserved for future use.
+ uint64_t rfu1; ///< Initialize to zero, reserved for future use.
} AHardwareBuffer_Desc;
/**
* Holds data for a single image plane.
*/
typedef struct AHardwareBuffer_Plane {
- void* data; ///< Points to first byte in plane
- uint32_t pixelStride; ///< Distance in bytes from the color channel of one pixel to the next
- uint32_t rowStride; ///< Distance in bytes from the first value of one row of the image to
+ void* _Nullable data; ///< Points to first byte in plane
+ uint32_t pixelStride; ///< Distance in bytes from the color channel of one pixel to the next
+ uint32_t rowStride; ///< Distance in bytes from the first value of one row of the image to
/// the first value of the next row.
} AHardwareBuffer_Plane;
@@ -323,8 +334,8 @@
* Holds all image planes that contain the pixel data.
*/
typedef struct AHardwareBuffer_Planes {
- uint32_t planeCount; ///< Number of distinct planes
- AHardwareBuffer_Plane planes[4]; ///< Array of image planes
+ uint32_t planeCount; ///< Number of distinct planes
+ AHardwareBuffer_Plane planes[4]; ///< Array of image planes
} AHardwareBuffer_Planes;
/**
@@ -332,6 +343,8 @@
*/
typedef struct AHardwareBuffer AHardwareBuffer;
+// clang-format on
+
/**
* Allocates a buffer that matches the passed AHardwareBuffer_Desc.
*
@@ -345,8 +358,8 @@
* \return 0 on success, or an error number of the allocation fails for
* any reason. The returned buffer has a reference count of 1.
*/
-int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc,
- AHardwareBuffer** outBuffer) __INTRODUCED_IN(26);
+int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* _Nonnull desc,
+ AHardwareBuffer* _Nullable* _Nonnull outBuffer) __INTRODUCED_IN(26);
/**
* Acquire a reference on the given AHardwareBuffer object.
*
@@ -355,7 +368,7 @@
*
* Available since API level 26.
*/
-void AHardwareBuffer_acquire(AHardwareBuffer* buffer) __INTRODUCED_IN(26);
+void AHardwareBuffer_acquire(AHardwareBuffer* _Nonnull buffer) __INTRODUCED_IN(26);
/**
* Remove a reference that was previously acquired with
@@ -363,7 +376,7 @@
*
* Available since API level 26.
*/
-void AHardwareBuffer_release(AHardwareBuffer* buffer) __INTRODUCED_IN(26);
+void AHardwareBuffer_release(AHardwareBuffer* _Nonnull buffer) __INTRODUCED_IN(26);
/**
* Return a description of the AHardwareBuffer in the passed
@@ -371,8 +384,8 @@
*
* Available since API level 26.
*/
-void AHardwareBuffer_describe(const AHardwareBuffer* buffer,
- AHardwareBuffer_Desc* outDesc) __INTRODUCED_IN(26);
+void AHardwareBuffer_describe(const AHardwareBuffer* _Nonnull buffer,
+ AHardwareBuffer_Desc* _Nonnull outDesc) __INTRODUCED_IN(26);
/**
* Lock the AHardwareBuffer for direct CPU access.
@@ -426,8 +439,53 @@
* has more than one layer. Error number if the lock fails for any other
* reason.
*/
-int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage,
- int32_t fence, const ARect* rect, void** outVirtualAddress) __INTRODUCED_IN(26);
+int AHardwareBuffer_lock(AHardwareBuffer* _Nonnull buffer, uint64_t usage, int32_t fence,
+ const ARect* _Nullable rect, void* _Nullable* _Nonnull outVirtualAddress)
+ __INTRODUCED_IN(26);
+
+/**
+ * Unlock the AHardwareBuffer from direct CPU access.
+ *
+ * Must be called after all changes to the buffer are completed by the
+ * caller. If \a fence is NULL, the function will block until all work
+ * is completed. Otherwise, \a fence will be set either to a valid file
+ * descriptor or to -1. The file descriptor will become signaled once
+ * the unlocking is complete and buffer contents are updated.
+ * The caller is responsible for closing the file descriptor once it's
+ * no longer needed. The value -1 indicates that unlocking has already
+ * completed before the function returned and no further operations are
+ * necessary.
+ *
+ * Available since API level 26.
+ *
+ * \return 0 on success. -EINVAL if \a buffer is NULL. Error number if
+ * the unlock fails for any reason.
+ */
+int AHardwareBuffer_unlock(AHardwareBuffer* _Nonnull buffer, int32_t* _Nullable fence)
+ __INTRODUCED_IN(26);
+
+/**
+ * Send the AHardwareBuffer to an AF_UNIX socket.
+ *
+ * Available since API level 26.
+ *
+ * \return 0 on success, -EINVAL if \a buffer is NULL, or an error
+ * number if the operation fails for any reason.
+ */
+int AHardwareBuffer_sendHandleToUnixSocket(const AHardwareBuffer* _Nonnull buffer, int socketFd)
+ __INTRODUCED_IN(26);
+
+/**
+ * Receive an AHardwareBuffer from an AF_UNIX socket.
+ *
+ * Available since API level 26.
+ *
+ * \return 0 on success, -EINVAL if \a outBuffer is NULL, or an error
+ * number if the operation fails for any reason.
+ */
+int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd,
+ AHardwareBuffer* _Nullable* _Nonnull outBuffer)
+ __INTRODUCED_IN(26);
/**
* Lock a potentially multi-planar AHardwareBuffer for direct CPU access.
@@ -456,48 +514,9 @@
* has more than one layer. Error number if the lock fails for any other
* reason.
*/
-int AHardwareBuffer_lockPlanes(AHardwareBuffer* buffer, uint64_t usage,
- int32_t fence, const ARect* rect, AHardwareBuffer_Planes* outPlanes) __INTRODUCED_IN(29);
-
-/**
- * Unlock the AHardwareBuffer from direct CPU access.
- *
- * Must be called after all changes to the buffer are completed by the
- * caller. If \a fence is NULL, the function will block until all work
- * is completed. Otherwise, \a fence will be set either to a valid file
- * descriptor or to -1. The file descriptor will become signaled once
- * the unlocking is complete and buffer contents are updated.
- * The caller is responsible for closing the file descriptor once it's
- * no longer needed. The value -1 indicates that unlocking has already
- * completed before the function returned and no further operations are
- * necessary.
- *
- * Available since API level 26.
- *
- * \return 0 on success. -EINVAL if \a buffer is NULL. Error number if
- * the unlock fails for any reason.
- */
-int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence) __INTRODUCED_IN(26);
-
-/**
- * Send the AHardwareBuffer to an AF_UNIX socket.
- *
- * Available since API level 26.
- *
- * \return 0 on success, -EINVAL if \a buffer is NULL, or an error
- * number if the operation fails for any reason.
- */
-int AHardwareBuffer_sendHandleToUnixSocket(const AHardwareBuffer* buffer, int socketFd) __INTRODUCED_IN(26);
-
-/**
- * Receive an AHardwareBuffer from an AF_UNIX socket.
- *
- * Available since API level 26.
- *
- * \return 0 on success, -EINVAL if \a outBuffer is NULL, or an error
- * number if the operation fails for any reason.
- */
-int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd, AHardwareBuffer** outBuffer) __INTRODUCED_IN(26);
+int AHardwareBuffer_lockPlanes(AHardwareBuffer* _Nonnull buffer, uint64_t usage, int32_t fence,
+ const ARect* _Nullable rect,
+ AHardwareBuffer_Planes* _Nonnull outPlanes) __INTRODUCED_IN(29);
/**
* Test whether the given format and usage flag combination is
@@ -518,7 +537,7 @@
* \return 1 if the format and usage flag combination is allocatable,
* 0 otherwise.
*/
-int AHardwareBuffer_isSupported(const AHardwareBuffer_Desc* desc) __INTRODUCED_IN(29);
+int AHardwareBuffer_isSupported(const AHardwareBuffer_Desc* _Nonnull desc) __INTRODUCED_IN(29);
/**
* Lock an AHardwareBuffer for direct CPU access.
@@ -531,9 +550,22 @@
*
* Available since API level 29.
*/
-int AHardwareBuffer_lockAndGetInfo(AHardwareBuffer* buffer, uint64_t usage,
- int32_t fence, const ARect* rect, void** outVirtualAddress,
- int32_t* outBytesPerPixel, int32_t* outBytesPerStride) __INTRODUCED_IN(29);
+int AHardwareBuffer_lockAndGetInfo(AHardwareBuffer* _Nonnull buffer, uint64_t usage, int32_t fence,
+ const ARect* _Nullable rect,
+ void* _Nullable* _Nonnull outVirtualAddress,
+ int32_t* _Nonnull outBytesPerPixel,
+ int32_t* _Nonnull outBytesPerStride) __INTRODUCED_IN(29);
+
+/**
+ * Get the system wide unique id for an AHardwareBuffer.
+ *
+ * Available since API level 31.
+ *
+ * \return 0 on success, -EINVAL if \a buffer or \a outId is NULL, or an error number if the
+ * operation fails for any reason.
+ */
+int AHardwareBuffer_getId(const AHardwareBuffer* _Nonnull buffer, uint64_t* _Nonnull outId)
+ __INTRODUCED_IN(31);
__END_DECLS
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index a3a45e3..3865ba5 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -34,6 +34,7 @@
#define ANDROID_NATIVE_WINDOW_H
#include <stdint.h>
+#include <stdbool.h>
#include <sys/cdefs.h>
#include <android/data_space.h>
@@ -156,6 +157,7 @@
* For all of these parameters, if 0 is supplied then the window's base
* value will come back in force.
*
+ * \param window pointer to an ANativeWindow object.
* \param width width of the buffers in pixels.
* \param height height of the buffers in pixels.
* \param format one of the AHardwareBuffer_Format constants.
@@ -190,6 +192,7 @@
*
* Available since API level 26.
*
+ * \param window pointer to an ANativeWindow object.
* \param transform combination of {@link ANativeWindowTransform} flags
* \return 0 for success, or -EINVAL if \p transform is invalid
*/
@@ -207,6 +210,7 @@
*
* Available since API level 28.
*
+ * \param window pointer to an ANativeWindow object.
* \param dataSpace data space of all buffers queued after this call.
* \return 0 for success, -EINVAL if window is invalid or the dataspace is not
* supported.
@@ -246,6 +250,41 @@
};
/**
+ * Same as ANativeWindow_setFrameRateWithChangeStrategy(window, frameRate, compatibility,
+ * ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS).
+ *
+ * See ANativeWindow_setFrameRateWithChangeStrategy().
+ *
+ * Available since API level 30.
+ */
+int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate, int8_t compatibility)
+ __INTRODUCED_IN(30);
+
+/**
+ * Provides a hint to the window that buffers should be preallocated ahead of
+ * time. Note that the window implementation is not guaranteed to preallocate
+ * any buffers, for instance if an implementation disallows allocation of new
+ * buffers, or if there is insufficient memory in the system to preallocate
+ * additional buffers
+ *
+ * Available since API level 30.
+ */
+void ANativeWindow_tryAllocateBuffers(ANativeWindow* window) __INTRODUCED_IN(30);
+
+/** Change frame rate strategy value for ANativeWindow_setFrameRate. */
+enum ANativeWindow_ChangeFrameRateStrategy {
+ /**
+ * Change the frame rate only if the transition is going to be seamless.
+ */
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS = 0,
+ /**
+ * Change the frame rate even if the transition is going to be non-seamless,
+ * i.e. with visual interruptions for the user.
+ */
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS = 1
+} __INTRODUCED_IN(31);
+
+/**
* Sets the intended frame rate for this window.
*
* On devices that are capable of running the display at different refresh
@@ -261,7 +300,9 @@
* this ANativeWindow is consumed by something other than the system compositor,
* e.g. a media codec, this call has no effect.
*
- * Available since API level 30.
+ * Available since API level 31.
+ *
+ * \param window pointer to an ANativeWindow object.
*
* \param frameRate The intended frame rate of this window, in frames per
* second. 0 is a special value that indicates the app will accept the system's
@@ -273,23 +314,20 @@
* \param compatibility The frame rate compatibility of this window. The
* compatibility value may influence the system's choice of display refresh
* rate. See the ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* values for more info.
+ * This parameter is ignored when frameRate is 0.
+ *
+ * \param changeFrameRateStrategy Whether display refresh rate transitions caused by this
+ * window should be seamless.
+ * A seamless transition is one that doesn't have any visual interruptions, such as a black
+ * screen for a second or two. See the ANATIVEWINDOW_CHANGE_FRAME_RATE_* values.
+ * This parameter is ignored when frameRate is 0.
*
* \return 0 for success, -EINVAL if the window, frame rate, or compatibility
* value are invalid.
*/
-int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate, int8_t compatibility)
- __INTRODUCED_IN(30);
-
-/**
- * Provides a hint to the window that buffers should be preallocated ahead of
- * time. Note that the window implementation is not guaranteed to preallocate
- * any buffers, for instance if an implementation disallows allocation of new
- * buffers, or if there is insufficient memory in the system to preallocate
- * additional buffers
- *
- * Available since API level 30.
- */
-void ANativeWindow_tryAllocateBuffers(ANativeWindow* window);
+int32_t ANativeWindow_setFrameRateWithChangeStrategy(ANativeWindow* window, float frameRate,
+ int8_t compatibility, int8_t changeFrameRateStrategy)
+ __INTRODUCED_IN(31);
#ifdef __cplusplus
};
diff --git a/libs/nativewindow/include/apex/window.h b/libs/nativewindow/include/apex/window.h
index 2d1354c..0923438 100644
--- a/libs/nativewindow/include/apex/window.h
+++ b/libs/nativewindow/include/apex/window.h
@@ -39,6 +39,19 @@
// clang-format on
};
+/*
+ * Internal extension of compatibility value for ANativeWindow_setFrameRate. */
+enum ANativeWindow_FrameRateCompatibilityInternal {
+ /**
+ * This surface belongs to an app on the High Refresh Rate Deny list, and needs the display
+ * to operate at the exact frame rate.
+ *
+ * This is used internally by the platform and should not be used by apps.
+ * @hide
+ */
+ ANATIVEWINDOW_FRAME_RATE_EXACT = 100,
+};
+
/**
* Prototype of the function that an ANativeWindow implementation would call
* when ANativeWindow_cancelBuffer is called.
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index b78fc5d..cc82bb4 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -255,6 +255,8 @@
NATIVE_WINDOW_ALLOCATE_BUFFERS = 45, /* private */
NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER = 46, /* private */
NATIVE_WINDOW_SET_QUERY_INTERCEPTOR = 47, /* private */
+ NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO = 48, /* private */
+ NATIVE_WINDOW_GET_EXTRA_BUFFER_COUNT = 49, /* private */
// clang-format on
};
@@ -1017,9 +1019,21 @@
}
static inline int native_window_set_frame_rate(struct ANativeWindow* window, float frameRate,
- int8_t compatibility) {
+ int8_t compatibility, int8_t changeFrameRateStrategy) {
return window->perform(window, NATIVE_WINDOW_SET_FRAME_RATE, (double)frameRate,
- (int)compatibility);
+ (int)compatibility, (int)changeFrameRateStrategy);
+}
+
+static inline int native_window_set_frame_timeline_info(struct ANativeWindow* window,
+ int64_t frameTimelineVsyncId,
+ int32_t inputEventId) {
+ return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO,
+ frameTimelineVsyncId, inputEventId);
+}
+
+static inline int native_window_get_extra_buffer_count(
+ struct ANativeWindow* window, int* extraBuffers) {
+ return window->perform(window, NATIVE_WINDOW_GET_EXTRA_BUFFER_COUNT, extraBuffers);
}
// ------------------------------------------------------------------------------------------------
diff --git a/libs/nativewindow/include/vndk/hardware_buffer.h b/libs/nativewindow/include/vndk/hardware_buffer.h
index 3392d7f..50fe0b7 100644
--- a/libs/nativewindow/include/vndk/hardware_buffer.h
+++ b/libs/nativewindow/include/vndk/hardware_buffer.h
@@ -24,7 +24,14 @@
__BEGIN_DECLS
-const native_handle_t* AHardwareBuffer_getNativeHandle(const AHardwareBuffer* buffer);
+/**
+ * Get the native handle from an AHardwareBuffer.
+ *
+ * \return a non-NULL native handle on success, NULL if \a buffer is nullptr or the operation fails
+ * for any reason.
+ */
+const native_handle_t* _Nullable AHardwareBuffer_getNativeHandle(
+ const AHardwareBuffer* _Nonnull buffer);
enum CreateFromHandleMethod {
// enum values chosen to match internal GraphicBuffer::HandleWrapMethod
@@ -33,9 +40,9 @@
};
/**
- * Create a AHardwareBuffer from a native handle.
+ * Create an AHardwareBuffer from a native handle.
*
- * This function wraps a native handle in a AHardwareBuffer suitable for use by applications or
+ * This function wraps a native handle in an AHardwareBuffer suitable for use by applications or
* other parts of the system. The contents of desc will be returned by AHardwareBuffer_describe().
*
* If method is AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_REGISTER, the handle is assumed to be
@@ -44,10 +51,13 @@
*
* If method is AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE, the handle will be cloned and the
* clone registered. The AHardwareBuffer will own the cloned handle but not the original.
+ *
+ * \return 0 on success, -EINVAL if \a desc or \a handle or outBuffer is NULL, or an error number if
+ * the operation fails for any reason.
*/
-int AHardwareBuffer_createFromHandle(const AHardwareBuffer_Desc* desc,
- const native_handle_t* handle, int32_t method,
- AHardwareBuffer** outBuffer);
+int AHardwareBuffer_createFromHandle(const AHardwareBuffer_Desc* _Nonnull desc,
+ const native_handle_t* _Nonnull handle, int32_t method,
+ AHardwareBuffer* _Nullable* _Nonnull outBuffer);
/**
* Buffer pixel formats.
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index 1b5d20d..988132c 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -4,6 +4,7 @@
AHardwareBuffer_allocate;
AHardwareBuffer_createFromHandle; # llndk # apex
AHardwareBuffer_describe;
+ AHardwareBuffer_getId; # introduced=31
AHardwareBuffer_getNativeHandle; # llndk # apex
AHardwareBuffer_isSupported; # introduced=29
AHardwareBuffer_lock;
@@ -46,6 +47,7 @@
ANativeWindow_setBuffersTransform;
ANativeWindow_setDequeueTimeout; # apex # introduced=30
ANativeWindow_setFrameRate; # introduced=30
+ ANativeWindow_setFrameRateWithChangeStrategy; # introduced=31
ANativeWindow_setSharedBufferMode; # llndk
ANativeWindow_setSwapInterval; # llndk
ANativeWindow_setUsage; # llndk
diff --git a/libs/nativewindow/tests/AHardwareBufferTest.cpp b/libs/nativewindow/tests/AHardwareBufferTest.cpp
index 71b1f9f..ef863b6 100644
--- a/libs/nativewindow/tests/AHardwareBufferTest.cpp
+++ b/libs/nativewindow/tests/AHardwareBufferTest.cpp
@@ -17,12 +17,11 @@
#define LOG_TAG "AHardwareBuffer_test"
//#define LOG_NDEBUG 0
-#include <android/hardware_buffer.h>
-#include <private/android/AHardwareBufferHelpers.h>
#include <android/hardware/graphics/common/1.0/types.h>
-#include <vndk/hardware_buffer.h>
-
#include <gtest/gtest.h>
+#include <private/android/AHardwareBufferHelpers.h>
+#include <ui/GraphicBuffer.h>
+#include <vndk/hardware_buffer.h>
using namespace android;
using android::hardware::graphics::common::V1_0::BufferUsage;
@@ -131,3 +130,43 @@
AHardwareBuffer_release(buffer);
AHardwareBuffer_release(otherBuffer);
}
+
+TEST(AHardwareBufferTest, GetIdTest) {
+ const uint32_t testWidth = 4;
+ const uint32_t testHeight = 4;
+ const uint32_t testLayers = 1;
+
+ AHardwareBuffer* ahb1 = nullptr;
+ uint64_t id1 = 0;
+ const AHardwareBuffer_Desc desc = {
+ .width = testWidth,
+ .height = testHeight,
+ .layers = testLayers,
+ .format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ .usage = AHARDWAREBUFFER_USAGE_CPU_READ_RARELY,
+ };
+ int res = AHardwareBuffer_allocate(&desc, &ahb1);
+ EXPECT_EQ(NO_ERROR, res);
+ EXPECT_NE(nullptr, ahb1);
+ EXPECT_EQ(0, AHardwareBuffer_getId(ahb1, &id1));
+ const GraphicBuffer* gb1 = AHardwareBuffer_to_GraphicBuffer(ahb1);
+ EXPECT_NE(nullptr, gb1);
+ EXPECT_EQ(id1, gb1->getId());
+ EXPECT_NE(id1, 0);
+
+ sp<GraphicBuffer> gb2(new GraphicBuffer(testWidth,
+ testHeight,
+ PIXEL_FORMAT_RGBA_8888,
+ testLayers,
+ GraphicBuffer::USAGE_SW_READ_RARELY,
+ std::string("test")));
+ EXPECT_NE(nullptr, gb2.get());
+ const AHardwareBuffer* ahb2 = AHardwareBuffer_from_GraphicBuffer(gb2.get());
+ EXPECT_NE(nullptr, ahb2);
+ uint64_t id2 = 0;
+ EXPECT_EQ(0, AHardwareBuffer_getId(ahb2, &id2));
+ EXPECT_EQ(id2, gb2->getId());
+ EXPECT_NE(id2, 0);
+
+ EXPECT_NE(id1, id2);
+}
diff --git a/libs/nativewindow/tests/Android.bp b/libs/nativewindow/tests/Android.bp
index 2744458..30737c1 100644
--- a/libs/nativewindow/tests/Android.bp
+++ b/libs/nativewindow/tests/Android.bp
@@ -35,6 +35,7 @@
"liblog",
"libnativewindow",
"libsync",
+ "libui",
"libutils",
"android.hardware.graphics.common@1.0",
],
diff --git a/libs/permission/AppOpsManager.cpp b/libs/permission/AppOpsManager.cpp
index f3ea1a7..baa9d75 100644
--- a/libs/permission/AppOpsManager.cpp
+++ b/libs/permission/AppOpsManager.cpp
@@ -22,6 +22,7 @@
#include <utils/SystemClock.h>
#include <sys/types.h>
+#include <private/android_filesystem_config.h>
#ifdef LOG_TAG
#undef LOG_TAG
@@ -100,7 +101,7 @@
sp<IAppOpsService> service = getService();
int32_t mode = service != nullptr
? service->noteOperation(op, uid, callingPackage, attributionTag,
- shouldCollectNotes(op), message)
+ shouldCollectNotes(op), message, uid == AID_SYSTEM)
: AppOpsManager::MODE_IGNORED;
return mode;
@@ -118,7 +119,8 @@
sp<IAppOpsService> service = getService();
int32_t mode = service != nullptr
? service->startOperation(getClientId(), op, uid, callingPackage,
- attributionTag, startIfModeDefault, shouldCollectNotes(op), message)
+ attributionTag, startIfModeDefault, shouldCollectNotes(op), message,
+ uid == AID_SYSTEM)
: AppOpsManager::MODE_IGNORED;
return mode;
diff --git a/libs/permission/IAppOpsService.cpp b/libs/permission/IAppOpsService.cpp
index 1af5ab8..d59f445 100644
--- a/libs/permission/IAppOpsService.cpp
+++ b/libs/permission/IAppOpsService.cpp
@@ -18,8 +18,8 @@
#include <binder/IAppOpsService.h>
-#include <utils/Log.h>
#include <binder/Parcel.h>
+#include <utils/Log.h>
#include <utils/String8.h>
#include <optional>
@@ -50,24 +50,29 @@
virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
const std::optional<String16>& attributionTag, bool shouldCollectAsyncNotedOp,
- const String16& message) {
+ const String16& message, bool shouldCollectMessage) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
data.writeInt32(code);
data.writeInt32(uid);
data.writeString16(packageName);
data.writeString16(attributionTag);
- data.writeInt32(shouldCollectAsyncNotedOp ? 1 : 0);
+ data.writeBool(shouldCollectAsyncNotedOp);
data.writeString16(message);
+ data.writeBool(shouldCollectMessage);
remote()->transact(NOTE_OPERATION_TRANSACTION, data, &reply);
// fail on exception
if (reply.readExceptionCode() != 0) return MODE_ERRORED;
+ // TODO b/184855056: extract to class
+ reply.readInt32();
+ reply.readByte();
return reply.readInt32();
}
virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
const String16& packageName, const std::optional<String16>& attributionTag,
- bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) {
+ bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message,
+ bool shouldCollectMessage) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
data.writeStrongBinder(token);
@@ -75,12 +80,16 @@
data.writeInt32(uid);
data.writeString16(packageName);
data.writeString16(attributionTag);
- data.writeInt32(startIfModeDefault ? 1 : 0);
- data.writeInt32(shouldCollectAsyncNotedOp ? 1 : 0);
+ data.writeBool(startIfModeDefault);
+ data.writeBool(shouldCollectAsyncNotedOp);
data.writeString16(message);
+ data.writeBool(shouldCollectMessage);
remote()->transact(START_OPERATION_TRANSACTION, data, &reply);
// fail on exception
if (reply.readExceptionCode() != 0) return MODE_ERRORED;
+ // TODO b/184855056: extract to class
+ reply.readInt32();
+ reply.readByte();
return reply.readInt32();
}
@@ -186,10 +195,11 @@
String16 packageName = data.readString16();
std::optional<String16> attributionTag;
data.readString16(&attributionTag);
- bool shouldCollectAsyncNotedOp = data.readInt32() == 1;
+ bool shouldCollectAsyncNotedOp = data.readBool();
String16 message = data.readString16();
+ bool shouldCollectMessage = data.readBool();
int32_t res = noteOperation(code, uid, packageName, attributionTag,
- shouldCollectAsyncNotedOp, message);
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage);
reply->writeNoException();
reply->writeInt32(res);
return NO_ERROR;
@@ -202,11 +212,12 @@
String16 packageName = data.readString16();
std::optional<String16> attributionTag;
data.readString16(&attributionTag);
- bool startIfModeDefault = data.readInt32() == 1;
- bool shouldCollectAsyncNotedOp = data.readInt32() == 1;
+ bool startIfModeDefault = data.readBool();
+ bool shouldCollectAsyncNotedOp = data.readBool();
String16 message = data.readString16();
+ bool shouldCollectMessage = data.readBool();
int32_t res = startOperation(token, code, uid, packageName, attributionTag,
- startIfModeDefault, shouldCollectAsyncNotedOp, message);
+ startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage);
reply->writeNoException();
reply->writeInt32(res);
return NO_ERROR;
diff --git a/libs/permission/include/binder/AppOpsManager.h b/libs/permission/include/binder/AppOpsManager.h
index 35c697e..c048cbe 100644
--- a/libs/permission/include/binder/AppOpsManager.h
+++ b/libs/permission/include/binder/AppOpsManager.h
@@ -136,7 +136,17 @@
OP_PHONE_CALL_MICROPHONE = 100,
OP_PHONE_CALL_CAMERA = 101,
OP_RECORD_AUDIO_HOTWORD = 102,
- _NUM_OP = 103
+ // Ops 103-105 are currently unused in native, and intentionally omitted
+ OP_RECORD_AUDIO_OUTPUT = 106,
+ OP_SCHEDULE_EXACT_ALARM = 107,
+ OP_FINE_LOCATION_SOURCE = 108,
+ OP_COARSE_LOCATION_SOURCE = 109,
+ OP_MANAGE_MEDIA = 110,
+ OP_BLUETOOTH_CONNECT = 111,
+ OP_UWB_RANGING = 112,
+ OP_ACTIVITY_RECOGNITION_SOURCE = 113,
+ OP_BLUETOOTH_ADVERTISE = 114,
+ _NUM_OP = 115
};
AppOpsManager();
diff --git a/libs/permission/include/binder/IAppOpsService.h b/libs/permission/include/binder/IAppOpsService.h
index b0719d4..22f056b 100644
--- a/libs/permission/include/binder/IAppOpsService.h
+++ b/libs/permission/include/binder/IAppOpsService.h
@@ -37,10 +37,11 @@
virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
const std::optional<String16>& attributionTag, bool shouldCollectAsyncNotedOp,
- const String16& message) = 0;
+ const String16& message, bool shouldCollectMessage) = 0;
virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
const String16& packageName, const std::optional<String16>& attributionTag,
- bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) = 0;
+ bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message,
+ bool shouldCollectMessage) = 0;
virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
const String16& packageName, const std::optional<String16>& attributionTag) = 0;
virtual void startWatchingMode(int32_t op, const String16& packageName,
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index aae1e31..f395ab4 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -48,6 +48,7 @@
name: "librenderengine_sources",
srcs: [
"Description.cpp",
+ "ExternalTexture.cpp",
"Mesh.cpp",
"RenderEngine.cpp",
"Texture.cpp",
@@ -73,23 +74,50 @@
],
}
+filegroup {
+ name: "librenderengine_threaded_sources",
+ srcs: [
+ "threaded/RenderEngineThreaded.cpp",
+ ],
+}
+
+filegroup {
+ name: "librenderengine_skia_sources",
+ srcs: [
+ "skia/AutoBackendTexture.cpp",
+ "skia/Cache.cpp",
+ "skia/ColorSpaces.cpp",
+ "skia/SkiaRenderEngine.cpp",
+ "skia/SkiaGLRenderEngine.cpp",
+ "skia/debug/CaptureTimer.cpp",
+ "skia/debug/CommonPool.cpp",
+ "skia/debug/SkiaCapture.cpp",
+ "skia/debug/SkiaMemoryReporter.cpp",
+ "skia/filters/BlurFilter.cpp",
+ "skia/filters/LinearEffect.cpp",
+ ],
+}
+
cc_library_static {
name: "librenderengine",
defaults: ["librenderengine_defaults"],
- vendor_available: true,
- vndk: {
- enabled: true,
- },
double_loadable: true,
clang: true,
cflags: [
"-fvisibility=hidden",
"-Werror=format",
+ "-Wno-unused-parameter",
],
srcs: [
":librenderengine_sources",
":librenderengine_gl_sources",
+ ":librenderengine_threaded_sources",
+ ":librenderengine_skia_sources",
],
+ include_dirs: [
+ "external/skia/src/gpu",
+ ],
+ whole_static_libs: ["libskia"],
lto: {
thin: true,
},
diff --git a/libs/renderengine/ExternalTexture.cpp b/libs/renderengine/ExternalTexture.cpp
new file mode 100644
index 0000000..eabff58
--- /dev/null
+++ b/libs/renderengine/ExternalTexture.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2021 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 <renderengine/ExternalTexture.h>
+#include <renderengine/RenderEngine.h>
+#include <ui/GraphicBuffer.h>
+
+#include "log/log_main.h"
+
+namespace android::renderengine {
+
+ExternalTexture::ExternalTexture(const sp<GraphicBuffer>& buffer, RenderEngine& renderEngine,
+ uint32_t usage)
+ : mBuffer(buffer), mRenderEngine(renderEngine) {
+ LOG_ALWAYS_FATAL_IF(buffer == nullptr,
+ "Attempted to bind a null buffer to an external texture!");
+ // GLESRenderEngine has a separate texture cache for output buffers,
+ if (usage == Usage::WRITEABLE &&
+ (mRenderEngine.getRenderEngineType() == RenderEngine::RenderEngineType::GLES ||
+ mRenderEngine.getRenderEngineType() == RenderEngine::RenderEngineType::THREADED)) {
+ return;
+ }
+ mRenderEngine.mapExternalTextureBuffer(mBuffer, usage & Usage::WRITEABLE);
+}
+
+ExternalTexture::~ExternalTexture() {
+ mRenderEngine.unmapExternalTextureBuffer(mBuffer);
+}
+
+} // namespace android::renderengine
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index 0fdf093..0c5a851 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -18,39 +18,82 @@
#include <cutils/properties.h>
#include <log/log.h>
-#include <private/gui/SyncFeatures.h>
#include "gl/GLESRenderEngine.h"
+#include "threaded/RenderEngineThreaded.h"
+
+#include "skia/SkiaGLRenderEngine.h"
namespace android {
namespace renderengine {
-std::unique_ptr<impl::RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) {
+std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) {
+ RenderEngineType renderEngineType = args.renderEngineType;
+
+ // Keep the ability to override by PROPERTIES:
char prop[PROPERTY_VALUE_MAX];
- property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "gles");
+ property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "");
if (strcmp(prop, "gles") == 0) {
- ALOGD("RenderEngine GLES Backend");
- return renderengine::gl::GLESRenderEngine::create(args);
+ renderEngineType = RenderEngineType::GLES;
}
- ALOGE("UNKNOWN BackendType: %s, create GLES RenderEngine.", prop);
- return renderengine::gl::GLESRenderEngine::create(args);
+ if (strcmp(prop, "threaded") == 0) {
+ renderEngineType = RenderEngineType::THREADED;
+ }
+ if (strcmp(prop, "skiagl") == 0) {
+ renderEngineType = RenderEngineType::SKIA_GL;
+ }
+ if (strcmp(prop, "skiaglthreaded") == 0) {
+ renderEngineType = RenderEngineType::SKIA_GL_THREADED;
+ }
+
+ switch (renderEngineType) {
+ case RenderEngineType::THREADED:
+ ALOGD("Threaded RenderEngine with GLES Backend");
+ return renderengine::threaded::RenderEngineThreaded::create(
+ [args]() { return android::renderengine::gl::GLESRenderEngine::create(args); },
+ renderEngineType);
+ case RenderEngineType::SKIA_GL:
+ ALOGD("RenderEngine with SkiaGL Backend");
+ return renderengine::skia::SkiaGLRenderEngine::create(args);
+ case RenderEngineType::SKIA_GL_THREADED: {
+ // These need to be recreated, since they are a constant reference, and we need to
+ // let SkiaRE know that it's running as threaded, and all GL operation will happen on
+ // the same thread.
+ RenderEngineCreationArgs skiaArgs =
+ RenderEngineCreationArgs::Builder()
+ .setPixelFormat(args.pixelFormat)
+ .setImageCacheSize(args.imageCacheSize)
+ .setUseColorManagerment(args.useColorManagement)
+ .setEnableProtectedContext(args.enableProtectedContext)
+ .setPrecacheToneMapperShaderOnly(args.precacheToneMapperShaderOnly)
+ .setSupportsBackgroundBlur(args.supportsBackgroundBlur)
+ .setContextPriority(args.contextPriority)
+ .setRenderEngineType(renderEngineType)
+ .build();
+ ALOGD("Threaded RenderEngine with SkiaGL Backend");
+ return renderengine::threaded::RenderEngineThreaded::create(
+ [skiaArgs]() {
+ return android::renderengine::skia::SkiaGLRenderEngine::create(skiaArgs);
+ },
+ renderEngineType);
+ }
+ case RenderEngineType::GLES:
+ default:
+ ALOGD("RenderEngine with GLES Backend");
+ return renderengine::gl::GLESRenderEngine::create(args);
+ }
}
RenderEngine::~RenderEngine() = default;
-namespace impl {
-
-RenderEngine::RenderEngine(const RenderEngineCreationArgs& args) : mArgs(args) {}
-
-RenderEngine::~RenderEngine() = default;
-
-bool RenderEngine::useNativeFenceSync() const {
- return SyncFeatures::getInstance().useNativeFenceSync();
+void RenderEngine::validateInputBufferUsage(const sp<GraphicBuffer>& buffer) {
+ LOG_ALWAYS_FATAL_IF(!(buffer->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE),
+ "input buffer not gpu readable");
}
-bool RenderEngine::useWaitSync() const {
- return SyncFeatures::getInstance().useWaitSync();
+void RenderEngine::validateOutputBufferUsage(const sp<GraphicBuffer>& buffer) {
+ LOG_ALWAYS_FATAL_IF(!(buffer->getUsage() & GraphicBuffer::USAGE_HW_RENDER),
+ "output buffer not gpu writeable");
}
-} // namespace impl
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 56d470e..d87315f 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -15,6 +15,7 @@
*/
//#define LOG_NDEBUG 0
+#include "EGL/egl.h"
#undef LOG_TAG
#define LOG_TAG "RenderEngine"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -51,8 +52,6 @@
#include "ProgramCache.h"
#include "filters/BlurFilter.h"
-extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
-
bool checkGlError(const char* op, int lineNumber) {
bool errorFound = false;
GLint error = glGetError();
@@ -116,6 +115,28 @@
namespace renderengine {
namespace gl {
+class BindNativeBufferAsFramebuffer {
+public:
+ BindNativeBufferAsFramebuffer(GLESRenderEngine& engine, ANativeWindowBuffer* buffer,
+ const bool useFramebufferCache)
+ : mEngine(engine), mFramebuffer(mEngine.getFramebufferForDrawing()), mStatus(NO_ERROR) {
+ mStatus = mFramebuffer->setNativeWindowBuffer(buffer, mEngine.isProtected(),
+ useFramebufferCache)
+ ? mEngine.bindFrameBuffer(mFramebuffer)
+ : NO_MEMORY;
+ }
+ ~BindNativeBufferAsFramebuffer() {
+ mFramebuffer->setNativeWindowBuffer(nullptr, false, /*arbitrary*/ true);
+ mEngine.unbindFrameBuffer(mFramebuffer);
+ }
+ status_t getStatus() const { return mStatus; }
+
+private:
+ GLESRenderEngine& mEngine;
+ Framebuffer* mFramebuffer;
+ status_t mStatus;
+};
+
using base::StringAppendF;
using ui::Dataspace;
@@ -201,6 +222,29 @@
return err;
}
+std::optional<RenderEngine::ContextPriority> GLESRenderEngine::createContextPriority(
+ const RenderEngineCreationArgs& args) {
+ if (!GLExtensions::getInstance().hasContextPriority()) {
+ return std::nullopt;
+ }
+
+ switch (args.contextPriority) {
+ case RenderEngine::ContextPriority::REALTIME:
+ if (gl::GLExtensions::getInstance().hasRealtimePriority()) {
+ return RenderEngine::ContextPriority::REALTIME;
+ } else {
+ ALOGI("Realtime priority unsupported, degrading gracefully to high priority");
+ return RenderEngine::ContextPriority::HIGH;
+ }
+ case RenderEngine::ContextPriority::HIGH:
+ case RenderEngine::ContextPriority::MEDIUM:
+ case RenderEngine::ContextPriority::LOW:
+ return args.contextPriority;
+ default:
+ return std::nullopt;
+ }
+}
+
std::unique_ptr<GLESRenderEngine> GLESRenderEngine::create(const RenderEngineCreationArgs& args) {
// initialize EGL for the default display
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
@@ -208,16 +252,17 @@
LOG_ALWAYS_FATAL("failed to initialize EGL");
}
- const auto eglVersion = eglQueryStringImplementationANDROID(display, EGL_VERSION);
+ const auto eglVersion = eglQueryString(display, EGL_VERSION);
if (!eglVersion) {
checkGlError(__FUNCTION__, __LINE__);
- LOG_ALWAYS_FATAL("eglQueryStringImplementationANDROID(EGL_VERSION) failed");
+ LOG_ALWAYS_FATAL("eglQueryString(EGL_VERSION) failed");
}
- const auto eglExtensions = eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS);
+ // Use the Android impl to grab EGL_NV_context_priority_realtime
+ const auto eglExtensions = eglQueryString(display, EGL_EXTENSIONS);
if (!eglExtensions) {
checkGlError(__FUNCTION__, __LINE__);
- LOG_ALWAYS_FATAL("eglQueryStringImplementationANDROID(EGL_EXTENSIONS) failed");
+ LOG_ALWAYS_FATAL("eglQueryString(EGL_EXTENSIONS) failed");
}
GLExtensions& extensions = GLExtensions::getInstance();
@@ -230,17 +275,16 @@
config = chooseEglConfig(display, args.pixelFormat, /*logConfig*/ true);
}
- bool useContextPriority =
- extensions.hasContextPriority() && args.contextPriority == ContextPriority::HIGH;
+ const std::optional<RenderEngine::ContextPriority> priority = createContextPriority(args);
EGLContext protectedContext = EGL_NO_CONTEXT;
if (args.enableProtectedContext && extensions.hasProtectedContent()) {
- protectedContext = createEglContext(display, config, nullptr, useContextPriority,
- Protection::PROTECTED);
+ protectedContext =
+ createEglContext(display, config, nullptr, priority, Protection::PROTECTED);
ALOGE_IF(protectedContext == EGL_NO_CONTEXT, "Can't create protected context");
}
- EGLContext ctxt = createEglContext(display, config, protectedContext, useContextPriority,
- Protection::UNPROTECTED);
+ EGLContext ctxt =
+ createEglContext(display, config, protectedContext, priority, Protection::UNPROTECTED);
// if can't create a GL context, we can only abort.
LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed");
@@ -290,7 +334,6 @@
ALOGI("extensions: %s", extensions.getExtensions());
ALOGI("GL_MAX_TEXTURE_SIZE = %zu", engine->getMaxTextureSize());
ALOGI("GL_MAX_VIEWPORT_DIMS = %zu", engine->getMaxViewportDims());
-
return engine;
}
@@ -336,7 +379,7 @@
GLESRenderEngine::GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
EGLConfig config, EGLContext ctxt, EGLSurface stub,
EGLContext protectedContext, EGLSurface protectedStub)
- : renderengine::impl::RenderEngine(args),
+ : RenderEngine(args.renderEngineType),
mEGLDisplay(display),
mEGLConfig(config),
mEGLContext(ctxt),
@@ -346,7 +389,8 @@
mVpWidth(0),
mVpHeight(0),
mFramebufferImageCacheSize(args.imageCacheSize),
- mUseColorManagement(args.useColorManagement) {
+ mUseColorManagement(args.useColorManagement),
+ mPrecacheToneMapperShaderOnly(args.precacheToneMapperShaderOnly) {
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims);
@@ -426,24 +470,37 @@
mPlaceholderBuffer, attributes);
ALOGE_IF(mPlaceholderImage == EGL_NO_IMAGE_KHR, "Failed to create placeholder image: %#x",
eglGetError());
+
+ mShadowTexture = std::make_unique<GLShadowTexture>();
}
GLESRenderEngine::~GLESRenderEngine() {
// Destroy the image manager first.
mImageManager = nullptr;
+ mShadowTexture = nullptr;
+ cleanFramebufferCache();
+ ProgramCache::getInstance().purgeCaches();
std::lock_guard<std::mutex> lock(mRenderingMutex);
+ glDisableVertexAttribArray(Program::position);
unbindFrameBuffer(mDrawingBuffer.get());
mDrawingBuffer = nullptr;
- while (!mFramebufferImageCache.empty()) {
- EGLImageKHR expired = mFramebufferImageCache.front().second;
- mFramebufferImageCache.pop_front();
- eglDestroyImageKHR(mEGLDisplay, expired);
- DEBUG_EGL_IMAGE_TRACKER_DESTROY();
- }
eglDestroyImageKHR(mEGLDisplay, mPlaceholderImage);
mImageCache.clear();
+ if (mStubSurface != EGL_NO_SURFACE) {
+ eglDestroySurface(mEGLDisplay, mStubSurface);
+ }
+ if (mProtectedStubSurface != EGL_NO_SURFACE) {
+ eglDestroySurface(mEGLDisplay, mProtectedStubSurface);
+ }
+ if (mEGLContext != EGL_NO_CONTEXT) {
+ eglDestroyContext(mEGLDisplay, mEGLContext);
+ }
+ if (mProtectedEGLContext != EGL_NO_CONTEXT) {
+ eglDestroyContext(mEGLDisplay, mProtectedEGLContext);
+ }
eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglTerminate(mEGLDisplay);
+ eglReleaseThread();
}
std::unique_ptr<Framebuffer> GLESRenderEngine::createFramebuffer() {
@@ -458,10 +515,9 @@
return mDrawingBuffer.get();
}
-void GLESRenderEngine::primeCache() const {
+void GLESRenderEngine::primeCache() {
ProgramCache::getInstance().primeCache(mInProtectedContext ? mProtectedEGLContext : mEGLContext,
- mArgs.useColorManagement,
- mArgs.precacheToneMapperShaderOnly);
+ mUseColorManagement, mPrecacheToneMapperShaderOnly);
}
base::unique_fd GLESRenderEngine::flush() {
@@ -624,13 +680,8 @@
}
}
-status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName,
- const sp<GraphicBuffer>& buffer,
- const sp<Fence>& bufferFence) {
- if (buffer == nullptr) {
- return BAD_VALUE;
- }
-
+void GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& bufferFence) {
ATRACE_CALL();
bool found = false;
@@ -646,7 +697,8 @@
if (!found) {
status_t cacheResult = mImageManager->cache(buffer);
if (cacheResult != NO_ERROR) {
- return cacheResult;
+ ALOGE("Error with caching buffer: %d", cacheResult);
+ return;
}
}
@@ -663,7 +715,7 @@
// We failed creating the image if we got here, so bail out.
ALOGE("Failed to create an EGLImage when rendering");
bindExternalTextureImage(texName, *createImage());
- return NO_INIT;
+ return;
}
bindExternalTextureImage(texName, *cachedImage->second);
@@ -676,25 +728,27 @@
base::unique_fd fenceFd(bufferFence->dup());
if (fenceFd == -1) {
ALOGE("error dup'ing fence fd: %d", errno);
- return -errno;
+ return;
}
if (!waitFence(std::move(fenceFd))) {
ALOGE("failed to wait on fence fd");
- return UNKNOWN_ERROR;
+ return;
}
} else {
status_t err = bufferFence->waitForever("RenderEngine::bindExternalTextureBuffer");
if (err != NO_ERROR) {
ALOGE("error waiting for fence: %d", err);
- return err;
+ return;
}
}
}
- return NO_ERROR;
+ return;
}
-void GLESRenderEngine::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+void GLESRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
+ bool /*isRenderable*/) {
+ ATRACE_CALL();
mImageManager->cacheAsync(buffer, nullptr);
}
@@ -725,9 +779,9 @@
bool created = newImage->setNativeWindowBuffer(buffer->getNativeBuffer(),
buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
if (!created) {
- ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d",
- buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(),
- buffer->getPixelFormat());
+ ALOGE("Failed to create image. id=%" PRIx64 " size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d",
+ buffer->getId(), buffer->getWidth(), buffer->getHeight(), buffer->getStride(),
+ buffer->getUsage(), buffer->getPixelFormat());
return NO_INIT;
}
@@ -744,8 +798,8 @@
return NO_ERROR;
}
-void GLESRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) {
- mImageManager->releaseAsync(bufferId, nullptr);
+void GLESRenderEngine::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+ mImageManager->releaseAsync(buffer->getId(), nullptr);
}
std::shared_ptr<ImageManager::Barrier> GLESRenderEngine::unbindExternalTextureBufferForTesting(
@@ -773,6 +827,12 @@
ALOGV("Failed to find image for buffer: %" PRIu64, bufferId);
}
+int GLESRenderEngine::getContextPriority() {
+ int value;
+ eglQueryContext(mEGLDisplay, mEGLContext, EGL_CONTEXT_PRIORITY_LEVEL_IMG, &value);
+ return value;
+}
+
FloatRect GLESRenderEngine::setupLayerCropping(const LayerSettings& layer, Mesh& mesh) {
// Translate win by the rounded corners rect coordinates, to have all values in
// layer coordinate space.
@@ -945,6 +1005,7 @@
// Bind the texture to placeholder so that backing image data can be freed.
GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(getFramebufferForDrawing());
glFramebuffer->allocateBuffers(1, 1, mPlaceholderDrawBuffer);
+
// Release the cached fence here, so that we don't churn reallocations when
// we could no-op repeated calls of this method instead.
mLastDrawFence = nullptr;
@@ -952,6 +1013,20 @@
return true;
}
+void GLESRenderEngine::cleanFramebufferCache() {
+ std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
+ // Bind the texture to placeholder so that backing image data can be freed.
+ GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(getFramebufferForDrawing());
+ glFramebuffer->allocateBuffers(1, 1, mPlaceholderDrawBuffer);
+
+ while (!mFramebufferImageCache.empty()) {
+ EGLImageKHR expired = mFramebufferImageCache.front().second;
+ mFramebufferImageCache.pop_front();
+ eglDestroyImageKHR(mEGLDisplay, expired);
+ DEBUG_EGL_IMAGE_TRACKER_DESTROY();
+ }
+}
+
void GLESRenderEngine::checkErrors() const {
checkErrors(nullptr);
}
@@ -1028,7 +1103,7 @@
status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
const std::vector<const LayerSettings*>& layers,
- ANativeWindowBuffer* const buffer,
+ const std::shared_ptr<ExternalTexture>& buffer,
const bool useFramebufferCache, base::unique_fd&& bufferFence,
base::unique_fd* drawFence) {
ATRACE_CALL();
@@ -1051,6 +1126,8 @@
return BAD_VALUE;
}
+ validateOutputBufferUsage(buffer->getBuffer());
+
std::unique_ptr<BindNativeBufferAsFramebuffer> fbo;
// Gathering layers that requested blur, we'll need them to decide when to render to an
// offscreen buffer, and when to render to the native buffer.
@@ -1065,10 +1142,14 @@
const auto blurLayersSize = blurLayers.size();
if (blurLayersSize == 0) {
- fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer, useFramebufferCache);
+ fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this,
+ buffer->getBuffer()
+ .get()
+ ->getNativeBuffer(),
+ useFramebufferCache);
if (fbo->getStatus() != NO_ERROR) {
ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
- buffer->handle);
+ buffer->getBuffer()->handle);
checkErrors();
return fbo->getStatus();
}
@@ -1079,7 +1160,7 @@
mBlurFilter->setAsDrawTarget(display, blurLayers.front()->backgroundBlurRadius);
if (status != NO_ERROR) {
ALOGE("Failed to prepare blur filter! Aborting GPU composition for buffer (%p).",
- buffer->handle);
+ buffer->getBuffer()->handle);
checkErrors();
return status;
}
@@ -1116,14 +1197,17 @@
auto status = mBlurFilter->prepare();
if (status != NO_ERROR) {
ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
- buffer->handle);
+ buffer->getBuffer()->handle);
checkErrors("Can't render first blur pass");
return status;
}
if (blurLayers.size() == 0) {
// Done blurring, time to bind the native FBO and render our blur onto it.
- fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer,
+ fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this,
+ buffer.get()
+ ->getBuffer()
+ ->getNativeBuffer(),
useFramebufferCache);
status = fbo->getStatus();
setViewportAndProjection(display.physicalDisplay, display.clip);
@@ -1135,7 +1219,7 @@
}
if (status != NO_ERROR) {
ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
- buffer->handle);
+ buffer->getBuffer()->handle);
checkErrors("Can't bind native framebuffer");
return status;
}
@@ -1143,7 +1227,7 @@
status = mBlurFilter->render(blurLayersSize > 1);
if (status != NO_ERROR) {
ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
- buffer->handle);
+ buffer->getBuffer()->handle);
checkErrors("Can't render blur filter");
return status;
}
@@ -1170,7 +1254,8 @@
disableTexture = false;
isOpaque = layer->source.buffer.isOpaque;
- sp<GraphicBuffer> gBuf = layer->source.buffer.buffer;
+ sp<GraphicBuffer> gBuf = layer->source.buffer.buffer->getBuffer();
+ validateInputBufferUsage(gBuf);
bindExternalTextureBuffer(layer->source.buffer.textureName, gBuf,
layer->source.buffer.fence);
@@ -1190,6 +1275,11 @@
texCoords[2] = vec2(1.0, 1.0);
texCoords[3] = vec2(1.0, 0.0);
setupLayerTexturing(texture);
+
+ // Do not cache protected EGLImage, protected memory is limited.
+ if (gBuf->getUsage() & GRALLOC_USAGE_PROTECTED) {
+ unmapExternalTextureBuffer(gBuf);
+ }
}
const half3 solidColor = layer->source.solidColor;
@@ -1564,7 +1654,8 @@
}
EGLContext GLESRenderEngine::createEglContext(EGLDisplay display, EGLConfig config,
- EGLContext shareContext, bool useContextPriority,
+ EGLContext shareContext,
+ std::optional<ContextPriority> contextPriority,
Protection protection) {
EGLint renderableType = 0;
if (config == EGL_NO_CONFIG) {
@@ -1587,9 +1678,23 @@
contextAttributes.reserve(7);
contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
contextAttributes.push_back(contextClientVersion);
- if (useContextPriority) {
+ if (contextPriority) {
contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
- contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
+ switch (*contextPriority) {
+ case ContextPriority::REALTIME:
+ contextAttributes.push_back(EGL_CONTEXT_PRIORITY_REALTIME_NV);
+ break;
+ case ContextPriority::MEDIUM:
+ contextAttributes.push_back(EGL_CONTEXT_PRIORITY_MEDIUM_IMG);
+ break;
+ case ContextPriority::LOW:
+ contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LOW_IMG);
+ break;
+ case ContextPriority::HIGH:
+ default:
+ contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
+ break;
+ }
}
if (protection == Protection::PROTECTED) {
contextAttributes.push_back(EGL_PROTECTED_CONTENT_EXT);
@@ -1766,7 +1871,7 @@
mState.cornerRadius = 0.0f;
mState.drawShadows = true;
- setupLayerTexturing(mShadowTexture.getTexture());
+ setupLayerTexturing(mShadowTexture->getTexture());
drawMesh(mesh);
mState.drawShadows = false;
}
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index d554041..e7ed9c0 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -48,7 +48,7 @@
class GLImage;
class BlurFilter;
-class GLESRenderEngine : public impl::RenderEngine {
+class GLESRenderEngine : public RenderEngine {
public:
static std::unique_ptr<GLESRenderEngine> create(const RenderEngineCreationArgs& args);
@@ -57,25 +57,21 @@
EGLSurface protectedStub);
~GLESRenderEngine() override EXCLUDES(mRenderingMutex);
- void primeCache() const override;
+ void primeCache() override;
void genTextures(size_t count, uint32_t* names) override;
void deleteTextures(size_t count, uint32_t const* names) override;
- void bindExternalTextureImage(uint32_t texName, const Image& image) override;
- status_t bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
- const sp<Fence>& fence) EXCLUDES(mRenderingMutex);
- void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex);
- void unbindExternalTextureBuffer(uint64_t bufferId) EXCLUDES(mRenderingMutex);
- status_t bindFrameBuffer(Framebuffer* framebuffer) override;
- void unbindFrameBuffer(Framebuffer* framebuffer) override;
-
bool isProtected() const override { return mInProtectedContext; }
bool supportsProtectedContent() const override;
bool useProtectedContext(bool useProtectedContext) override;
status_t drawLayers(const DisplaySettings& display,
const std::vector<const LayerSettings*>& layers,
- ANativeWindowBuffer* buffer, const bool useFramebufferCache,
- base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
+ const std::shared_ptr<ExternalTexture>& buffer,
+ const bool useFramebufferCache, base::unique_fd&& bufferFence,
+ base::unique_fd* drawFence) override;
bool cleanupPostRender(CleanupMode mode) override;
+ int getContextPriority() override;
+ bool supportsBackgroundBlur() override { return mBlurFilter != nullptr; }
+ void onPrimaryDisplaySizeChanged(ui::Size size) override {}
EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
// Creates an output image for rendering to
@@ -102,13 +98,18 @@
std::shared_ptr<ImageManager::Barrier> unbindExternalTextureBufferForTesting(uint64_t bufferId);
protected:
- Framebuffer* getFramebufferForDrawing() override;
+ Framebuffer* getFramebufferForDrawing();
void dump(std::string& result) override EXCLUDES(mRenderingMutex)
EXCLUDES(mFramebufferImageCacheMutex);
size_t getMaxTextureSize() const override;
size_t getMaxViewportDims() const override;
+ void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable)
+ EXCLUDES(mRenderingMutex);
+ void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex);
private:
+ friend class BindNativeBufferAsFramebuffer;
+
enum GlesVersion {
GLES_VERSION_1_0 = 0x10000,
GLES_VERSION_1_1 = 0x10001,
@@ -119,8 +120,11 @@
static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
static GlesVersion parseGlesVersion(const char* str);
static EGLContext createEglContext(EGLDisplay display, EGLConfig config,
- EGLContext shareContext, bool useContextPriority,
+ EGLContext shareContext,
+ std::optional<ContextPriority> contextPriority,
Protection protection);
+ static std::optional<RenderEngine::ContextPriority> createContextPriority(
+ const RenderEngineCreationArgs& args);
static EGLSurface createStubEglPbufferSurface(EGLDisplay display, EGLConfig config,
int hwcFormat, Protection protection);
std::unique_ptr<Framebuffer> createFramebuffer();
@@ -133,6 +137,12 @@
status_t cacheExternalTextureBufferInternal(const sp<GraphicBuffer>& buffer)
EXCLUDES(mRenderingMutex);
void unbindExternalTextureBufferInternal(uint64_t bufferId) EXCLUDES(mRenderingMutex);
+ status_t bindFrameBuffer(Framebuffer* framebuffer);
+ void unbindFrameBuffer(Framebuffer* framebuffer);
+ void bindExternalTextureImage(uint32_t texName, const Image& image);
+ void bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& fence) EXCLUDES(mRenderingMutex);
+ void cleanFramebufferCache() EXCLUDES(mFramebufferImageCacheMutex) override;
// A data space is considered HDR data space if it has BT2020 color space
// with PQ or HLG transfer function.
@@ -190,7 +200,7 @@
GLuint mVpWidth;
GLuint mVpHeight;
Description mState;
- GLShadowTexture mShadowTexture;
+ std::unique_ptr<GLShadowTexture> mShadowTexture = nullptr;
mat4 mSrgbToXyz;
mat4 mDisplayP3ToXyz;
@@ -229,6 +239,10 @@
// supports sRGB, DisplayP3 color spaces.
const bool mUseColorManagement = false;
+ // Whether only shaders performing tone mapping from HDR to SDR will be generated on
+ // primeCache().
+ const bool mPrecacheToneMapperShaderOnly = false;
+
// Cache of GL images that we'll store per GraphicBuffer ID
std::unordered_map<uint64_t, std::unique_ptr<Image>> mImageCache GUARDED_BY(mRenderingMutex);
std::unordered_map<uint32_t, std::optional<uint64_t>> mTextureView;
@@ -287,7 +301,7 @@
friend class BlurFilter;
friend class GenericProgram;
std::unique_ptr<FlushTracer> mFlushTracer;
- std::unique_ptr<ImageManager> mImageManager = std::make_unique<ImageManager>(this);
+ std::unique_ptr<ImageManager> mImageManager;
};
} // namespace gl
diff --git a/libs/renderengine/gl/GLExtensions.cpp b/libs/renderengine/gl/GLExtensions.cpp
index 2924b0e..3dd534e 100644
--- a/libs/renderengine/gl/GLExtensions.cpp
+++ b/libs/renderengine/gl/GLExtensions.cpp
@@ -120,6 +120,10 @@
if (extensionSet.hasExtension("EGL_KHR_surfaceless_context")) {
mHasSurfacelessContext = true;
}
+
+ if (extensionSet.hasExtension("EGL_NV_context_priority_realtime")) {
+ mHasRealtimePriority = true;
+ }
}
char const* GLExtensions::getEGLVersion() const {
diff --git a/libs/renderengine/gl/GLExtensions.h b/libs/renderengine/gl/GLExtensions.h
index ef00009..e415ff3 100644
--- a/libs/renderengine/gl/GLExtensions.h
+++ b/libs/renderengine/gl/GLExtensions.h
@@ -41,6 +41,7 @@
bool hasContextPriority() const { return mHasContextPriority; }
bool hasSurfacelessContext() const { return mHasSurfacelessContext; }
bool hasProtectedTexture() const { return mHasProtectedTexture; }
+ bool hasRealtimePriority() const { return mHasRealtimePriority; }
void initWithGLStrings(GLubyte const* vendor, GLubyte const* renderer, GLubyte const* version,
GLubyte const* extensions);
@@ -67,6 +68,7 @@
bool mHasContextPriority = false;
bool mHasSurfacelessContext = false;
bool mHasProtectedTexture = false;
+ bool mHasRealtimePriority = false;
String8 mVendor;
String8 mRenderer;
diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp
index 383486b..58d6caa 100644
--- a/libs/renderengine/gl/GLFramebuffer.cpp
+++ b/libs/renderengine/gl/GLFramebuffer.cpp
@@ -38,6 +38,7 @@
}
GLFramebuffer::~GLFramebuffer() {
+ setNativeWindowBuffer(nullptr, false, false);
glDeleteFramebuffers(1, &mFramebufferName);
glDeleteTextures(1, &mTextureName);
}
diff --git a/libs/renderengine/gl/Program.cpp b/libs/renderengine/gl/Program.cpp
index a172c56..26f6166 100644
--- a/libs/renderengine/gl/Program.cpp
+++ b/libs/renderengine/gl/Program.cpp
@@ -82,6 +82,14 @@
}
}
+Program::~Program() {
+ glDetachShader(mProgram, mVertexShader);
+ glDetachShader(mProgram, mFragmentShader);
+ glDeleteShader(mVertexShader);
+ glDeleteShader(mFragmentShader);
+ glDeleteProgram(mProgram);
+}
+
bool Program::isValid() const {
return mInitialized;
}
diff --git a/libs/renderengine/gl/Program.h b/libs/renderengine/gl/Program.h
index 4292645..41f1bf8 100644
--- a/libs/renderengine/gl/Program.h
+++ b/libs/renderengine/gl/Program.h
@@ -54,7 +54,7 @@
};
Program(const ProgramCache::Key& needs, const char* vertex, const char* fragment);
- ~Program() = default;
+ ~Program();
/* whether this object is usable */
bool isValid() const;
diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp
index 611755e..5ff9240 100644
--- a/libs/renderengine/gl/ProgramCache.cpp
+++ b/libs/renderengine/gl/ProgramCache.cpp
@@ -181,9 +181,8 @@
? Key::OUTPUT_TRANSFORM_MATRIX_ON
: Key::OUTPUT_TRANSFORM_MATRIX_OFF)
.set(Key::Key::DISPLAY_COLOR_TRANSFORM_MATRIX_MASK,
- description.hasDisplayColorMatrix()
- ? Key::DISPLAY_COLOR_TRANSFORM_MATRIX_ON
- : Key::DISPLAY_COLOR_TRANSFORM_MATRIX_OFF)
+ description.hasDisplayColorMatrix() ? Key::DISPLAY_COLOR_TRANSFORM_MATRIX_ON
+ : Key::DISPLAY_COLOR_TRANSFORM_MATRIX_OFF)
.set(Key::ROUNDED_CORNERS_MASK,
description.cornerRadius > 0 ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF)
.set(Key::SHADOW_MASK, description.drawShadows ? Key::SHADOW_ON : Key::SHADOW_OFF);
@@ -665,8 +664,7 @@
)__SHADER__";
}
- if (needs.hasTransformMatrix() ||
- (needs.getInputTF() != needs.getOutputTF()) ||
+ if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF()) ||
needs.hasDisplayColorMatrix()) {
if (needs.needsToneMapping()) {
fs << "uniform float displayMaxLuminance;";
@@ -744,8 +742,7 @@
}
}
- if (needs.hasTransformMatrix() ||
- (needs.getInputTF() != needs.getOutputTF()) ||
+ if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF()) ||
needs.hasDisplayColorMatrix()) {
if (!needs.isOpaque() && needs.isPremultiplied()) {
// un-premultiply if needed before linearization
@@ -753,7 +750,8 @@
fs << "gl_FragColor.rgb = gl_FragColor.rgb / (gl_FragColor.a + 0.0019);";
}
fs << "gl_FragColor.rgb = "
- "DisplayColorMatrix(OETF(OutputTransform(OOTF(InputTransform(EOTF(gl_FragColor.rgb))))));";
+ "DisplayColorMatrix(OETF(OutputTransform(OOTF(InputTransform(EOTF(gl_FragColor.rgb)))"
+ ")));";
if (!needs.isOpaque() && needs.isPremultiplied()) {
// and re-premultiply if needed after gamma correction
diff --git a/libs/renderengine/gl/ProgramCache.h b/libs/renderengine/gl/ProgramCache.h
index b492cb3..535d21c 100644
--- a/libs/renderengine/gl/ProgramCache.h
+++ b/libs/renderengine/gl/ProgramCache.h
@@ -149,7 +149,8 @@
return (mKey & OUTPUT_TRANSFORM_MATRIX_MASK) == OUTPUT_TRANSFORM_MATRIX_ON;
}
inline bool hasDisplayColorMatrix() const {
- return (mKey & DISPLAY_COLOR_TRANSFORM_MATRIX_MASK) == DISPLAY_COLOR_TRANSFORM_MATRIX_ON;
+ return (mKey & DISPLAY_COLOR_TRANSFORM_MATRIX_MASK) ==
+ DISPLAY_COLOR_TRANSFORM_MATRIX_ON;
}
inline bool hasTransformMatrix() const {
return hasInputTransformMatrix() || hasOutputTransformMatrix();
@@ -202,6 +203,8 @@
// if none can be found.
void useProgram(const EGLContext context, const Description& description);
+ void purgeCaches() { mCaches.clear(); }
+
private:
// compute a cache Key from a Description
static Key computeKey(const Description& description);
diff --git a/libs/renderengine/gl/filters/BlurFilter.cpp b/libs/renderengine/gl/filters/BlurFilter.cpp
index 19f18c0..3455e08 100644
--- a/libs/renderengine/gl/filters/BlurFilter.cpp
+++ b/libs/renderengine/gl/filters/BlurFilter.cpp
@@ -207,7 +207,7 @@
}
string BlurFilter::getVertexShader() const {
- return R"SHADER(#version 310 es
+ return R"SHADER(#version 300 es
precision mediump float;
in vec2 aPosition;
@@ -222,7 +222,7 @@
}
string BlurFilter::getFragmentShader() const {
- return R"SHADER(#version 310 es
+ return R"SHADER(#version 300 es
precision mediump float;
uniform sampler2D uTexture;
@@ -244,7 +244,7 @@
}
string BlurFilter::getMixFragShader() const {
- string shader = R"SHADER(#version 310 es
+ string shader = R"SHADER(#version 300 es
precision mediump float;
in highp vec2 vUV;
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
index ca16d2c..a637796 100644
--- a/libs/renderengine/include/renderengine/DisplaySettings.h
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -47,8 +47,8 @@
// DataSpace::UNKNOWN otherwise.
ui::Dataspace outputDataspace = ui::Dataspace::UNKNOWN;
- // Additional color transform to apply in linear space after transforming
- // to the output dataspace.
+ // Additional color transform to apply after transforming to the output
+ // dataspace, in non-linear space.
mat4 colorTransform = mat4();
// Region that will be cleared to (0, 0, 0, 1) prior to rendering.
diff --git a/libs/renderengine/include/renderengine/ExternalTexture.h b/libs/renderengine/include/renderengine/ExternalTexture.h
new file mode 100644
index 0000000..07f0833
--- /dev/null
+++ b/libs/renderengine/include/renderengine/ExternalTexture.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#pragma once
+
+#include <android-base/macros.h>
+#include <ui/GraphicBuffer.h>
+
+namespace android::renderengine {
+
+class RenderEngine;
+
+/**
+ * Manages GPU image resources on behalf of clients using RenderEngine.
+ *
+ * Clients of RenderEngine are required to wrap their GraphicBuffer objects as an ExternalTexture,
+ * which is then mapped into GPU resources required by RenderEngine. When a client no longer needs
+ * to use the GraphicBuffer as input into RenderEngine::drawLayers, then the client should delete
+ * their ExternalTexture so that resources may be freed.
+ */
+class ExternalTexture {
+public:
+ // Usage specifies the rendering intent for the buffer.
+ enum Usage : uint32_t {
+ // When a buffer is not READABLE but is WRITEABLE, then GLESRenderEngine will use that as a
+ // hint to load the buffer into a separate cache
+ READABLE = 1 << 0,
+
+ // The buffer needs to be mapped as a 2D texture if set, otherwise must be mapped as an
+ // external texture
+ WRITEABLE = 1 << 1,
+ };
+ // Creates an ExternalTexture for the provided buffer and RenderEngine instance, with the given
+ // usage hint of type Usage.
+ ExternalTexture(const sp<GraphicBuffer>& buffer, RenderEngine& renderEngine, uint32_t usage);
+
+ ~ExternalTexture();
+
+ // Retrieves the buffer that is bound to this texture.
+ const sp<GraphicBuffer>& getBuffer() const { return mBuffer; }
+
+private:
+ sp<GraphicBuffer> mBuffer;
+ RenderEngine& mRenderEngine;
+ DISALLOW_COPY_AND_ASSIGN(ExternalTexture);
+};
+
+} // namespace android::renderengine
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 95e9367..c54c5ba 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -16,19 +16,21 @@
#pragma once
-#include <iosfwd>
-
#include <math/mat4.h>
#include <math/vec3.h>
-#include <renderengine/Texture.h>
+#include <renderengine/ExternalTexture.h>
+#include <ui/BlurRegion.h>
#include <ui/Fence.h>
#include <ui/FloatRect.h>
#include <ui/GraphicBuffer.h>
#include <ui/GraphicTypes.h>
#include <ui/Rect.h>
#include <ui/Region.h>
+#include <ui/StretchEffect.h>
#include <ui/Transform.h>
+#include <iosfwd>
+
namespace android {
namespace renderengine {
@@ -37,7 +39,7 @@
// Buffer containing the image that we will render.
// If buffer == nullptr, then the rest of the fields in this struct will be
// ignored.
- sp<GraphicBuffer> buffer = nullptr;
+ std::shared_ptr<ExternalTexture> buffer = nullptr;
// Fence that will fire when the buffer is ready to be bound.
sp<Fence> fence = nullptr;
@@ -151,6 +153,13 @@
ShadowSettings shadow;
int backgroundBlurRadius = 0;
+
+ std::vector<BlurRegion> blurRegions;
+
+ StretchEffect stretchEffect;
+
+ // Name associated with the layer for debugging purposes.
+ std::string name;
};
// Keep in sync with custom comparison function in
@@ -182,7 +191,29 @@
lhs.length == rhs.length && lhs.casterIsTranslucent == rhs.casterIsTranslucent;
}
+static inline bool operator==(const BlurRegion& lhs, const BlurRegion& rhs) {
+ return lhs.alpha == rhs.alpha && lhs.cornerRadiusTL == rhs.cornerRadiusTL &&
+ lhs.cornerRadiusTR == rhs.cornerRadiusTR && lhs.cornerRadiusBL == rhs.cornerRadiusBL &&
+ lhs.cornerRadiusBR == rhs.cornerRadiusBR && lhs.blurRadius == rhs.blurRadius &&
+ lhs.left == rhs.left && lhs.top == rhs.top && lhs.right == rhs.right &&
+ lhs.bottom == rhs.bottom;
+}
+
+static inline bool operator!=(const BlurRegion& lhs, const BlurRegion& rhs) {
+ return !(lhs == rhs);
+}
+
static inline bool operator==(const LayerSettings& lhs, const LayerSettings& rhs) {
+ if (lhs.blurRegions.size() != rhs.blurRegions.size()) {
+ return false;
+ }
+ const auto size = lhs.blurRegions.size();
+ for (size_t i = 0; i < size; i++) {
+ if (lhs.blurRegions[i] != rhs.blurRegions[i]) {
+ return false;
+ }
+ }
+
return lhs.geometry == rhs.geometry && lhs.source == rhs.source && lhs.alpha == rhs.alpha &&
lhs.sourceDataspace == rhs.sourceDataspace &&
lhs.colorTransform == rhs.colorTransform &&
@@ -249,6 +280,10 @@
*os << "\n .colorTransform = " << settings.colorTransform;
*os << "\n .disableBlending = " << settings.disableBlending;
*os << "\n .backgroundBlurRadius = " << settings.backgroundBlurRadius;
+ for (auto blurRegion : settings.blurRegions) {
+ *os << "\n";
+ PrintTo(blurRegion, os);
+ }
*os << "\n .shadow = ";
PrintTo(settings.shadow, os);
*os << "\n}";
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 74bc44b..ddaa7c7 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -17,24 +17,32 @@
#ifndef SF_RENDERENGINE_H_
#define SF_RENDERENGINE_H_
-#include <stdint.h>
-#include <sys/types.h>
-#include <memory>
-
#include <android-base/unique_fd.h>
#include <math/mat4.h>
#include <renderengine/DisplaySettings.h>
+#include <renderengine/ExternalTexture.h>
#include <renderengine/Framebuffer.h>
#include <renderengine/Image.h>
#include <renderengine/LayerSettings.h>
+#include <stdint.h>
+#include <sys/types.h>
#include <ui/GraphicTypes.h>
#include <ui/Transform.h>
+#include <memory>
+
/**
- * Allows to set RenderEngine backend to GLES (default) or Vulkan (NOT yet supported).
+ * Allows to set RenderEngine backend to GLES (default) or SkiaGL (NOT yet supported).
*/
#define PROPERTY_DEBUG_RENDERENGINE_BACKEND "debug.renderengine.backend"
+/**
+ * Turns on recording of skia commands in SkiaGL version of the RE. This property
+ * defines number of milliseconds for the recording to take place. A non zero value
+ * turns on the recording.
+ */
+#define PROPERTY_DEBUG_RENDERENGINE_CAPTURE_SKIA_MS "debug.renderengine.capture_skia_ms"
+
struct ANativeWindowBuffer;
namespace android {
@@ -44,12 +52,16 @@
namespace renderengine {
-class BindNativeBufferAsFramebuffer;
+class ExternalTexture;
class Image;
class Mesh;
class Texture;
struct RenderEngineCreationArgs;
+namespace threaded {
+class RenderEngineThreaded;
+}
+
namespace impl {
class RenderEngine;
}
@@ -65,9 +77,21 @@
LOW = 1,
MEDIUM = 2,
HIGH = 3,
+ REALTIME = 4,
};
- static std::unique_ptr<impl::RenderEngine> create(const RenderEngineCreationArgs& args);
+ enum class RenderEngineType {
+ GLES = 1,
+ THREADED = 2,
+ SKIA_GL = 3,
+ SKIA_GL_THREADED = 4,
+ };
+
+ static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args);
+
+ RenderEngine() : RenderEngine(RenderEngineType::GLES) {}
+
+ RenderEngine(RenderEngineType type) : mRenderEngineType(type) {}
virtual ~RenderEngine() = 0;
@@ -75,42 +99,13 @@
// This interface, while still in use until a suitable replacement is built,
// should be considered deprecated, minus some methods which still may be
// used to support legacy behavior.
- virtual void primeCache() const = 0;
+ virtual void primeCache() = 0;
// dump the extension strings. always call the base class.
virtual void dump(std::string& result) = 0;
- virtual bool useNativeFenceSync() const = 0;
- virtual bool useWaitSync() const = 0;
virtual void genTextures(size_t count, uint32_t* names) = 0;
virtual void deleteTextures(size_t count, uint32_t const* names) = 0;
- virtual void bindExternalTextureImage(uint32_t texName, const Image& image) = 0;
- // Legacy public method used by devices that don't support native fence
- // synchronization in their GPU driver, as this method provides implicit
- // synchronization for latching buffers.
- virtual status_t bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
- const sp<Fence>& fence) = 0;
- // Caches Image resources for this buffer, but does not bind the buffer to
- // a particular texture.
- // Note that work is deferred to an additional thread, i.e. this call
- // is made asynchronously, but the caller can expect that cache/unbind calls
- // are performed in a manner that's conflict serializable, i.e. unbinding
- // a buffer should never occur before binding the buffer if the caller
- // called {bind, cache}ExternalTextureBuffer before calling unbind.
- virtual void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) = 0;
- // Removes internal resources referenced by the bufferId. This method should be
- // invoked when the caller will no longer hold a reference to a GraphicBuffer
- // and needs to clean up its resources.
- // Note that work is deferred to an additional thread, i.e. this call
- // is made asynchronously, but the caller can expect that cache/unbind calls
- // are performed in a manner that's conflict serializable, i.e. unbinding
- // a buffer should never occur before binding the buffer if the caller
- // called {bind, cache}ExternalTextureBuffer before calling unbind.
- virtual void unbindExternalTextureBuffer(uint64_t bufferId) = 0;
- // When binding a native buffer, it must be done before setViewportAndProjection
- // Returns NO_ERROR when binds successfully, NO_MEMORY when there's no memory for allocation.
- virtual status_t bindFrameBuffer(Framebuffer* framebuffer) = 0;
- virtual void unbindFrameBuffer(Framebuffer* framebuffer) = 0;
enum class CleanupMode {
CLEAN_OUTPUT_RESOURCES,
@@ -131,7 +126,7 @@
// do any work.
virtual bool cleanupPostRender(CleanupMode mode = CleanupMode::CLEAN_OUTPUT_RESOURCES) = 0;
- // queries
+ // queries that are required to be thread safe
virtual size_t getMaxTextureSize() const = 0;
virtual size_t getMaxViewportDims() const = 0;
@@ -139,10 +134,17 @@
// ----- BEGIN NEW INTERFACE -----
+ // queries that are required to be thread safe
virtual bool isProtected() const = 0;
virtual bool supportsProtectedContent() const = 0;
+
+ // Attempt to switch RenderEngine into and out of protectedContext mode
virtual bool useProtectedContext(bool useProtectedContext) = 0;
+ // Notify RenderEngine of changes to the dimensions of the primary display
+ // so that it can configure its internal caches accordingly.
+ virtual void onPrimaryDisplaySizeChanged(ui::Size size) = 0;
+
// Renders layers for a particular display via GPU composition. This method
// should be called for every display that needs to be rendered via the GPU.
// @param display The display-wide settings that should be applied prior to
@@ -174,17 +176,56 @@
// now, this always returns NO_ERROR.
virtual status_t drawLayers(const DisplaySettings& display,
const std::vector<const LayerSettings*>& layers,
- ANativeWindowBuffer* buffer, const bool useFramebufferCache,
- base::unique_fd&& bufferFence, base::unique_fd* drawFence) = 0;
+ const std::shared_ptr<ExternalTexture>& buffer,
+ const bool useFramebufferCache, base::unique_fd&& bufferFence,
+ base::unique_fd* drawFence) = 0;
+ virtual void cleanFramebufferCache() = 0;
+ // Returns the priority this context was actually created with. Note: this may not be
+ // the same as specified at context creation time, due to implementation limits on the
+ // number of contexts that can be created at a specific priority level in the system.
+ virtual int getContextPriority() = 0;
+
+ // Returns true if blur was requested in the RenderEngineCreationArgs and the implementation
+ // also supports background blur. If false, no blur will be applied when drawing layers. This
+ // query is required to be thread safe.
+ virtual bool supportsBackgroundBlur() = 0;
+
+ // Returns the current type of RenderEngine instance that was created.
+ // TODO(b/180767535): This is only implemented to allow for backend-specific behavior, which
+ // we should not allow in general, so remove this.
+ RenderEngineType getRenderEngineType() const { return mRenderEngineType; }
+
+ static void validateInputBufferUsage(const sp<GraphicBuffer>&);
+ static void validateOutputBufferUsage(const sp<GraphicBuffer>&);
protected:
- // Gets a framebuffer to render to. This framebuffer may or may not be
- // cached depending on the implementation.
- //
- // Note that this method does not transfer ownership, so the caller most not
- // live longer than RenderEngine.
- virtual Framebuffer* getFramebufferForDrawing() = 0;
- friend class BindNativeBufferAsFramebuffer;
+ // Maps GPU resources for this buffer.
+ // Note that work may be deferred to an additional thread, i.e. this call
+ // is made asynchronously, but the caller can expect that map/unmap calls
+ // are performed in a manner that's conflict serializable, i.e. unmapping
+ // a buffer should never occur before binding the buffer if the caller
+ // called mapExternalTextureBuffer before calling unmap.
+ // Note also that if the buffer contains protected content, then mapping those GPU resources may
+ // be deferred until the buffer is really used for drawing. This is because typical SoCs that
+ // support protected memory only support a limited amount, so optimisitically mapping protected
+ // memory may be too burdensome. If a buffer contains protected content and the RenderEngine
+ // implementation supports protected context, then GPU resources may be mapped into both the
+ // protected and unprotected contexts.
+ // If the buffer may ever be written to by RenderEngine, then isRenderable must be true.
+ virtual void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) = 0;
+ // Unmaps GPU resources used by this buffer. This method should be
+ // invoked when the caller will no longer hold a reference to a GraphicBuffer
+ // and needs to clean up its resources.
+ // Note that if there are multiple callers holding onto the same buffer, then the buffer's
+ // resources may be internally ref-counted to guard against use-after-free errors. Note that
+ // work may be deferred to an additional thread, i.e. this call is expected to be made
+ // asynchronously, but the caller can expect that map/unmap calls are performed in a manner
+ // that's conflict serializable, i.e. unmap a buffer should never occur before binding the
+ // buffer if the caller called mapExternalTextureBuffer before calling unmap.
+ virtual void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) = 0;
+ friend class ExternalTexture;
+ friend class threaded::RenderEngineThreaded;
+ const RenderEngineType mRenderEngineType;
};
struct RenderEngineCreationArgs {
@@ -195,26 +236,25 @@
bool precacheToneMapperShaderOnly;
bool supportsBackgroundBlur;
RenderEngine::ContextPriority contextPriority;
+ RenderEngine::RenderEngineType renderEngineType;
struct Builder;
private:
// must be created by Builder via constructor with full argument list
- RenderEngineCreationArgs(
- int _pixelFormat,
- uint32_t _imageCacheSize,
- bool _useColorManagement,
- bool _enableProtectedContext,
- bool _precacheToneMapperShaderOnly,
- bool _supportsBackgroundBlur,
- RenderEngine::ContextPriority _contextPriority)
- : pixelFormat(_pixelFormat)
- , imageCacheSize(_imageCacheSize)
- , useColorManagement(_useColorManagement)
- , enableProtectedContext(_enableProtectedContext)
- , precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly)
- , supportsBackgroundBlur(_supportsBackgroundBlur)
- , contextPriority(_contextPriority) {}
+ RenderEngineCreationArgs(int _pixelFormat, uint32_t _imageCacheSize, bool _useColorManagement,
+ bool _enableProtectedContext, bool _precacheToneMapperShaderOnly,
+ bool _supportsBackgroundBlur,
+ RenderEngine::ContextPriority _contextPriority,
+ RenderEngine::RenderEngineType _renderEngineType)
+ : pixelFormat(_pixelFormat),
+ imageCacheSize(_imageCacheSize),
+ useColorManagement(_useColorManagement),
+ enableProtectedContext(_enableProtectedContext),
+ precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly),
+ supportsBackgroundBlur(_supportsBackgroundBlur),
+ contextPriority(_contextPriority),
+ renderEngineType(_renderEngineType) {}
RenderEngineCreationArgs() = delete;
};
@@ -249,10 +289,14 @@
this->contextPriority = contextPriority;
return *this;
}
+ Builder& setRenderEngineType(RenderEngine::RenderEngineType renderEngineType) {
+ this->renderEngineType = renderEngineType;
+ return *this;
+ }
RenderEngineCreationArgs build() const {
return RenderEngineCreationArgs(pixelFormat, imageCacheSize, useColorManagement,
enableProtectedContext, precacheToneMapperShaderOnly,
- supportsBackgroundBlur, contextPriority);
+ supportsBackgroundBlur, contextPriority, renderEngineType);
}
private:
@@ -264,46 +308,10 @@
bool precacheToneMapperShaderOnly = false;
bool supportsBackgroundBlur = false;
RenderEngine::ContextPriority contextPriority = RenderEngine::ContextPriority::MEDIUM;
+ RenderEngine::RenderEngineType renderEngineType =
+ RenderEngine::RenderEngineType::SKIA_GL_THREADED;
};
-class BindNativeBufferAsFramebuffer {
-public:
- BindNativeBufferAsFramebuffer(RenderEngine& engine, ANativeWindowBuffer* buffer,
- const bool useFramebufferCache)
- : mEngine(engine), mFramebuffer(mEngine.getFramebufferForDrawing()), mStatus(NO_ERROR) {
- mStatus = mFramebuffer->setNativeWindowBuffer(buffer, mEngine.isProtected(),
- useFramebufferCache)
- ? mEngine.bindFrameBuffer(mFramebuffer)
- : NO_MEMORY;
- }
- ~BindNativeBufferAsFramebuffer() {
- mFramebuffer->setNativeWindowBuffer(nullptr, false, /*arbitrary*/ true);
- mEngine.unbindFrameBuffer(mFramebuffer);
- }
- status_t getStatus() const { return mStatus; }
-
-private:
- RenderEngine& mEngine;
- Framebuffer* mFramebuffer;
- status_t mStatus;
-};
-
-namespace impl {
-
-// impl::RenderEngine contains common implementation that is graphics back-end agnostic.
-class RenderEngine : public renderengine::RenderEngine {
-public:
- virtual ~RenderEngine() = 0;
-
- bool useNativeFenceSync() const override;
- bool useWaitSync() const override;
-
-protected:
- RenderEngine(const RenderEngineCreationArgs& args);
- const RenderEngineCreationArgs mArgs;
-};
-
-} // namespace impl
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index 101cbb7..27dbd1e 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -35,21 +35,10 @@
RenderEngine();
~RenderEngine() override;
- MOCK_METHOD0(getFramebufferForDrawing, Framebuffer*());
- MOCK_CONST_METHOD0(primeCache, void());
+ MOCK_METHOD0(primeCache, void());
MOCK_METHOD1(dump, void(std::string&));
- MOCK_CONST_METHOD0(useNativeFenceSync, bool());
- MOCK_CONST_METHOD0(useWaitSync, bool());
- MOCK_CONST_METHOD0(isCurrent, bool());
MOCK_METHOD2(genTextures, void(size_t, uint32_t*));
MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*));
- MOCK_METHOD2(bindExternalTextureImage, void(uint32_t, const renderengine::Image&));
- MOCK_METHOD1(cacheExternalTextureBuffer, void(const sp<GraphicBuffer>&));
- MOCK_METHOD3(bindExternalTextureBuffer,
- status_t(uint32_t, const sp<GraphicBuffer>&, const sp<Fence>&));
- MOCK_METHOD1(unbindExternalTextureBuffer, void(uint64_t));
- MOCK_METHOD1(bindFrameBuffer, status_t(renderengine::Framebuffer*));
- MOCK_METHOD1(unbindFrameBuffer, void(renderengine::Framebuffer*));
MOCK_METHOD1(drawMesh, void(const renderengine::Mesh&));
MOCK_CONST_METHOD0(getMaxTextureSize, size_t());
MOCK_CONST_METHOD0(getMaxViewportDims, size_t());
@@ -59,7 +48,17 @@
MOCK_METHOD1(cleanupPostRender, bool(CleanupMode mode));
MOCK_METHOD6(drawLayers,
status_t(const DisplaySettings&, const std::vector<const LayerSettings*>&,
- ANativeWindowBuffer*, const bool, base::unique_fd&&, base::unique_fd*));
+ const std::shared_ptr<ExternalTexture>&, const bool, base::unique_fd&&,
+ base::unique_fd*));
+ MOCK_METHOD0(cleanFramebufferCache, void());
+ MOCK_METHOD0(getContextPriority, int());
+ MOCK_METHOD0(supportsBackgroundBlur, bool());
+ MOCK_METHOD1(onPrimaryDisplaySizeChanged, void(ui::Size));
+
+protected:
+ // mock renderengine still needs to implement these, but callers should never need to call them.
+ void mapExternalTextureBuffer(const sp<GraphicBuffer>&, bool) {}
+ void unmapExternalTextureBuffer(const sp<GraphicBuffer>&) {}
};
} // namespace mock
diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp
new file mode 100644
index 0000000..8ae69de
--- /dev/null
+++ b/libs/renderengine/skia/AutoBackendTexture.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2020 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 "AutoBackendTexture.h"
+
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "ColorSpaces.h"
+#include "log/log_main.h"
+#include "utils/Trace.h"
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+AutoBackendTexture::AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer,
+ bool isOutputBuffer)
+ : mIsOutputBuffer(isOutputBuffer) {
+ ATRACE_CALL();
+ AHardwareBuffer_Desc desc;
+ AHardwareBuffer_describe(buffer, &desc);
+ bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT);
+ GrBackendFormat backendFormat =
+ GrAHardwareBufferUtils::GetBackendFormat(context, buffer, desc.format, false);
+ mBackendTexture =
+ GrAHardwareBufferUtils::MakeBackendTexture(context, buffer, desc.width, desc.height,
+ &mDeleteProc, &mUpdateProc, &mImageCtx,
+ createProtectedImage, backendFormat,
+ isOutputBuffer);
+ mColorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format);
+ ALOGE_IF(!mBackendTexture.isValid(),
+ "Failed to create a valid texture. [%p]:[%d,%d] isProtected:%d isWriteable:%d "
+ "format:%d",
+ this, desc.width, desc.height, createProtectedImage, isOutputBuffer, desc.format);
+}
+
+void AutoBackendTexture::unref(bool releaseLocalResources) {
+ if (releaseLocalResources) {
+ mSurface = nullptr;
+ mImage = nullptr;
+ }
+
+ mUsageCount--;
+ if (mUsageCount <= 0) {
+ if (mBackendTexture.isValid()) {
+ mDeleteProc(mImageCtx);
+ mBackendTexture = {};
+ }
+ delete this;
+ }
+}
+
+// releaseSurfaceProc is invoked by SkSurface, when the texture is no longer in use.
+// "releaseContext" contains an "AutoBackendTexture*".
+void AutoBackendTexture::releaseSurfaceProc(SkSurface::ReleaseContext releaseContext) {
+ AutoBackendTexture* textureRelease = reinterpret_cast<AutoBackendTexture*>(releaseContext);
+ textureRelease->unref(false);
+}
+
+// releaseImageProc is invoked by SkImage, when the texture is no longer in use.
+// "releaseContext" contains an "AutoBackendTexture*".
+void AutoBackendTexture::releaseImageProc(SkImage::ReleaseContext releaseContext) {
+ AutoBackendTexture* textureRelease = reinterpret_cast<AutoBackendTexture*>(releaseContext);
+ textureRelease->unref(false);
+}
+
+sk_sp<SkImage> AutoBackendTexture::makeImage(ui::Dataspace dataspace, SkAlphaType alphaType,
+ GrDirectContext* context) {
+ ATRACE_CALL();
+
+ if (mBackendTexture.isValid()) {
+ mUpdateProc(mImageCtx, context);
+ }
+
+ sk_sp<SkImage> image =
+ SkImage::MakeFromTexture(context, mBackendTexture, kTopLeft_GrSurfaceOrigin, mColorType,
+ alphaType, toSkColorSpace(dataspace), releaseImageProc, this);
+ if (image.get()) {
+ // The following ref will be counteracted by releaseProc, when SkImage is discarded.
+ ref();
+ }
+
+ mImage = image;
+ mDataspace = dataspace;
+ LOG_ALWAYS_FATAL_IF(mImage == nullptr,
+ "Unable to generate SkImage. isTextureValid:%d dataspace:%d",
+ mBackendTexture.isValid(), dataspace);
+ return mImage;
+}
+
+sk_sp<SkSurface> AutoBackendTexture::getOrCreateSurface(ui::Dataspace dataspace,
+ GrDirectContext* context) {
+ ATRACE_CALL();
+ LOG_ALWAYS_FATAL_IF(!mIsOutputBuffer, "You can't generate a SkSurface for a read-only texture");
+ if (!mSurface.get() || mDataspace != dataspace) {
+ sk_sp<SkSurface> surface =
+ SkSurface::MakeFromBackendTexture(context, mBackendTexture,
+ kTopLeft_GrSurfaceOrigin, 0, mColorType,
+ toSkColorSpace(dataspace), nullptr,
+ releaseSurfaceProc, this);
+ if (surface.get()) {
+ // The following ref will be counteracted by releaseProc, when SkSurface is discarded.
+ ref();
+ }
+ mSurface = surface;
+ }
+
+ mDataspace = dataspace;
+ LOG_ALWAYS_FATAL_IF(mSurface == nullptr,
+ "Unable to generate SkSurface. isTextureValid:%d dataspace:%d",
+ mBackendTexture.isValid(), dataspace);
+ return mSurface;
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/AutoBackendTexture.h b/libs/renderengine/skia/AutoBackendTexture.h
new file mode 100644
index 0000000..3133de6
--- /dev/null
+++ b/libs/renderengine/skia/AutoBackendTexture.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <GrAHardwareBufferUtils.h>
+#include <GrDirectContext.h>
+#include <SkImage.h>
+#include <SkSurface.h>
+#include <sys/types.h>
+#include <ui/GraphicTypes.h>
+
+#include "android-base/macros.h"
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+/**
+ * AutoBackendTexture manages GPU image lifetime. It is a ref-counted object
+ * that keeps GPU resources alive until the last SkImage or SkSurface object using them is
+ * destroyed.
+ */
+class AutoBackendTexture {
+public:
+ // Local reference that supports RAII-style management of an AutoBackendTexture
+ // AutoBackendTexture by itself can't be managed in a similar fashion because
+ // of shared ownership with Skia objects, so we wrap it here instead.
+ class LocalRef {
+ public:
+ LocalRef(GrDirectContext* context, AHardwareBuffer* buffer, bool isOutputBuffer) {
+ mTexture = new AutoBackendTexture(context, buffer, isOutputBuffer);
+ mTexture->ref();
+ }
+
+ ~LocalRef() {
+ if (mTexture != nullptr) {
+ mTexture->unref(true);
+ }
+ }
+
+ // Makes a new SkImage from the texture content.
+ // As SkImages are immutable but buffer content is not, we create
+ // a new SkImage every time.
+ sk_sp<SkImage> makeImage(ui::Dataspace dataspace, SkAlphaType alphaType,
+ GrDirectContext* context) {
+ return mTexture->makeImage(dataspace, alphaType, context);
+ }
+
+ // Makes a new SkSurface from the texture content, if needed.
+ sk_sp<SkSurface> getOrCreateSurface(ui::Dataspace dataspace, GrDirectContext* context) {
+ return mTexture->getOrCreateSurface(dataspace, context);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(LocalRef);
+
+ private:
+ AutoBackendTexture* mTexture = nullptr;
+ };
+
+private:
+ // Creates a GrBackendTexture whose contents come from the provided buffer.
+ AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer, bool isOutputBuffer);
+
+ // The only way to invoke dtor is with unref, when mUsageCount is 0.
+ ~AutoBackendTexture() {}
+
+ void ref() { mUsageCount++; }
+
+ // releaseLocalResources is true if the underlying SkImage and SkSurface
+ // should be deleted from local tracking.
+ void unref(bool releaseLocalResources);
+
+ // Makes a new SkImage from the texture content.
+ // As SkImages are immutable but buffer content is not, we create
+ // a new SkImage every time.
+ sk_sp<SkImage> makeImage(ui::Dataspace dataspace, SkAlphaType alphaType,
+ GrDirectContext* context);
+
+ // Makes a new SkSurface from the texture content, if needed.
+ sk_sp<SkSurface> getOrCreateSurface(ui::Dataspace dataspace, GrDirectContext* context);
+
+ GrBackendTexture mBackendTexture;
+ GrAHardwareBufferUtils::DeleteImageProc mDeleteProc;
+ GrAHardwareBufferUtils::UpdateImageProc mUpdateProc;
+ GrAHardwareBufferUtils::TexImageCtx mImageCtx;
+
+ static void releaseSurfaceProc(SkSurface::ReleaseContext releaseContext);
+ static void releaseImageProc(SkImage::ReleaseContext releaseContext);
+
+ int mUsageCount = 0;
+
+ const bool mIsOutputBuffer;
+ sk_sp<SkImage> mImage = nullptr;
+ sk_sp<SkSurface> mSurface = nullptr;
+ ui::Dataspace mDataspace = ui::Dataspace::UNKNOWN;
+ SkColorType mColorType = kUnknown_SkColorType;
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
new file mode 100644
index 0000000..a0da888
--- /dev/null
+++ b/libs/renderengine/skia/Cache.cpp
@@ -0,0 +1,298 @@
+/*
+ * Copyright 2021 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 "Cache.h"
+#include "AutoBackendTexture.h"
+#include "SkiaRenderEngine.h"
+#include "android-base/unique_fd.h"
+#include "renderengine/DisplaySettings.h"
+#include "renderengine/LayerSettings.h"
+#include "ui/GraphicBuffer.h"
+#include "ui/GraphicTypes.h"
+#include "ui/PixelFormat.h"
+#include "ui/Rect.h"
+#include "utils/Timers.h"
+
+namespace android::renderengine::skia {
+
+namespace {
+// Warming shader cache, not framebuffer cache.
+constexpr bool kUseFrameBufferCache = false;
+
+// clang-format off
+// Any non-identity matrix will do.
+const auto kScaleAndTranslate = mat4(0.7f, 0.f, 0.f, 0.f,
+ 0.f, 0.7f, 0.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f,
+ 67.3f, 52.2f, 0.f, 1.f);
+const auto kScaleYOnly = mat4(1.f, 0.f, 0.f, 0.f,
+ 0.f, 0.7f, 0.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f,
+ 0.f, 0.f, 0.f, 1.f);
+// clang-format on
+// When choosing dataspaces below, whether the match the destination or not determined whether
+// a color correction effect is added to the shader. There may be other additional shader details
+// for particular color spaces.
+// TODO(b/184842383) figure out which color related shaders are necessary
+constexpr auto kDestDataSpace = ui::Dataspace::SRGB;
+constexpr auto kOtherDataSpace = ui::Dataspace::DISPLAY_P3;
+} // namespace
+
+static void drawShadowLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+ const std::shared_ptr<ExternalTexture>& dstTexture) {
+ // Somewhat arbitrary dimensions, but on screen and slightly shorter, based
+ // on actual use.
+ FloatRect rect(0, 0, display.physicalDisplay.width(), display.physicalDisplay.height() - 30);
+ LayerSettings layer{
+ .geometry =
+ Geometry{
+ .boundaries = rect,
+ .roundedCornersCrop = rect,
+ },
+ // drawShadow ignores alpha
+ .shadow =
+ ShadowSettings{
+ .ambientColor = vec4(0, 0, 0, 0.00935997f),
+ .spotColor = vec4(0, 0, 0, 0.0455841f),
+ .lightPos = vec3(370.508f, -1527.03f, 1650.f),
+ .lightRadius = 2200.0f,
+ .length = 0.955342f,
+ },
+ // important that this matches dest so the general shadow fragment shader doesn't
+ // have color correction added, and important that it be srgb, so the *vertex* shader
+ // doesn't have color correction added.
+ .sourceDataspace = kDestDataSpace,
+ };
+
+ auto layers = std::vector<const LayerSettings*>{&layer};
+ // The identity matrix will generate the fast shader
+ renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, base::unique_fd(),
+ nullptr);
+ // This matrix, which has different scales for x and y, will
+ // generate the slower (more general case) version, which has variants for translucent
+ // casters and rounded rects.
+ // clang-format off
+ layer.geometry.positionTransform = mat4(0.7f, 0.f, 0.f, 0.f,
+ 0.f, 0.8f, 0.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f,
+ 0.f, 0.f, 0.f, 1.f);
+ // clang-format on
+ for (auto translucent : {false, true}) {
+ layer.shadow.casterIsTranslucent = translucent;
+ renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+ base::unique_fd(), nullptr);
+ }
+}
+
+static void drawImageLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+ const std::shared_ptr<ExternalTexture>& dstTexture,
+ const std::shared_ptr<ExternalTexture>& srcTexture) {
+ const Rect& displayRect = display.physicalDisplay;
+ FloatRect rect(0, 0, displayRect.width(), displayRect.height());
+ LayerSettings layer{
+ .geometry =
+ Geometry{
+ .boundaries = rect,
+ .roundedCornersCrop = rect,
+ },
+ .source = PixelSource{.buffer =
+ Buffer{
+ .buffer = srcTexture,
+ .maxMasteringLuminance = 1000.f,
+ .maxContentLuminance = 1000.f,
+ }},
+ };
+
+ auto threeCornerRadii = {0.0f, 0.05f, 50.f};
+ auto oneCornerRadius = {50.f};
+
+ // Test both drawRect and drawRRect
+ auto layers = std::vector<const LayerSettings*>{&layer};
+ for (bool identity : {true, false}) {
+ layer.geometry.positionTransform = identity ? mat4() : kScaleAndTranslate;
+ // Corner radii less than 0.5 creates a special shader. This likely occurs in real usage
+ // due to animating corner radius.
+ // For the non-idenity matrix, only the large corner radius will create a new shader.
+ for (float roundedCornersRadius : identity ? threeCornerRadii : oneCornerRadius) {
+ // roundedCornersCrop is always set, but it is this radius that triggers the behavior
+ layer.geometry.roundedCornersRadius = roundedCornersRadius;
+ for (bool isOpaque : {true, false}) {
+ layer.source.buffer.isOpaque = isOpaque;
+ for (auto alpha : {half(.23999f), half(1.0f)}) {
+ layer.alpha = alpha;
+ renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+ base::unique_fd(), nullptr);
+ }
+ }
+ }
+ }
+}
+
+static void drawSolidLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+ const std::shared_ptr<ExternalTexture>& dstTexture) {
+ const Rect& displayRect = display.physicalDisplay;
+ FloatRect rect(0, 0, displayRect.width(), displayRect.height());
+ LayerSettings layer{
+ .geometry =
+ Geometry{
+ .boundaries = rect,
+ },
+ .source =
+ PixelSource{
+ .solidColor = half3(0.1f, 0.2f, 0.3f),
+ },
+ .alpha = 1,
+ };
+
+ auto layers = std::vector<const LayerSettings*>{&layer};
+ for (auto transform : {mat4(), kScaleAndTranslate}) {
+ layer.geometry.positionTransform = transform;
+ for (float roundedCornersRadius : {0.0f, 0.05f, 50.f}) {
+ layer.geometry.roundedCornersRadius = roundedCornersRadius;
+ renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+ base::unique_fd(), nullptr);
+ }
+ }
+}
+
+static void drawBlurLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+ const std::shared_ptr<ExternalTexture>& dstTexture) {
+ const Rect& displayRect = display.physicalDisplay;
+ FloatRect rect(0, 0, displayRect.width(), displayRect.height());
+ LayerSettings layer{
+ .geometry =
+ Geometry{
+ .boundaries = rect,
+ },
+ .alpha = 1,
+ };
+
+ auto layers = std::vector<const LayerSettings*>{&layer};
+ for (int radius : {9, 60}) {
+ layer.backgroundBlurRadius = radius;
+ renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+ base::unique_fd(), nullptr);
+ }
+}
+
+static void drawTextureScaleLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+ const std::shared_ptr<ExternalTexture>& dstTexture,
+ const std::shared_ptr<ExternalTexture>& srcTexture) {
+ const Rect& displayRect = display.physicalDisplay;
+ FloatRect rect(0, 0, displayRect.width(), displayRect.height());
+ LayerSettings layer{
+ .geometry =
+ Geometry{
+ .boundaries = rect,
+ .roundedCornersCrop = rect,
+ .positionTransform = kScaleAndTranslate,
+ .roundedCornersRadius = 300,
+ },
+ .source = PixelSource{.buffer =
+ Buffer{
+ .buffer = srcTexture,
+ .maxMasteringLuminance = 1000.f,
+ .maxContentLuminance = 1000.f,
+ .textureTransform = kScaleYOnly,
+ }},
+ .sourceDataspace = kOtherDataSpace,
+ };
+
+ auto layers = std::vector<const LayerSettings*>{&layer};
+ for (float alpha : {0.5f, 1.f}) {
+ layer.alpha = alpha,
+ renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+ base::unique_fd(), nullptr);
+ }
+}
+
+//
+// The collection of shaders cached here were found by using perfetto to record shader compiles
+// during actions that involve RenderEngine, logging the layer settings, and the shader code
+// and reproducing those settings here.
+//
+// It is helpful when debugging this to turn on
+// in SkGLRenderEngine.cpp:
+// kPrintLayerSettings = true
+// kFlushAfterEveryLayer = true
+// in external/skia/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp
+// gPrintSKSL = true
+//
+// TODO(b/184631553) cache the shader involved in youtube pip return.
+void Cache::primeShaderCache(SkiaRenderEngine* renderengine) {
+ const int previousCount = renderengine->reportShadersCompiled();
+ if (previousCount) {
+ ALOGD("%d Shaders already compiled before Cache::primeShaderCache ran\n", previousCount);
+ }
+ const nsecs_t timeBefore = systemTime();
+ // The dimensions should not matter, so long as we draw inside them.
+ const Rect displayRect(0, 0, 1080, 2340);
+ DisplaySettings display{
+ .physicalDisplay = displayRect,
+ .clip = displayRect,
+ .maxLuminance = 500,
+ .outputDataspace = kDestDataSpace,
+ };
+
+ const int64_t usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
+
+ sp<GraphicBuffer> dstBuffer =
+ new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1,
+ usage, "primeShaderCache_dst");
+
+ const auto dstTexture = std::make_shared<ExternalTexture>(dstBuffer, *renderengine,
+ ExternalTexture::Usage::WRITEABLE);
+ // This buffer will be the source for the call to drawImageLayers. Draw
+ // something to it as a placeholder for what an app draws. We should draw
+ // something, but the details are not important. Make use of the shadow layer drawing step
+ // to populate it.
+ sp<GraphicBuffer> srcBuffer =
+ new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1,
+ usage, "drawImageLayer_src");
+
+ const auto srcTexture =
+ std::make_shared<ExternalTexture>(srcBuffer, *renderengine,
+ ExternalTexture::Usage::READABLE |
+ ExternalTexture::Usage::WRITEABLE);
+
+ drawSolidLayers(renderengine, display, dstTexture);
+ drawShadowLayers(renderengine, display, srcTexture);
+ drawBlurLayers(renderengine, display, dstTexture);
+ // The majority of shaders are related to sampling images.
+ drawImageLayers(renderengine, display, dstTexture, srcTexture);
+
+ // should be the same as AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
+ const int64_t usageExternal = GRALLOC_USAGE_HW_TEXTURE;
+
+ sp<GraphicBuffer> externalBuffer =
+ new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1,
+ usageExternal, "primeShaderCache_external");
+ const auto externalTexture =
+ std::make_shared<ExternalTexture>(externalBuffer, *renderengine,
+ ExternalTexture::Usage::READABLE);
+ // TODO(b/184665179) doubles number of image shader compilations, but only somewhere
+ // between 6 and 8 will occur in real uses.
+ drawImageLayers(renderengine, display, dstTexture, externalTexture);
+
+ // Draw layers for b/185569240.
+ drawTextureScaleLayers(renderengine, display, dstTexture, externalTexture);
+
+ const nsecs_t timeAfter = systemTime();
+ const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
+ const int shadersCompiled = renderengine->reportShadersCompiled();
+ ALOGD("Shader cache generated %d shaders in %f ms\n", shadersCompiled, compileTimeMs);
+}
+
+} // namespace android::renderengine::skia
diff --git a/libs/ui/UiConfig.cpp b/libs/renderengine/skia/Cache.h
similarity index 66%
copy from libs/ui/UiConfig.cpp
copy to libs/renderengine/skia/Cache.h
index 0ac863d..437571e 100644
--- a/libs/ui/UiConfig.cpp
+++ b/libs/renderengine/skia/Cache.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright 2021 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.
@@ -14,15 +14,18 @@
* limitations under the License.
*/
-#include <ui/UiConfig.h>
+#pragma once
-namespace android {
+namespace android::renderengine::skia {
-void appendUiConfigString(std::string& configStr) {
- static const char* config =
- " [libui]";
- configStr.append(config);
-}
+class SkiaRenderEngine;
+class Cache {
+public:
+ static void primeShaderCache(SkiaRenderEngine*);
-}; // namespace android
+private:
+ Cache() = default;
+};
+
+} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/ColorSpaces.cpp b/libs/renderengine/skia/ColorSpaces.cpp
new file mode 100644
index 0000000..ff4d348
--- /dev/null
+++ b/libs/renderengine/skia/ColorSpaces.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2021 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 "ColorSpaces.h"
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+sk_sp<SkColorSpace> toSkColorSpace(ui::Dataspace dataspace) {
+ skcms_Matrix3x3 gamut;
+ switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
+ case HAL_DATASPACE_STANDARD_BT709:
+ gamut = SkNamedGamut::kSRGB;
+ break;
+ case HAL_DATASPACE_STANDARD_BT2020:
+ gamut = SkNamedGamut::kRec2020;
+ break;
+ case HAL_DATASPACE_STANDARD_DCI_P3:
+ gamut = SkNamedGamut::kDisplayP3;
+ break;
+ default:
+ gamut = SkNamedGamut::kSRGB;
+ break;
+ }
+
+ switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_LINEAR:
+ return SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, gamut);
+ case HAL_DATASPACE_TRANSFER_SRGB:
+ return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut);
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ return SkColorSpace::MakeRGB(SkNamedTransferFn::kPQ, gamut);
+ case HAL_DATASPACE_TRANSFER_HLG:
+ return SkColorSpace::MakeRGB(SkNamedTransferFn::kHLG, gamut);
+ default:
+ return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut);
+ }
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/skia/ColorSpaces.h b/libs/renderengine/skia/ColorSpaces.h
new file mode 100644
index 0000000..2cbdeb8
--- /dev/null
+++ b/libs/renderengine/skia/ColorSpaces.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#pragma once
+
+#include "SkColorSpace.h"
+#include "ui/GraphicTypes.h"
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+// Converts an android dataspace to a supported SkColorSpace
+// Supported dataspaces are
+// 1. sRGB
+// 2. Display P3
+// 3. BT2020 PQ
+// 4. BT2020 HLG
+// Unknown primaries are mapped to BT709, and unknown transfer functions
+// are mapped to sRGB.
+sk_sp<SkColorSpace> toSkColorSpace(ui::Dataspace dataspace);
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
new file mode 100644
index 0000000..377b6f8
--- /dev/null
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -0,0 +1,1336 @@
+/*
+ * Copyright 2020 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.
+ */
+
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "SkiaGLRenderEngine.h"
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GrContextOptions.h>
+#include <SkCanvas.h>
+#include <SkColorFilter.h>
+#include <SkColorMatrix.h>
+#include <SkColorSpace.h>
+#include <SkGraphics.h>
+#include <SkImage.h>
+#include <SkImageFilters.h>
+#include <SkRegion.h>
+#include <SkShadowUtils.h>
+#include <SkSurface.h>
+#include <android-base/stringprintf.h>
+#include <gl/GrGLInterface.h>
+#include <sync/sync.h>
+#include <ui/BlurRegion.h>
+#include <ui/DebugUtils.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/Trace.h>
+
+#include <cmath>
+#include <cstdint>
+#include <memory>
+
+#include "../gl/GLExtensions.h"
+#include "Cache.h"
+#include "ColorSpaces.h"
+#include "SkBlendMode.h"
+#include "SkImageInfo.h"
+#include "filters/BlurFilter.h"
+#include "filters/LinearEffect.h"
+#include "log/log_main.h"
+#include "skia/debug/SkiaCapture.h"
+#include "skia/debug/SkiaMemoryReporter.h"
+#include "system/graphics-base-v1.0.h"
+
+namespace {
+// Debugging settings
+static const bool kPrintLayerSettings = false;
+static const bool kFlushAfterEveryLayer = false;
+} // namespace
+
+bool checkGlError(const char* op, int lineNumber);
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+using base::StringAppendF;
+
+static status_t selectConfigForAttribute(EGLDisplay dpy, EGLint const* attrs, EGLint attribute,
+ EGLint wanted, EGLConfig* outConfig) {
+ EGLint numConfigs = -1, n = 0;
+ eglGetConfigs(dpy, nullptr, 0, &numConfigs);
+ std::vector<EGLConfig> configs(numConfigs, EGL_NO_CONFIG_KHR);
+ eglChooseConfig(dpy, attrs, configs.data(), configs.size(), &n);
+ configs.resize(n);
+
+ if (!configs.empty()) {
+ if (attribute != EGL_NONE) {
+ for (EGLConfig config : configs) {
+ EGLint value = 0;
+ eglGetConfigAttrib(dpy, config, attribute, &value);
+ if (wanted == value) {
+ *outConfig = config;
+ return NO_ERROR;
+ }
+ }
+ } else {
+ // just pick the first one
+ *outConfig = configs[0];
+ return NO_ERROR;
+ }
+ }
+
+ return NAME_NOT_FOUND;
+}
+
+static status_t selectEGLConfig(EGLDisplay display, EGLint format, EGLint renderableType,
+ EGLConfig* config) {
+ // select our EGLConfig. It must support EGL_RECORDABLE_ANDROID if
+ // it is to be used with WIFI displays
+ status_t err;
+ EGLint wantedAttribute;
+ EGLint wantedAttributeValue;
+
+ std::vector<EGLint> attribs;
+ if (renderableType) {
+ const ui::PixelFormat pixelFormat = static_cast<ui::PixelFormat>(format);
+ const bool is1010102 = pixelFormat == ui::PixelFormat::RGBA_1010102;
+
+ // Default to 8 bits per channel.
+ const EGLint tmpAttribs[] = {
+ EGL_RENDERABLE_TYPE,
+ renderableType,
+ EGL_RECORDABLE_ANDROID,
+ EGL_TRUE,
+ EGL_SURFACE_TYPE,
+ EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
+ EGL_FRAMEBUFFER_TARGET_ANDROID,
+ EGL_TRUE,
+ EGL_RED_SIZE,
+ is1010102 ? 10 : 8,
+ EGL_GREEN_SIZE,
+ is1010102 ? 10 : 8,
+ EGL_BLUE_SIZE,
+ is1010102 ? 10 : 8,
+ EGL_ALPHA_SIZE,
+ is1010102 ? 2 : 8,
+ EGL_NONE,
+ };
+ std::copy(tmpAttribs, tmpAttribs + (sizeof(tmpAttribs) / sizeof(EGLint)),
+ std::back_inserter(attribs));
+ wantedAttribute = EGL_NONE;
+ wantedAttributeValue = EGL_NONE;
+ } else {
+ // if no renderable type specified, fallback to a simplified query
+ wantedAttribute = EGL_NATIVE_VISUAL_ID;
+ wantedAttributeValue = format;
+ }
+
+ err = selectConfigForAttribute(display, attribs.data(), wantedAttribute, wantedAttributeValue,
+ config);
+ if (err == NO_ERROR) {
+ EGLint caveat;
+ if (eglGetConfigAttrib(display, *config, EGL_CONFIG_CAVEAT, &caveat))
+ ALOGW_IF(caveat == EGL_SLOW_CONFIG, "EGL_SLOW_CONFIG selected!");
+ }
+
+ return err;
+}
+
+std::unique_ptr<SkiaGLRenderEngine> SkiaGLRenderEngine::create(
+ const RenderEngineCreationArgs& args) {
+ // initialize EGL for the default display
+ EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (!eglInitialize(display, nullptr, nullptr)) {
+ LOG_ALWAYS_FATAL("failed to initialize EGL");
+ }
+
+ const auto eglVersion = eglQueryString(display, EGL_VERSION);
+ if (!eglVersion) {
+ checkGlError(__FUNCTION__, __LINE__);
+ LOG_ALWAYS_FATAL("eglQueryString(EGL_VERSION) failed");
+ }
+
+ const auto eglExtensions = eglQueryString(display, EGL_EXTENSIONS);
+ if (!eglExtensions) {
+ checkGlError(__FUNCTION__, __LINE__);
+ LOG_ALWAYS_FATAL("eglQueryString(EGL_EXTENSIONS) failed");
+ }
+
+ auto& extensions = gl::GLExtensions::getInstance();
+ extensions.initWithEGLStrings(eglVersion, eglExtensions);
+
+ // The code assumes that ES2 or later is available if this extension is
+ // supported.
+ EGLConfig config = EGL_NO_CONFIG_KHR;
+ if (!extensions.hasNoConfigContext()) {
+ config = chooseEglConfig(display, args.pixelFormat, /*logConfig*/ true);
+ }
+
+ EGLContext protectedContext = EGL_NO_CONTEXT;
+ const std::optional<RenderEngine::ContextPriority> priority = createContextPriority(args);
+ if (args.enableProtectedContext && extensions.hasProtectedContent()) {
+ protectedContext =
+ createEglContext(display, config, nullptr, priority, Protection::PROTECTED);
+ ALOGE_IF(protectedContext == EGL_NO_CONTEXT, "Can't create protected context");
+ }
+
+ EGLContext ctxt =
+ createEglContext(display, config, protectedContext, priority, Protection::UNPROTECTED);
+
+ // if can't create a GL context, we can only abort.
+ LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed");
+
+ EGLSurface placeholder = EGL_NO_SURFACE;
+ if (!extensions.hasSurfacelessContext()) {
+ placeholder = createPlaceholderEglPbufferSurface(display, config, args.pixelFormat,
+ Protection::UNPROTECTED);
+ LOG_ALWAYS_FATAL_IF(placeholder == EGL_NO_SURFACE, "can't create placeholder pbuffer");
+ }
+ EGLBoolean success = eglMakeCurrent(display, placeholder, placeholder, ctxt);
+ LOG_ALWAYS_FATAL_IF(!success, "can't make placeholder pbuffer current");
+ extensions.initWithGLStrings(glGetString(GL_VENDOR), glGetString(GL_RENDERER),
+ glGetString(GL_VERSION), glGetString(GL_EXTENSIONS));
+
+ EGLSurface protectedPlaceholder = EGL_NO_SURFACE;
+ if (protectedContext != EGL_NO_CONTEXT && !extensions.hasSurfacelessContext()) {
+ protectedPlaceholder = createPlaceholderEglPbufferSurface(display, config, args.pixelFormat,
+ Protection::PROTECTED);
+ ALOGE_IF(protectedPlaceholder == EGL_NO_SURFACE,
+ "can't create protected placeholder pbuffer");
+ }
+
+ // initialize the renderer while GL is current
+ std::unique_ptr<SkiaGLRenderEngine> engine =
+ std::make_unique<SkiaGLRenderEngine>(args, display, ctxt, placeholder, protectedContext,
+ protectedPlaceholder);
+
+ ALOGI("OpenGL ES informations:");
+ ALOGI("vendor : %s", extensions.getVendor());
+ ALOGI("renderer : %s", extensions.getRenderer());
+ ALOGI("version : %s", extensions.getVersion());
+ ALOGI("extensions: %s", extensions.getExtensions());
+ ALOGI("GL_MAX_TEXTURE_SIZE = %zu", engine->getMaxTextureSize());
+ ALOGI("GL_MAX_VIEWPORT_DIMS = %zu", engine->getMaxViewportDims());
+
+ return engine;
+}
+
+void SkiaGLRenderEngine::primeCache() {
+ Cache::primeShaderCache(this);
+}
+
+EGLConfig SkiaGLRenderEngine::chooseEglConfig(EGLDisplay display, int format, bool logConfig) {
+ status_t err;
+ EGLConfig config;
+
+ // First try to get an ES3 config
+ err = selectEGLConfig(display, format, EGL_OPENGL_ES3_BIT, &config);
+ if (err != NO_ERROR) {
+ // If ES3 fails, try to get an ES2 config
+ err = selectEGLConfig(display, format, EGL_OPENGL_ES2_BIT, &config);
+ if (err != NO_ERROR) {
+ // If ES2 still doesn't work, probably because we're on the emulator.
+ // try a simplified query
+ ALOGW("no suitable EGLConfig found, trying a simpler query");
+ err = selectEGLConfig(display, format, 0, &config);
+ if (err != NO_ERROR) {
+ // this EGL is too lame for android
+ LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up");
+ }
+ }
+ }
+
+ if (logConfig) {
+ // print some debugging info
+ EGLint r, g, b, a;
+ eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r);
+ eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g);
+ eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b);
+ eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a);
+ ALOGI("EGL information:");
+ ALOGI("vendor : %s", eglQueryString(display, EGL_VENDOR));
+ ALOGI("version : %s", eglQueryString(display, EGL_VERSION));
+ ALOGI("extensions: %s", eglQueryString(display, EGL_EXTENSIONS));
+ ALOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS) ?: "Not Supported");
+ ALOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config);
+ }
+
+ return config;
+}
+
+sk_sp<SkData> SkiaGLRenderEngine::SkSLCacheMonitor::load(const SkData& key) {
+ // This "cache" does not actually cache anything. It just allows us to
+ // monitor Skia's internal cache. So this method always returns null.
+ return nullptr;
+}
+
+void SkiaGLRenderEngine::SkSLCacheMonitor::store(const SkData& key, const SkData& data,
+ const SkString& description) {
+ mShadersCachedSinceLastCall++;
+}
+
+void SkiaGLRenderEngine::assertShadersCompiled(int numShaders) {
+ const int cached = mSkSLCacheMonitor.shadersCachedSinceLastCall();
+ LOG_ALWAYS_FATAL_IF(cached != numShaders, "Attempted to cache %i shaders; cached %i",
+ numShaders, cached);
+}
+
+int SkiaGLRenderEngine::reportShadersCompiled() {
+ return mSkSLCacheMonitor.shadersCachedSinceLastCall();
+}
+
+SkiaGLRenderEngine::SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
+ EGLContext ctxt, EGLSurface placeholder,
+ EGLContext protectedContext, EGLSurface protectedPlaceholder)
+ : SkiaRenderEngine(args.renderEngineType),
+ mEGLDisplay(display),
+ mEGLContext(ctxt),
+ mPlaceholderSurface(placeholder),
+ mProtectedEGLContext(protectedContext),
+ mProtectedPlaceholderSurface(protectedPlaceholder),
+ mDefaultPixelFormat(static_cast<PixelFormat>(args.pixelFormat)),
+ mUseColorManagement(args.useColorManagement) {
+ sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
+ LOG_ALWAYS_FATAL_IF(!glInterface.get());
+
+ GrContextOptions options;
+ options.fDisableDriverCorrectnessWorkarounds = true;
+ options.fDisableDistanceFieldPaths = true;
+ options.fPersistentCache = &mSkSLCacheMonitor;
+ mGrContext = GrDirectContext::MakeGL(glInterface, options);
+ if (useProtectedContext(true)) {
+ mProtectedGrContext = GrDirectContext::MakeGL(glInterface, options);
+ useProtectedContext(false);
+ }
+
+ if (args.supportsBackgroundBlur) {
+ ALOGD("Background Blurs Enabled");
+ mBlurFilter = new BlurFilter();
+ }
+ mCapture = std::make_unique<SkiaCapture>();
+}
+
+SkiaGLRenderEngine::~SkiaGLRenderEngine() {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ if (mBlurFilter) {
+ delete mBlurFilter;
+ }
+
+ mCapture = nullptr;
+
+ mGrContext->flushAndSubmit(true);
+ mGrContext->abandonContext();
+
+ if (mProtectedGrContext) {
+ mProtectedGrContext->flushAndSubmit(true);
+ mProtectedGrContext->abandonContext();
+ }
+
+ if (mPlaceholderSurface != EGL_NO_SURFACE) {
+ eglDestroySurface(mEGLDisplay, mPlaceholderSurface);
+ }
+ if (mProtectedPlaceholderSurface != EGL_NO_SURFACE) {
+ eglDestroySurface(mEGLDisplay, mProtectedPlaceholderSurface);
+ }
+ if (mEGLContext != EGL_NO_CONTEXT) {
+ eglDestroyContext(mEGLDisplay, mEGLContext);
+ }
+ if (mProtectedEGLContext != EGL_NO_CONTEXT) {
+ eglDestroyContext(mEGLDisplay, mProtectedEGLContext);
+ }
+ eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglTerminate(mEGLDisplay);
+ eglReleaseThread();
+}
+
+bool SkiaGLRenderEngine::supportsProtectedContent() const {
+ return mProtectedEGLContext != EGL_NO_CONTEXT;
+}
+
+bool SkiaGLRenderEngine::useProtectedContext(bool useProtectedContext) {
+ if (useProtectedContext == mInProtectedContext) {
+ return true;
+ }
+ if (useProtectedContext && !supportsProtectedContent()) {
+ return false;
+ }
+ const EGLSurface surface =
+ useProtectedContext ? mProtectedPlaceholderSurface : mPlaceholderSurface;
+ const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext;
+ const bool success = eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE;
+
+ if (success) {
+ mInProtectedContext = useProtectedContext;
+ }
+ return success;
+}
+
+base::unique_fd SkiaGLRenderEngine::flush() {
+ ATRACE_CALL();
+ if (!gl::GLExtensions::getInstance().hasNativeFenceSync()) {
+ return base::unique_fd();
+ }
+
+ EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
+ if (sync == EGL_NO_SYNC_KHR) {
+ ALOGW("failed to create EGL native fence sync: %#x", eglGetError());
+ return base::unique_fd();
+ }
+
+ // native fence fd will not be populated until flush() is done.
+ glFlush();
+
+ // get the fence fd
+ base::unique_fd fenceFd(eglDupNativeFenceFDANDROID(mEGLDisplay, sync));
+ eglDestroySyncKHR(mEGLDisplay, sync);
+ if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
+ ALOGW("failed to dup EGL native fence sync: %#x", eglGetError());
+ }
+
+ return fenceFd;
+}
+
+bool SkiaGLRenderEngine::waitFence(base::unique_fd fenceFd) {
+ if (!gl::GLExtensions::getInstance().hasNativeFenceSync() ||
+ !gl::GLExtensions::getInstance().hasWaitSync()) {
+ return false;
+ }
+
+ // release the fd and transfer the ownership to EGLSync
+ EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd.release(), EGL_NONE};
+ EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
+ if (sync == EGL_NO_SYNC_KHR) {
+ ALOGE("failed to create EGL native fence sync: %#x", eglGetError());
+ return false;
+ }
+
+ // XXX: The spec draft is inconsistent as to whether this should return an
+ // EGLint or void. Ignore the return value for now, as it's not strictly
+ // needed.
+ eglWaitSyncKHR(mEGLDisplay, sync, 0);
+ EGLint error = eglGetError();
+ eglDestroySyncKHR(mEGLDisplay, sync);
+ if (error != EGL_SUCCESS) {
+ ALOGE("failed to wait for EGL native fence sync: %#x", error);
+ return false;
+ }
+
+ return true;
+}
+
+static float toDegrees(uint32_t transform) {
+ switch (transform) {
+ case ui::Transform::ROT_90:
+ return 90.0;
+ case ui::Transform::ROT_180:
+ return 180.0;
+ case ui::Transform::ROT_270:
+ return 270.0;
+ default:
+ return 0.0;
+ }
+}
+
+static SkColorMatrix toSkColorMatrix(const mat4& matrix) {
+ return SkColorMatrix(matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0], 0, matrix[0][1],
+ matrix[1][1], matrix[2][1], matrix[3][1], 0, matrix[0][2], matrix[1][2],
+ matrix[2][2], matrix[3][2], 0, matrix[0][3], matrix[1][3], matrix[2][3],
+ matrix[3][3], 0);
+}
+
+static bool needsToneMapping(ui::Dataspace sourceDataspace, ui::Dataspace destinationDataspace) {
+ int64_t sourceTransfer = sourceDataspace & HAL_DATASPACE_TRANSFER_MASK;
+ int64_t destTransfer = destinationDataspace & HAL_DATASPACE_TRANSFER_MASK;
+
+ // Treat unsupported dataspaces as srgb
+ if (destTransfer != HAL_DATASPACE_TRANSFER_LINEAR &&
+ destTransfer != HAL_DATASPACE_TRANSFER_HLG &&
+ destTransfer != HAL_DATASPACE_TRANSFER_ST2084) {
+ destTransfer = HAL_DATASPACE_TRANSFER_SRGB;
+ }
+
+ if (sourceTransfer != HAL_DATASPACE_TRANSFER_LINEAR &&
+ sourceTransfer != HAL_DATASPACE_TRANSFER_HLG &&
+ sourceTransfer != HAL_DATASPACE_TRANSFER_ST2084) {
+ sourceTransfer = HAL_DATASPACE_TRANSFER_SRGB;
+ }
+
+ const bool isSourceLinear = sourceTransfer == HAL_DATASPACE_TRANSFER_LINEAR;
+ const bool isSourceSRGB = sourceTransfer == HAL_DATASPACE_TRANSFER_SRGB;
+ const bool isDestLinear = destTransfer == HAL_DATASPACE_TRANSFER_LINEAR;
+ const bool isDestSRGB = destTransfer == HAL_DATASPACE_TRANSFER_SRGB;
+
+ return !(isSourceLinear && isDestSRGB) && !(isSourceSRGB && isDestLinear) &&
+ sourceTransfer != destTransfer;
+}
+
+void SkiaGLRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
+ bool isRenderable) {
+ // Only run this if RE is running on its own thread. This way the access to GL
+ // operations is guaranteed to be happening on the same thread.
+ if (mRenderEngineType != RenderEngineType::SKIA_GL_THREADED) {
+ return;
+ }
+ ATRACE_CALL();
+
+ // We need to switch the currently bound context if the buffer is protected but the current
+ // context is not. The current state must then be restored after the buffer is cached.
+ const bool protectedContextState = mInProtectedContext;
+ if (!useProtectedContext(protectedContextState ||
+ (buffer->getUsage() & GRALLOC_USAGE_PROTECTED))) {
+ ALOGE("Attempting to cache a buffer into a different context than what is currently bound");
+ return;
+ }
+
+ auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
+ auto& cache = mInProtectedContext ? mProtectedTextureCache : mTextureCache;
+
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ mGraphicBufferExternalRefs[buffer->getId()]++;
+
+ if (const auto& iter = cache.find(buffer->getId()); iter == cache.end()) {
+ std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef =
+ std::make_shared<AutoBackendTexture::LocalRef>(grContext.get(),
+ buffer->toAHardwareBuffer(),
+ isRenderable);
+ cache.insert({buffer->getId(), imageTextureRef});
+ }
+ // restore the original state of the protected context if necessary
+ useProtectedContext(protectedContextState);
+}
+
+void SkiaGLRenderEngine::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ if (const auto& iter = mGraphicBufferExternalRefs.find(buffer->getId());
+ iter != mGraphicBufferExternalRefs.end()) {
+ if (iter->second == 0) {
+ ALOGW("Attempted to unmap GraphicBuffer <id: %" PRId64
+ "> from RenderEngine texture, but the "
+ "ref count was already zero!",
+ buffer->getId());
+ mGraphicBufferExternalRefs.erase(buffer->getId());
+ return;
+ }
+
+ iter->second--;
+
+ if (iter->second == 0) {
+ mTextureCache.erase(buffer->getId());
+ mProtectedTextureCache.erase(buffer->getId());
+ mGraphicBufferExternalRefs.erase(buffer->getId());
+ }
+ }
+}
+
+sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(sk_sp<SkShader> shader,
+ const LayerSettings* layer,
+ const DisplaySettings& display,
+ bool undoPremultipliedAlpha,
+ bool requiresLinearEffect) {
+ if (layer->stretchEffect.hasEffect()) {
+ // TODO: Implement
+ }
+ if (requiresLinearEffect) {
+ const ui::Dataspace inputDataspace =
+ mUseColorManagement ? layer->sourceDataspace : ui::Dataspace::UNKNOWN;
+ const ui::Dataspace outputDataspace =
+ mUseColorManagement ? display.outputDataspace : ui::Dataspace::UNKNOWN;
+
+ LinearEffect effect = LinearEffect{.inputDataspace = inputDataspace,
+ .outputDataspace = outputDataspace,
+ .undoPremultipliedAlpha = undoPremultipliedAlpha};
+
+ auto effectIter = mRuntimeEffects.find(effect);
+ sk_sp<SkRuntimeEffect> runtimeEffect = nullptr;
+ if (effectIter == mRuntimeEffects.end()) {
+ runtimeEffect = buildRuntimeEffect(effect);
+ mRuntimeEffects.insert({effect, runtimeEffect});
+ } else {
+ runtimeEffect = effectIter->second;
+ }
+ return createLinearEffectShader(shader, effect, runtimeEffect, layer->colorTransform,
+ display.maxLuminance,
+ layer->source.buffer.maxMasteringLuminance,
+ layer->source.buffer.maxContentLuminance);
+ }
+ return shader;
+}
+
+void SkiaGLRenderEngine::initCanvas(SkCanvas* canvas, const DisplaySettings& display) {
+ if (CC_UNLIKELY(mCapture->isCaptureRunning())) {
+ // Record display settings when capture is running.
+ std::stringstream displaySettings;
+ PrintTo(display, &displaySettings);
+ // Store the DisplaySettings in additional information.
+ canvas->drawAnnotation(SkRect::MakeEmpty(), "DisplaySettings",
+ SkData::MakeWithCString(displaySettings.str().c_str()));
+ }
+
+ // Before doing any drawing, let's make sure that we'll start at the origin of the display.
+ // Some displays don't start at 0,0 for example when we're mirroring the screen. Also, virtual
+ // displays might have different scaling when compared to the physical screen.
+
+ canvas->clipRect(getSkRect(display.physicalDisplay));
+ canvas->translate(display.physicalDisplay.left, display.physicalDisplay.top);
+
+ const auto clipWidth = display.clip.width();
+ const auto clipHeight = display.clip.height();
+ auto rotatedClipWidth = clipWidth;
+ auto rotatedClipHeight = clipHeight;
+ // Scale is contingent on the rotation result.
+ if (display.orientation & ui::Transform::ROT_90) {
+ std::swap(rotatedClipWidth, rotatedClipHeight);
+ }
+ const auto scaleX = static_cast<SkScalar>(display.physicalDisplay.width()) /
+ static_cast<SkScalar>(rotatedClipWidth);
+ const auto scaleY = static_cast<SkScalar>(display.physicalDisplay.height()) /
+ static_cast<SkScalar>(rotatedClipHeight);
+ canvas->scale(scaleX, scaleY);
+
+ // Canvas rotation is done by centering the clip window at the origin, rotating, translating
+ // back so that the top left corner of the clip is at (0, 0).
+ canvas->translate(rotatedClipWidth / 2, rotatedClipHeight / 2);
+ canvas->rotate(toDegrees(display.orientation));
+ canvas->translate(-clipWidth / 2, -clipHeight / 2);
+ canvas->translate(-display.clip.left, -display.clip.top);
+}
+
+class AutoSaveRestore {
+public:
+ AutoSaveRestore(SkCanvas* canvas) : mCanvas(canvas) { mSaveCount = canvas->save(); }
+ ~AutoSaveRestore() { restore(); }
+ void replace(SkCanvas* canvas) {
+ mCanvas = canvas;
+ mSaveCount = canvas->save();
+ }
+ void restore() {
+ if (mCanvas) {
+ mCanvas->restoreToCount(mSaveCount);
+ mCanvas = nullptr;
+ }
+ }
+
+private:
+ SkCanvas* mCanvas;
+ int mSaveCount;
+};
+
+status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
+ const std::vector<const LayerSettings*>& layers,
+ const std::shared_ptr<ExternalTexture>& buffer,
+ const bool /*useFramebufferCache*/,
+ base::unique_fd&& bufferFence, base::unique_fd* drawFence) {
+ ATRACE_NAME("SkiaGL::drawLayers");
+
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ if (layers.empty()) {
+ ALOGV("Drawing empty layer stack");
+ return NO_ERROR;
+ }
+
+ if (bufferFence.get() >= 0) {
+ // Duplicate the fence for passing to waitFence.
+ base::unique_fd bufferFenceDup(dup(bufferFence.get()));
+ if (bufferFenceDup < 0 || !waitFence(std::move(bufferFenceDup))) {
+ ATRACE_NAME("Waiting before draw");
+ sync_wait(bufferFence.get(), -1);
+ }
+ }
+ if (buffer == nullptr) {
+ ALOGE("No output buffer provided. Aborting GPU composition.");
+ return BAD_VALUE;
+ }
+
+ validateOutputBufferUsage(buffer->getBuffer());
+
+ auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
+ auto& cache = mInProtectedContext ? mProtectedTextureCache : mTextureCache;
+
+ std::shared_ptr<AutoBackendTexture::LocalRef> surfaceTextureRef;
+ if (const auto& it = cache.find(buffer->getBuffer()->getId()); it != cache.end()) {
+ surfaceTextureRef = it->second;
+ } else {
+ surfaceTextureRef =
+ std::make_shared<AutoBackendTexture::LocalRef>(grContext.get(),
+ buffer->getBuffer()
+ ->toAHardwareBuffer(),
+ true);
+ }
+
+ const ui::Dataspace dstDataspace =
+ mUseColorManagement ? display.outputDataspace : ui::Dataspace::UNKNOWN;
+ sk_sp<SkSurface> dstSurface =
+ surfaceTextureRef->getOrCreateSurface(dstDataspace, grContext.get());
+
+ SkCanvas* dstCanvas = mCapture->tryCapture(dstSurface.get());
+ if (dstCanvas == nullptr) {
+ ALOGE("Cannot acquire canvas from Skia.");
+ return BAD_VALUE;
+ }
+
+ // Find if any layers have requested blur, we'll use that info to decide when to render to an
+ // offscreen buffer and when to render to the native buffer.
+ sk_sp<SkSurface> activeSurface(dstSurface);
+ SkCanvas* canvas = dstCanvas;
+ SkiaCapture::OffscreenState offscreenCaptureState;
+ const LayerSettings* blurCompositionLayer = nullptr;
+ if (mBlurFilter) {
+ bool requiresCompositionLayer = false;
+ for (const auto& layer : layers) {
+ if (layer->backgroundBlurRadius > 0 &&
+ layer->backgroundBlurRadius < BlurFilter::kMaxCrossFadeRadius) {
+ requiresCompositionLayer = true;
+ }
+ for (auto region : layer->blurRegions) {
+ if (region.blurRadius < BlurFilter::kMaxCrossFadeRadius) {
+ requiresCompositionLayer = true;
+ }
+ }
+ if (requiresCompositionLayer) {
+ activeSurface = dstSurface->makeSurface(dstSurface->imageInfo());
+ canvas = mCapture->tryOffscreenCapture(activeSurface.get(), &offscreenCaptureState);
+ blurCompositionLayer = layer;
+ break;
+ }
+ }
+ }
+
+ AutoSaveRestore surfaceAutoSaveRestore(canvas);
+ // Clear the entire canvas with a transparent black to prevent ghost images.
+ canvas->clear(SK_ColorTRANSPARENT);
+ initCanvas(canvas, display);
+
+ // TODO: clearRegion was required for SurfaceView when a buffer is not yet available but the
+ // view is still on-screen. The clear region could be re-specified as a black color layer,
+ // however.
+ if (!display.clearRegion.isEmpty()) {
+ ATRACE_NAME("ClearRegion");
+ size_t numRects = 0;
+ Rect const* rects = display.clearRegion.getArray(&numRects);
+ SkIRect skRects[numRects];
+ for (int i = 0; i < numRects; ++i) {
+ skRects[i] =
+ SkIRect::MakeLTRB(rects[i].left, rects[i].top, rects[i].right, rects[i].bottom);
+ }
+ SkRegion clearRegion;
+ SkPaint paint;
+ sk_sp<SkShader> shader =
+ SkShaders::Color(SkColor4f{.fR = 0., .fG = 0., .fB = 0., .fA = 1.0},
+ toSkColorSpace(dstDataspace));
+ paint.setShader(shader);
+ clearRegion.setRects(skRects, numRects);
+ canvas->drawRegion(clearRegion, paint);
+ }
+
+ // setup color filter if necessary
+ sk_sp<SkColorFilter> displayColorTransform;
+ if (display.colorTransform != mat4()) {
+ displayColorTransform = SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform));
+ }
+
+ for (const auto& layer : layers) {
+ ATRACE_NAME("DrawLayer");
+
+ if (kPrintLayerSettings) {
+ std::stringstream ls;
+ PrintTo(*layer, &ls);
+ auto debugs = ls.str();
+ int pos = 0;
+ while (pos < debugs.size()) {
+ ALOGD("cache_debug %s", debugs.substr(pos, 1000).c_str());
+ pos += 1000;
+ }
+ }
+
+ sk_sp<SkImage> blurInput;
+ if (blurCompositionLayer == layer) {
+ LOG_ALWAYS_FATAL_IF(activeSurface == dstSurface);
+ LOG_ALWAYS_FATAL_IF(canvas == dstCanvas);
+
+ // save a snapshot of the activeSurface to use as input to the blur shaders
+ blurInput = activeSurface->makeImageSnapshot();
+
+ // TODO we could skip this step if we know the blur will cover the entire image
+ // blit the offscreen framebuffer into the destination AHB
+ SkPaint paint;
+ paint.setBlendMode(SkBlendMode::kSrc);
+ if (CC_UNLIKELY(mCapture->isCaptureRunning())) {
+ uint64_t id = mCapture->endOffscreenCapture(&offscreenCaptureState);
+ dstCanvas->drawAnnotation(SkRect::Make(dstCanvas->imageInfo().dimensions()),
+ String8::format("SurfaceID|%" PRId64, id).c_str(),
+ nullptr);
+ dstCanvas->drawImage(blurInput, 0, 0, SkSamplingOptions(), &paint);
+ } else {
+ activeSurface->draw(dstCanvas, 0, 0, SkSamplingOptions(), &paint);
+ }
+
+ // assign dstCanvas to canvas and ensure that the canvas state is up to date
+ canvas = dstCanvas;
+ surfaceAutoSaveRestore.replace(canvas);
+ initCanvas(canvas, display);
+
+ LOG_ALWAYS_FATAL_IF(activeSurface->getCanvas()->getSaveCount() !=
+ dstSurface->getCanvas()->getSaveCount());
+ LOG_ALWAYS_FATAL_IF(activeSurface->getCanvas()->getTotalMatrix() !=
+ dstSurface->getCanvas()->getTotalMatrix());
+
+ // assign dstSurface to activeSurface
+ activeSurface = dstSurface;
+ }
+
+ SkAutoCanvasRestore layerAutoSaveRestore(canvas, true);
+ if (CC_UNLIKELY(mCapture->isCaptureRunning())) {
+ // Record the name of the layer if the capture is running.
+ std::stringstream layerSettings;
+ PrintTo(*layer, &layerSettings);
+ // Store the LayerSettings in additional information.
+ canvas->drawAnnotation(SkRect::MakeEmpty(), layer->name.c_str(),
+ SkData::MakeWithCString(layerSettings.str().c_str()));
+ }
+ // Layers have a local transform that should be applied to them
+ canvas->concat(getSkM44(layer->geometry.positionTransform).asM33());
+
+ const auto bounds = getSkRect(layer->geometry.boundaries);
+ if (mBlurFilter && layerHasBlur(layer)) {
+ std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs;
+
+ // if multiple layers have blur, then we need to take a snapshot now because
+ // only the lowest layer will have blurImage populated earlier
+ if (!blurInput) {
+ blurInput = activeSurface->makeImageSnapshot();
+ }
+ // rect to be blurred in the coordinate space of blurInput
+ const auto blurRect = canvas->getTotalMatrix().mapRect(bounds);
+
+ // TODO(b/182216890): Filter out empty layers earlier
+ if (blurRect.width() > 0 && blurRect.height() > 0) {
+ if (layer->backgroundBlurRadius > 0) {
+ ATRACE_NAME("BackgroundBlur");
+ auto blurredImage =
+ mBlurFilter->generate(grContext.get(), layer->backgroundBlurRadius,
+ blurInput, blurRect);
+
+ cachedBlurs[layer->backgroundBlurRadius] = blurredImage;
+
+ mBlurFilter->drawBlurRegion(canvas, getBlurRegion(layer), blurRect,
+ blurredImage, blurInput);
+ }
+ for (auto region : layer->blurRegions) {
+ if (cachedBlurs[region.blurRadius] == nullptr) {
+ ATRACE_NAME("BlurRegion");
+ cachedBlurs[region.blurRadius] =
+ mBlurFilter->generate(grContext.get(), region.blurRadius, blurInput,
+ blurRect);
+ }
+
+ mBlurFilter->drawBlurRegion(canvas, region, blurRect,
+ cachedBlurs[region.blurRadius], blurInput);
+ }
+ }
+ }
+
+ // Shadows are assumed to live only on their own layer - it's not valid
+ // to draw the boundary rectangles when there is already a caster shadow
+ // TODO(b/175915334): consider relaxing this restriction to enable more flexible
+ // composition - using a well-defined invalid color is long-term less error-prone.
+ if (layer->shadow.length > 0) {
+ const auto rect = layer->geometry.roundedCornersRadius > 0
+ ? getSkRect(layer->geometry.roundedCornersCrop)
+ : bounds;
+ // This would require a new parameter/flag to SkShadowUtils::DrawShadow
+ LOG_ALWAYS_FATAL_IF(layer->disableBlending, "Cannot disableBlending with a shadow");
+ drawShadow(canvas, rect, layer->geometry.roundedCornersRadius, layer->shadow);
+ continue;
+ }
+
+ const bool requiresLinearEffect = layer->colorTransform != mat4() ||
+ (mUseColorManagement &&
+ needsToneMapping(layer->sourceDataspace, display.outputDataspace));
+
+ // quick abort from drawing the remaining portion of the layer
+ if (layer->alpha == 0 && !requiresLinearEffect && !layer->disableBlending &&
+ (!displayColorTransform || displayColorTransform->isAlphaUnchanged())) {
+ continue;
+ }
+
+ // If we need to map to linear space or color management is disabled, then mark the source
+ // image with the same colorspace as the destination surface so that Skia's color
+ // management is a no-op.
+ const ui::Dataspace layerDataspace = (!mUseColorManagement || requiresLinearEffect)
+ ? dstDataspace
+ : layer->sourceDataspace;
+
+ SkPaint paint;
+ if (layer->source.buffer.buffer) {
+ ATRACE_NAME("DrawImage");
+ validateInputBufferUsage(layer->source.buffer.buffer->getBuffer());
+ const auto& item = layer->source.buffer;
+ std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef = nullptr;
+
+ if (const auto& iter = cache.find(item.buffer->getBuffer()->getId());
+ iter != cache.end()) {
+ imageTextureRef = iter->second;
+ } else {
+ // If we didn't find the image in the cache, then create a local ref but don't cache
+ // it. If we're using skia, we're guaranteed to run on a dedicated GPU thread so if
+ // we didn't find anything in the cache then we intentionally did not cache this
+ // buffer's resources.
+ imageTextureRef = std::make_shared<
+ AutoBackendTexture::LocalRef>(grContext.get(),
+ item.buffer->getBuffer()->toAHardwareBuffer(),
+ false);
+ }
+
+ sk_sp<SkImage> image =
+ imageTextureRef->makeImage(layerDataspace,
+ item.usePremultipliedAlpha ? kPremul_SkAlphaType
+ : kUnpremul_SkAlphaType,
+ grContext.get());
+
+ auto texMatrix = getSkM44(item.textureTransform).asM33();
+ // textureTansform was intended to be passed directly into a shader, so when
+ // building the total matrix with the textureTransform we need to first
+ // normalize it, then apply the textureTransform, then scale back up.
+ texMatrix.preScale(1.0f / bounds.width(), 1.0f / bounds.height());
+ texMatrix.postScale(image->width(), image->height());
+
+ SkMatrix matrix;
+ if (!texMatrix.invert(&matrix)) {
+ matrix = texMatrix;
+ }
+ // The shader does not respect the translation, so we add it to the texture
+ // transform for the SkImage. This will make sure that the correct layer contents
+ // are drawn in the correct part of the screen.
+ matrix.postTranslate(layer->geometry.boundaries.left, layer->geometry.boundaries.top);
+
+ sk_sp<SkShader> shader;
+
+ if (layer->source.buffer.useTextureFiltering) {
+ shader = image->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
+ SkSamplingOptions(
+ {SkFilterMode::kLinear, SkMipmapMode::kNone}),
+ &matrix);
+ } else {
+ shader = image->makeShader(SkSamplingOptions(), matrix);
+ }
+
+ // Handle opaque images - it's a little nonstandard how we do this.
+ // Fundamentally we need to support SurfaceControl.Builder#setOpaque:
+ // https://developer.android.com/reference/android/view/SurfaceControl.Builder#setOpaque(boolean)
+ // The important language is that when isOpaque is set, opacity is not sampled from the
+ // alpha channel, but blending may still be supported on a transaction via setAlpha. So,
+ // here's the conundrum:
+ // 1. We can't force the SkImage alpha type to kOpaque_SkAlphaType, because it's treated
+ // as an internal hint - composition is undefined when there are alpha bits present.
+ // 2. We can try to lie about the pixel layout, but that only works for RGBA8888
+ // buffers, i.e., treating them as RGBx8888 instead. But we can't do the same for
+ // RGBA1010102 because RGBx1010102 is not supported as a pixel layout for SkImages. It's
+ // also not clear what to use for F16 either, and lying about the pixel layout is a bit
+ // of a hack anyways.
+ // 3. We can't change the blendmode to src, because while this satisfies the requirement
+ // for ignoring the alpha channel, it doesn't quite satisfy the blending requirement
+ // because src always clobbers the destination content.
+ //
+ // So, what we do here instead is an additive blend mode where we compose the input
+ // image with a solid black. This might need to be reassess if this does not support
+ // FP16 incredibly well, but FP16 end-to-end isn't well supported anyway at the moment.
+ if (item.isOpaque) {
+ shader = SkShaders::Blend(SkBlendMode::kPlus, shader,
+ SkShaders::Color(SkColors::kBlack,
+ toSkColorSpace(layerDataspace)));
+ }
+
+ paint.setShader(createRuntimeEffectShader(shader, layer, display,
+ !item.isOpaque && item.usePremultipliedAlpha,
+ requiresLinearEffect));
+ paint.setAlphaf(layer->alpha);
+ } else {
+ ATRACE_NAME("DrawColor");
+ const auto color = layer->source.solidColor;
+ sk_sp<SkShader> shader = SkShaders::Color(SkColor4f{.fR = color.r,
+ .fG = color.g,
+ .fB = color.b,
+ .fA = layer->alpha},
+ toSkColorSpace(layerDataspace));
+ paint.setShader(createRuntimeEffectShader(shader, layer, display,
+ /* undoPremultipliedAlpha */ false,
+ requiresLinearEffect));
+ }
+
+ if (layer->disableBlending) {
+ paint.setBlendMode(SkBlendMode::kSrc);
+ }
+
+ paint.setColorFilter(displayColorTransform);
+
+ if (layer->geometry.roundedCornersRadius > 0) {
+ paint.setAntiAlias(true);
+ canvas->drawRRect(getRoundedRect(layer), paint);
+ } else {
+ canvas->drawRect(bounds, paint);
+ }
+ if (kFlushAfterEveryLayer) {
+ ATRACE_NAME("flush surface");
+ activeSurface->flush();
+ }
+ }
+ surfaceAutoSaveRestore.restore();
+ mCapture->endCapture();
+ {
+ ATRACE_NAME("flush surface");
+ LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface);
+ activeSurface->flush();
+ }
+
+ if (drawFence != nullptr) {
+ *drawFence = flush();
+ }
+
+ // If flush failed or we don't support native fences, we need to force the
+ // gl command stream to be executed.
+ bool requireSync = drawFence == nullptr || drawFence->get() < 0;
+ if (requireSync) {
+ ATRACE_BEGIN("Submit(sync=true)");
+ } else {
+ ATRACE_BEGIN("Submit(sync=false)");
+ }
+ bool success = grContext->submit(requireSync);
+ ATRACE_END();
+ if (!success) {
+ ALOGE("Failed to flush RenderEngine commands");
+ // Chances are, something illegal happened (either the caller passed
+ // us bad parameters, or we messed up our shader generation).
+ return INVALID_OPERATION;
+ }
+
+ // checkErrors();
+ return NO_ERROR;
+}
+
+inline SkRect SkiaGLRenderEngine::getSkRect(const FloatRect& rect) {
+ return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
+}
+
+inline SkRect SkiaGLRenderEngine::getSkRect(const Rect& rect) {
+ return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
+}
+
+inline SkRRect SkiaGLRenderEngine::getRoundedRect(const LayerSettings* layer) {
+ const auto rect = getSkRect(layer->geometry.roundedCornersCrop);
+ const auto cornerRadius = layer->geometry.roundedCornersRadius;
+ return SkRRect::MakeRectXY(rect, cornerRadius, cornerRadius);
+}
+
+inline BlurRegion SkiaGLRenderEngine::getBlurRegion(const LayerSettings* layer) {
+ const auto rect = getSkRect(layer->geometry.boundaries);
+ const auto cornersRadius = layer->geometry.roundedCornersRadius;
+ return BlurRegion{.blurRadius = static_cast<uint32_t>(layer->backgroundBlurRadius),
+ .cornerRadiusTL = cornersRadius,
+ .cornerRadiusTR = cornersRadius,
+ .cornerRadiusBL = cornersRadius,
+ .cornerRadiusBR = cornersRadius,
+ .alpha = 1,
+ .left = static_cast<int>(rect.fLeft),
+ .top = static_cast<int>(rect.fTop),
+ .right = static_cast<int>(rect.fRight),
+ .bottom = static_cast<int>(rect.fBottom)};
+}
+
+inline bool SkiaGLRenderEngine::layerHasBlur(const LayerSettings* layer) {
+ return layer->backgroundBlurRadius > 0 || layer->blurRegions.size();
+}
+
+inline SkColor SkiaGLRenderEngine::getSkColor(const vec4& color) {
+ return SkColorSetARGB(color.a * 255, color.r * 255, color.g * 255, color.b * 255);
+}
+
+inline SkM44 SkiaGLRenderEngine::getSkM44(const mat4& matrix) {
+ return SkM44(matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0],
+ matrix[0][1], matrix[1][1], matrix[2][1], matrix[3][1],
+ matrix[0][2], matrix[1][2], matrix[2][2], matrix[3][2],
+ matrix[0][3], matrix[1][3], matrix[2][3], matrix[3][3]);
+}
+
+inline SkPoint3 SkiaGLRenderEngine::getSkPoint3(const vec3& vector) {
+ return SkPoint3::Make(vector.x, vector.y, vector.z);
+}
+
+size_t SkiaGLRenderEngine::getMaxTextureSize() const {
+ return mGrContext->maxTextureSize();
+}
+
+size_t SkiaGLRenderEngine::getMaxViewportDims() const {
+ return mGrContext->maxRenderTargetSize();
+}
+
+void SkiaGLRenderEngine::drawShadow(SkCanvas* canvas, const SkRect& casterRect, float cornerRadius,
+ const ShadowSettings& settings) {
+ ATRACE_CALL();
+ const float casterZ = settings.length / 2.0f;
+ const auto shadowShape = cornerRadius > 0
+ ? SkPath::RRect(SkRRect::MakeRectXY(casterRect, cornerRadius, cornerRadius))
+ : SkPath::Rect(casterRect);
+ const auto flags =
+ settings.casterIsTranslucent ? kTransparentOccluder_ShadowFlag : kNone_ShadowFlag;
+
+ SkShadowUtils::DrawShadow(canvas, shadowShape, SkPoint3::Make(0, 0, casterZ),
+ getSkPoint3(settings.lightPos), settings.lightRadius,
+ getSkColor(settings.ambientColor), getSkColor(settings.spotColor),
+ flags);
+}
+
+EGLContext SkiaGLRenderEngine::createEglContext(EGLDisplay display, EGLConfig config,
+ EGLContext shareContext,
+ std::optional<ContextPriority> contextPriority,
+ Protection protection) {
+ EGLint renderableType = 0;
+ if (config == EGL_NO_CONFIG_KHR) {
+ renderableType = EGL_OPENGL_ES3_BIT;
+ } else if (!eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType)) {
+ LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE");
+ }
+ EGLint contextClientVersion = 0;
+ if (renderableType & EGL_OPENGL_ES3_BIT) {
+ contextClientVersion = 3;
+ } else if (renderableType & EGL_OPENGL_ES2_BIT) {
+ contextClientVersion = 2;
+ } else if (renderableType & EGL_OPENGL_ES_BIT) {
+ contextClientVersion = 1;
+ } else {
+ LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs");
+ }
+
+ std::vector<EGLint> contextAttributes;
+ contextAttributes.reserve(7);
+ contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
+ contextAttributes.push_back(contextClientVersion);
+ if (contextPriority) {
+ contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
+ switch (*contextPriority) {
+ case ContextPriority::REALTIME:
+ contextAttributes.push_back(EGL_CONTEXT_PRIORITY_REALTIME_NV);
+ break;
+ case ContextPriority::MEDIUM:
+ contextAttributes.push_back(EGL_CONTEXT_PRIORITY_MEDIUM_IMG);
+ break;
+ case ContextPriority::LOW:
+ contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LOW_IMG);
+ break;
+ case ContextPriority::HIGH:
+ default:
+ contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
+ break;
+ }
+ }
+ if (protection == Protection::PROTECTED) {
+ contextAttributes.push_back(EGL_PROTECTED_CONTENT_EXT);
+ contextAttributes.push_back(EGL_TRUE);
+ }
+ contextAttributes.push_back(EGL_NONE);
+
+ EGLContext context = eglCreateContext(display, config, shareContext, contextAttributes.data());
+
+ if (contextClientVersion == 3 && context == EGL_NO_CONTEXT) {
+ // eglGetConfigAttrib indicated we can create GLES 3 context, but we failed, thus
+ // EGL_NO_CONTEXT so that we can abort.
+ if (config != EGL_NO_CONFIG_KHR) {
+ return context;
+ }
+ // If |config| is EGL_NO_CONFIG_KHR, we speculatively try to create GLES 3 context, so we
+ // should try to fall back to GLES 2.
+ contextAttributes[1] = 2;
+ context = eglCreateContext(display, config, shareContext, contextAttributes.data());
+ }
+
+ return context;
+}
+
+std::optional<RenderEngine::ContextPriority> SkiaGLRenderEngine::createContextPriority(
+ const RenderEngineCreationArgs& args) {
+ if (!gl::GLExtensions::getInstance().hasContextPriority()) {
+ return std::nullopt;
+ }
+
+ switch (args.contextPriority) {
+ case RenderEngine::ContextPriority::REALTIME:
+ if (gl::GLExtensions::getInstance().hasRealtimePriority()) {
+ return RenderEngine::ContextPriority::REALTIME;
+ } else {
+ ALOGI("Realtime priority unsupported, degrading gracefully to high priority");
+ return RenderEngine::ContextPriority::HIGH;
+ }
+ case RenderEngine::ContextPriority::HIGH:
+ case RenderEngine::ContextPriority::MEDIUM:
+ case RenderEngine::ContextPriority::LOW:
+ return args.contextPriority;
+ default:
+ return std::nullopt;
+ }
+}
+
+EGLSurface SkiaGLRenderEngine::createPlaceholderEglPbufferSurface(EGLDisplay display,
+ EGLConfig config, int hwcFormat,
+ Protection protection) {
+ EGLConfig placeholderConfig = config;
+ if (placeholderConfig == EGL_NO_CONFIG_KHR) {
+ placeholderConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
+ }
+ std::vector<EGLint> attributes;
+ attributes.reserve(7);
+ attributes.push_back(EGL_WIDTH);
+ attributes.push_back(1);
+ attributes.push_back(EGL_HEIGHT);
+ attributes.push_back(1);
+ if (protection == Protection::PROTECTED) {
+ attributes.push_back(EGL_PROTECTED_CONTENT_EXT);
+ attributes.push_back(EGL_TRUE);
+ }
+ attributes.push_back(EGL_NONE);
+
+ return eglCreatePbufferSurface(display, placeholderConfig, attributes.data());
+}
+
+int SkiaGLRenderEngine::getContextPriority() {
+ int value;
+ eglQueryContext(mEGLDisplay, mEGLContext, EGL_CONTEXT_PRIORITY_LEVEL_IMG, &value);
+ return value;
+}
+
+void SkiaGLRenderEngine::onPrimaryDisplaySizeChanged(ui::Size size) {
+ // This cache multiplier was selected based on review of cache sizes relative
+ // to the screen resolution. Looking at the worst case memory needed by blur (~1.5x),
+ // shadows (~1x), and general data structures (e.g. vertex buffers) we selected this as a
+ // conservative default based on that analysis.
+ const float SURFACE_SIZE_MULTIPLIER = 3.5f * bytesPerPixel(mDefaultPixelFormat);
+ const int maxResourceBytes = size.width * size.height * SURFACE_SIZE_MULTIPLIER;
+
+ // start by resizing the current context
+ auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
+ grContext->setResourceCacheLimit(maxResourceBytes);
+
+ // if it is possible to switch contexts then we will resize the other context
+ if (useProtectedContext(!mInProtectedContext)) {
+ grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
+ grContext->setResourceCacheLimit(maxResourceBytes);
+ // reset back to the initial context that was active when this method was called
+ useProtectedContext(!mInProtectedContext);
+ }
+}
+
+void SkiaGLRenderEngine::dump(std::string& result) {
+ const gl::GLExtensions& extensions = gl::GLExtensions::getInstance();
+
+ StringAppendF(&result, "\n ------------RE-----------------\n");
+ StringAppendF(&result, "EGL implementation : %s\n", extensions.getEGLVersion());
+ StringAppendF(&result, "%s\n", extensions.getEGLExtensions());
+ StringAppendF(&result, "GLES: %s, %s, %s\n", extensions.getVendor(), extensions.getRenderer(),
+ extensions.getVersion());
+ StringAppendF(&result, "%s\n", extensions.getExtensions());
+ StringAppendF(&result, "RenderEngine supports protected context: %d\n",
+ supportsProtectedContent());
+ StringAppendF(&result, "RenderEngine is in protected context: %d\n", mInProtectedContext);
+ StringAppendF(&result, "RenderEngine shaders cached since last dump/primeCache: %d\n",
+ mSkSLCacheMonitor.shadersCachedSinceLastCall());
+
+ std::vector<ResourcePair> cpuResourceMap = {
+ {"skia/sk_resource_cache/bitmap_", "Bitmaps"},
+ {"skia/sk_resource_cache/rrect-blur_", "Masks"},
+ {"skia/sk_resource_cache/rects-blur_", "Masks"},
+ {"skia/sk_resource_cache/tessellated", "Shadows"},
+ {"skia", "Other"},
+ };
+ SkiaMemoryReporter cpuReporter(cpuResourceMap, false);
+ SkGraphics::DumpMemoryStatistics(&cpuReporter);
+ StringAppendF(&result, "Skia CPU Caches: ");
+ cpuReporter.logTotals(result);
+ cpuReporter.logOutput(result);
+
+ {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+
+ std::vector<ResourcePair> gpuResourceMap = {
+ {"texture_renderbuffer", "Texture/RenderBuffer"},
+ {"texture", "Texture"},
+ {"gr_text_blob_cache", "Text"},
+ {"skia", "Other"},
+ };
+ SkiaMemoryReporter gpuReporter(gpuResourceMap, true);
+ mGrContext->dumpMemoryStatistics(&gpuReporter);
+ StringAppendF(&result, "Skia's GPU Caches: ");
+ gpuReporter.logTotals(result);
+ gpuReporter.logOutput(result);
+ StringAppendF(&result, "Skia's Wrapped Objects:\n");
+ gpuReporter.logOutput(result, true);
+
+ StringAppendF(&result, "RenderEngine tracked buffers: %zu\n",
+ mGraphicBufferExternalRefs.size());
+ StringAppendF(&result, "Dumping buffer ids...\n");
+ for (const auto& [id, refCounts] : mGraphicBufferExternalRefs) {
+ StringAppendF(&result, "- 0x%" PRIx64 " - %d refs \n", id, refCounts);
+ }
+ StringAppendF(&result, "RenderEngine AHB/BackendTexture cache size: %zu\n",
+ mTextureCache.size());
+ StringAppendF(&result, "Dumping buffer ids...\n");
+ // TODO(178539829): It would be nice to know which layer these are coming from and what
+ // the texture sizes are.
+ for (const auto& [id, unused] : mTextureCache) {
+ StringAppendF(&result, "- 0x%" PRIx64 "\n", id);
+ }
+ StringAppendF(&result, "\n");
+
+ SkiaMemoryReporter gpuProtectedReporter(gpuResourceMap, true);
+ if (mProtectedGrContext) {
+ mProtectedGrContext->dumpMemoryStatistics(&gpuProtectedReporter);
+ }
+ StringAppendF(&result, "Skia's GPU Protected Caches: ");
+ gpuProtectedReporter.logTotals(result);
+ gpuProtectedReporter.logOutput(result);
+ StringAppendF(&result, "Skia's Protected Wrapped Objects:\n");
+ gpuProtectedReporter.logOutput(result, true);
+
+ StringAppendF(&result, "RenderEngine protected AHB/BackendTexture cache size: %zu\n",
+ mProtectedTextureCache.size());
+ StringAppendF(&result, "Dumping buffer ids...\n");
+ for (const auto& [id, unused] : mProtectedTextureCache) {
+ StringAppendF(&result, "- 0x%" PRIx64 "\n", id);
+ }
+ StringAppendF(&result, "\n");
+ StringAppendF(&result, "RenderEngine runtime effects: %zu\n", mRuntimeEffects.size());
+ for (const auto& [linearEffect, unused] : mRuntimeEffects) {
+ StringAppendF(&result, "- inputDataspace: %s\n",
+ dataspaceDetails(
+ static_cast<android_dataspace>(linearEffect.inputDataspace))
+ .c_str());
+ StringAppendF(&result, "- outputDataspace: %s\n",
+ dataspaceDetails(
+ static_cast<android_dataspace>(linearEffect.outputDataspace))
+ .c_str());
+ StringAppendF(&result, "undoPremultipliedAlpha: %s\n",
+ linearEffect.undoPremultipliedAlpha ? "true" : "false");
+ }
+ }
+ StringAppendF(&result, "\n");
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
new file mode 100644
index 0000000..e71c560
--- /dev/null
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#ifndef SF_SKIAGLRENDERENGINE_H_
+#define SF_SKIAGLRENDERENGINE_H_
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GrDirectContext.h>
+#include <SkSurface.h>
+#include <android-base/thread_annotations.h>
+#include <renderengine/ExternalTexture.h>
+#include <renderengine/RenderEngine.h>
+#include <sys/types.h>
+
+#include <mutex>
+#include <unordered_map>
+
+#include "AutoBackendTexture.h"
+#include "EGL/egl.h"
+#include "GrContextOptions.h"
+#include "SkImageInfo.h"
+#include "SkiaRenderEngine.h"
+#include "android-base/macros.h"
+#include "debug/SkiaCapture.h"
+#include "filters/BlurFilter.h"
+#include "filters/LinearEffect.h"
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+class SkiaGLRenderEngine : public skia::SkiaRenderEngine {
+public:
+ static std::unique_ptr<SkiaGLRenderEngine> create(const RenderEngineCreationArgs& args);
+ SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLContext ctxt,
+ EGLSurface placeholder, EGLContext protectedContext,
+ EGLSurface protectedPlaceholder);
+ ~SkiaGLRenderEngine() override EXCLUDES(mRenderingMutex);
+
+ void primeCache() override;
+ status_t drawLayers(const DisplaySettings& display,
+ const std::vector<const LayerSettings*>& layers,
+ const std::shared_ptr<ExternalTexture>& buffer,
+ const bool useFramebufferCache, base::unique_fd&& bufferFence,
+ base::unique_fd* drawFence) override;
+ void cleanFramebufferCache() override {}
+ int getContextPriority() override;
+ bool isProtected() const override { return mInProtectedContext; }
+ bool supportsProtectedContent() const override;
+ bool useProtectedContext(bool useProtectedContext) override;
+ bool supportsBackgroundBlur() override { return mBlurFilter != nullptr; }
+ void assertShadersCompiled(int numShaders) override;
+ void onPrimaryDisplaySizeChanged(ui::Size size) override;
+ int reportShadersCompiled() override;
+
+protected:
+ void dump(std::string& result) override;
+ size_t getMaxTextureSize() const override;
+ size_t getMaxViewportDims() const override;
+ void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override;
+ void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
+
+private:
+ static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
+ static EGLContext createEglContext(EGLDisplay display, EGLConfig config,
+ EGLContext shareContext,
+ std::optional<ContextPriority> contextPriority,
+ Protection protection);
+ static std::optional<RenderEngine::ContextPriority> createContextPriority(
+ const RenderEngineCreationArgs& args);
+ static EGLSurface createPlaceholderEglPbufferSurface(EGLDisplay display, EGLConfig config,
+ int hwcFormat, Protection protection);
+ inline SkRect getSkRect(const FloatRect& layer);
+ inline SkRect getSkRect(const Rect& layer);
+ inline SkRRect getRoundedRect(const LayerSettings* layer);
+ inline BlurRegion getBlurRegion(const LayerSettings* layer);
+ inline bool layerHasBlur(const LayerSettings* layer);
+ inline SkColor getSkColor(const vec4& color);
+ inline SkM44 getSkM44(const mat4& matrix);
+ inline SkPoint3 getSkPoint3(const vec3& vector);
+
+ base::unique_fd flush();
+ bool waitFence(base::unique_fd fenceFd);
+ void initCanvas(SkCanvas* canvas, const DisplaySettings& display);
+ void drawShadow(SkCanvas* canvas, const SkRect& casterRect, float casterCornerRadius,
+ const ShadowSettings& shadowSettings);
+ // If requiresLinearEffect is true or the layer has a stretchEffect a new shader is returned.
+ // Otherwise it returns the input shader.
+ sk_sp<SkShader> createRuntimeEffectShader(sk_sp<SkShader> shader, const LayerSettings* layer,
+ const DisplaySettings& display,
+ bool undoPremultipliedAlpha,
+ bool requiresLinearEffect);
+
+ EGLDisplay mEGLDisplay;
+ EGLContext mEGLContext;
+ EGLSurface mPlaceholderSurface;
+ EGLContext mProtectedEGLContext;
+ EGLSurface mProtectedPlaceholderSurface;
+ BlurFilter* mBlurFilter = nullptr;
+
+ const PixelFormat mDefaultPixelFormat;
+ const bool mUseColorManagement;
+
+ // Number of external holders of ExternalTexture references, per GraphicBuffer ID.
+ std::unordered_map<uint64_t, int32_t> mGraphicBufferExternalRefs GUARDED_BY(mRenderingMutex);
+ // Cache of GL textures that we'll store per GraphicBuffer ID, sliced by GPU context.
+ std::unordered_map<uint64_t, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache
+ GUARDED_BY(mRenderingMutex);
+ std::unordered_map<uint64_t, std::shared_ptr<AutoBackendTexture::LocalRef>>
+ mProtectedTextureCache GUARDED_BY(mRenderingMutex);
+ std::unordered_map<LinearEffect, sk_sp<SkRuntimeEffect>, LinearEffectHasher> mRuntimeEffects;
+ // Mutex guarding rendering operations, so that:
+ // 1. GL operations aren't interleaved, and
+ // 2. Internal state related to rendering that is potentially modified by
+ // multiple threads is guaranteed thread-safe.
+ std::mutex mRenderingMutex;
+
+ sp<Fence> mLastDrawFence;
+
+ // Graphics context used for creating surfaces and submitting commands
+ sk_sp<GrDirectContext> mGrContext;
+ // Same as above, but for protected content (eg. DRM)
+ sk_sp<GrDirectContext> mProtectedGrContext;
+
+ bool mInProtectedContext = false;
+ // Object to capture commands send to Skia.
+ std::unique_ptr<SkiaCapture> mCapture;
+
+ // Implements PersistentCache as a way to monitor what SkSL shaders Skia has
+ // cached.
+ class SkSLCacheMonitor : public GrContextOptions::PersistentCache {
+ public:
+ SkSLCacheMonitor() = default;
+ ~SkSLCacheMonitor() override = default;
+
+ sk_sp<SkData> load(const SkData& key) override;
+
+ void store(const SkData& key, const SkData& data, const SkString& description) override;
+
+ int shadersCachedSinceLastCall() {
+ const int shadersCachedSinceLastCall = mShadersCachedSinceLastCall;
+ mShadersCachedSinceLastCall = 0;
+ return shadersCachedSinceLastCall;
+ }
+
+ private:
+ int mShadersCachedSinceLastCall = 0;
+ };
+
+ SkSLCacheMonitor mSkSLCacheMonitor;
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
+
+#endif /* SF_GLESRENDERENGINE_H_ */
diff --git a/libs/ui/UiConfig.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
similarity index 68%
rename from libs/ui/UiConfig.cpp
rename to libs/renderengine/skia/SkiaRenderEngine.cpp
index 0ac863d..81f0b6f 100644
--- a/libs/ui/UiConfig.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright 2020 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.
@@ -14,15 +14,13 @@
* limitations under the License.
*/
-#include <ui/UiConfig.h>
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
namespace android {
-
-void appendUiConfigString(std::string& configStr) {
- static const char* config =
- " [libui]";
- configStr.append(config);
-}
-
-
-}; // namespace android
+namespace renderengine {
+namespace skia {} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
new file mode 100644
index 0000000..308c5ff
--- /dev/null
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#ifndef SF_SKIARENDERENGINE_H_
+#define SF_SKIARENDERENGINE_H_
+
+#include <renderengine/RenderEngine.h>
+#include <sys/types.h>
+
+namespace android {
+
+namespace renderengine {
+
+class Mesh;
+class Texture;
+
+namespace skia {
+
+class BlurFilter;
+
+// TODO: Put common skia stuff here that can be shared between the GL & Vulkan backends
+// Currently mostly just handles all the no-op / missing APIs
+class SkiaRenderEngine : public RenderEngine {
+public:
+ static std::unique_ptr<SkiaRenderEngine> create(const RenderEngineCreationArgs& args);
+ SkiaRenderEngine(RenderEngineType type) : RenderEngine(type) {}
+ ~SkiaRenderEngine() override {}
+
+ virtual void primeCache() override{};
+ virtual void genTextures(size_t /*count*/, uint32_t* /*names*/) override{};
+ virtual void deleteTextures(size_t /*count*/, uint32_t const* /*names*/) override{};
+ virtual bool isProtected() const override { return false; } // mInProtectedContext; }
+ virtual bool supportsProtectedContent() const override { return false; };
+ virtual bool useProtectedContext(bool /*useProtectedContext*/) override { return false; };
+ virtual status_t drawLayers(const DisplaySettings& /*display*/,
+ const std::vector<const LayerSettings*>& /*layers*/,
+ const std::shared_ptr<ExternalTexture>& /*buffer*/,
+ const bool /*useFramebufferCache*/,
+ base::unique_fd&& /*bufferFence*/,
+ base::unique_fd* /*drawFence*/) override {
+ return 0;
+ };
+ virtual bool cleanupPostRender(CleanupMode) override { return true; };
+ virtual int getContextPriority() override { return 0; }
+ virtual void assertShadersCompiled(int numShaders) {}
+ virtual int reportShadersCompiled() { return 0; }
+
+protected:
+ virtual void mapExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/,
+ bool /*isRenderable*/) override;
+ virtual void unmapExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/) override;
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
+
+#endif /* SF_GLESRENDERENGINE_H_ */
diff --git a/libs/renderengine/skia/debug/CaptureTimer.cpp b/libs/renderengine/skia/debug/CaptureTimer.cpp
new file mode 100644
index 0000000..11bcdb8
--- /dev/null
+++ b/libs/renderengine/skia/debug/CaptureTimer.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2020 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 "CaptureTimer.h"
+
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "CommonPool.h"
+
+#include <thread>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+void CaptureTimer::setTimeout(TimeoutCallback function, std::chrono::milliseconds delay) {
+ this->clear = false;
+ CommonPool::post([=]() {
+ if (this->clear) return;
+ std::this_thread::sleep_for(delay);
+ if (this->clear) return;
+ function();
+ });
+}
+
+void CaptureTimer::stop() {
+ this->clear = true;
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/skia/debug/CaptureTimer.h b/libs/renderengine/skia/debug/CaptureTimer.h
new file mode 100644
index 0000000..a0aa302
--- /dev/null
+++ b/libs/renderengine/skia/debug/CaptureTimer.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <chrono>
+#include <functional>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+/**
+ * Simple timer that times out after a given delay and executes a void
+ * callback function.
+ */
+class CaptureTimer {
+ bool clear = false;
+
+public:
+ using TimeoutCallback = std::function<void()>;
+ // Start the timeout.
+ void setTimeout(TimeoutCallback function, std::chrono::milliseconds delay);
+ // Stop and clean up.
+ void stop();
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/skia/debug/CommonPool.cpp b/libs/renderengine/skia/debug/CommonPool.cpp
new file mode 100644
index 0000000..bf15300
--- /dev/null
+++ b/libs/renderengine/skia/debug/CommonPool.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2020 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 "CommonPool.h"
+
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <sys/resource.h>
+#include <utils/Trace.h>
+
+#include <system/thread_defs.h>
+#include <array>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+CommonPool::CommonPool() {
+ ATRACE_CALL();
+
+ CommonPool* pool = this;
+ // Create 2 workers
+ for (int i = 0; i < THREAD_COUNT; i++) {
+ std::thread worker([pool, i] {
+ {
+ std::array<char, 20> name{"reTask"};
+ snprintf(name.data(), name.size(), "reTask%d", i);
+ auto self = pthread_self();
+ pthread_setname_np(self, name.data());
+ setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_FOREGROUND);
+ }
+ pool->workerLoop();
+ });
+ worker.detach();
+ }
+}
+
+CommonPool& CommonPool::instance() {
+ static CommonPool pool;
+ return pool;
+}
+
+void CommonPool::post(Task&& task) {
+ instance().enqueue(std::move(task));
+}
+
+void CommonPool::enqueue(Task&& task) {
+ std::unique_lock lock(mLock);
+ while (mWorkQueue.size() > QUEUE_SIZE) {
+ lock.unlock();
+ ALOGW("Queue is full: %d, waiting before adding more tasks.", QUEUE_SIZE);
+ usleep(100);
+ lock.lock();
+ }
+ mWorkQueue.push(std::move(task));
+ if (mWaitingThreads == THREAD_COUNT || (mWaitingThreads > 0 && mWorkQueue.size() > 1)) {
+ mCondition.notify_one();
+ }
+}
+
+void CommonPool::workerLoop() {
+ std::unique_lock lock(mLock);
+ while (true) {
+ if (mWorkQueue.size() == 0) {
+ mWaitingThreads++;
+ mCondition.wait(lock);
+ mWaitingThreads--;
+ }
+ // Need to double-check that work is still available now that we have the lock
+ // It may have already been grabbed by a different thread
+ while (mWorkQueue.size() > 0) {
+ auto work = mWorkQueue.front();
+ mWorkQueue.pop();
+ lock.unlock();
+ work();
+ lock.lock();
+ }
+ }
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/skia/debug/CommonPool.h b/libs/renderengine/skia/debug/CommonPool.h
new file mode 100644
index 0000000..7fc3d23
--- /dev/null
+++ b/libs/renderengine/skia/debug/CommonPool.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <log/log.h>
+
+#include <condition_variable>
+#include <functional>
+#include <future>
+#include <mutex>
+#include <queue>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+namespace {
+#define PREVENT_COPY_AND_ASSIGN(Type) \
+private: \
+ Type(const Type&) = delete; \
+ void operator=(const Type&) = delete
+} // namespace
+
+/**
+ * Shamelessly copied from HWUI to execute Skia Capturing on the back thread in
+ * a safe manner.
+ */
+class CommonPool {
+ PREVENT_COPY_AND_ASSIGN(CommonPool);
+
+public:
+ using Task = std::function<void()>;
+ static constexpr auto THREAD_COUNT = 2;
+ static constexpr auto QUEUE_SIZE = 128;
+
+ static void post(Task&& func);
+
+private:
+ static CommonPool& instance();
+
+ CommonPool();
+ ~CommonPool() {}
+
+ void enqueue(Task&&);
+
+ void workerLoop();
+
+ std::mutex mLock;
+ std::condition_variable mCondition;
+ int mWaitingThreads = 0;
+ std::queue<Task> mWorkQueue;
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/debug/README.md b/libs/renderengine/skia/debug/README.md
new file mode 100644
index 0000000..4719e34
--- /dev/null
+++ b/libs/renderengine/skia/debug/README.md
@@ -0,0 +1,17 @@
+This library turns on recording of skia commands in SkiaGL version of the RE.
+The debug property defines number of milliseconds for the recording to take place.
+A non zero value turns on the recording. The recording will stop after MS specified.
+To reset the recording, set the capture_skia_ms flag to a new time. When recording
+is finished, the capture_skia_ms flag will be set to 0 to avoid circular recording.
+
+In order to allow the process to write files onto the device run:
+adb shell setenforce 0
+
+To start recording run:
+adb shell setprop debug.renderengine.capture_skia_ms 1000
+
+File will be stored in the /data/user/ directory on the device:
+adb shell ls -al /data/user/
+
+To retrieve the data from the device:
+adb pull /data/user/re_skiacapture_<timestamp>.mskp
diff --git a/libs/renderengine/skia/debug/SkiaCapture.cpp b/libs/renderengine/skia/debug/SkiaCapture.cpp
new file mode 100644
index 0000000..40f5cf2
--- /dev/null
+++ b/libs/renderengine/skia/debug/SkiaCapture.cpp
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2020 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 "SkiaCapture.h"
+
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <log/log.h>
+#include <renderengine/RenderEngine.h>
+#include <utils/Trace.h>
+
+#include "CommonPool.h"
+#include "src/utils/SkMultiPictureDocument.h"
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+// The root of the filename to write a recorded SKP to. In order for this file to
+// be written to /data/user/, user must run 'adb shell setenforce 0' in the device.
+static const std::string CAPTURED_FILENAME_BASE = "/data/user/re_skiacapture";
+
+SkiaCapture::~SkiaCapture() {
+ mTimer.stop();
+}
+
+SkCanvas* SkiaCapture::tryCapture(SkSurface* surface) NO_THREAD_SAFETY_ANALYSIS {
+ ATRACE_CALL();
+
+ // If we are not running yet, set up.
+ if (CC_LIKELY(!mCaptureRunning)) {
+ mTimerInterval = std::chrono::milliseconds(
+ base::GetIntProperty(PROPERTY_DEBUG_RENDERENGINE_CAPTURE_SKIA_MS, 0));
+ // Set up the multi-frame capture. If we fail to set it up, then just return canvas.
+ // If interval is 0, return surface.
+ if (CC_LIKELY(mTimerInterval == 0ms || !setupMultiFrameCapture())) {
+ return surface->getCanvas();
+ }
+ // Start the new timer. When timer expires, write to file.
+ mTimer.setTimeout(
+ [this] {
+ const std::scoped_lock lock(mMutex);
+ LOG_ALWAYS_FATAL_IF(mCurrentPageCanvas != nullptr);
+ writeToFile();
+ // To avoid going in circles, set the flag to 0. This way the capture can be
+ // restarted just by setting the flag and without restarting the process.
+ base::SetProperty(PROPERTY_DEBUG_RENDERENGINE_CAPTURE_SKIA_MS, "0");
+ },
+ mTimerInterval);
+ }
+
+ mMutex.lock();
+
+ // Create a canvas pointer, fill it.
+ mCurrentPageCanvas = mMultiPic->beginPage(surface->width(), surface->height());
+
+ // Setting up an nway canvas is common to any kind of capture.
+ mNwayCanvas = std::make_unique<SkNWayCanvas>(surface->width(), surface->height());
+ mNwayCanvas->addCanvas(surface->getCanvas());
+ mNwayCanvas->addCanvas(mCurrentPageCanvas);
+
+ return mNwayCanvas.get();
+}
+
+void SkiaCapture::endCapture() NO_THREAD_SAFETY_ANALYSIS {
+ ATRACE_CALL();
+ // Don't end anything if we are not running.
+ if (CC_LIKELY(!mCaptureRunning)) {
+ return;
+ }
+ // Reset the canvas pointer.
+ mCurrentPageCanvas = nullptr;
+ mNwayCanvas.reset();
+ // End page.
+ if (mMultiPic) {
+ mMultiPic->endPage();
+ }
+ mMutex.unlock();
+}
+
+SkCanvas* SkiaCapture::tryOffscreenCapture(SkSurface* surface, OffscreenState* state) {
+ ATRACE_CALL();
+ // Don't start anything if we are not running.
+ if (CC_LIKELY(!mCaptureRunning)) {
+ return surface->getCanvas();
+ }
+
+ // Create a canvas pointer, fill it.
+ state->offscreenRecorder = std::make_unique<SkPictureRecorder>();
+ SkCanvas* pictureCanvas =
+ state->offscreenRecorder->beginRecording(surface->width(), surface->height());
+
+ // Setting up an nway canvas is common to any kind of capture.
+ state->offscreenCanvas = std::make_unique<SkNWayCanvas>(surface->width(), surface->height());
+ state->offscreenCanvas->addCanvas(surface->getCanvas());
+ state->offscreenCanvas->addCanvas(pictureCanvas);
+
+ return state->offscreenCanvas.get();
+}
+
+uint64_t SkiaCapture::endOffscreenCapture(OffscreenState* state) {
+ ATRACE_CALL();
+ // Don't end anything if we are not running.
+ if (CC_LIKELY(!mCaptureRunning)) {
+ return 0;
+ }
+
+ // compute the uniqueID for this capture
+ static std::atomic<uint64_t> nextID{1};
+ const uint64_t uniqueID = nextID.fetch_add(1, std::memory_order_relaxed);
+
+ // Reset the canvas pointer as we are no longer drawing into it
+ state->offscreenCanvas.reset();
+
+ // Record the offscreen as a picture in the currently active page.
+ SkRect bounds =
+ SkRect::Make(state->offscreenRecorder->getRecordingCanvas()->imageInfo().dimensions());
+ mCurrentPageCanvas
+ ->drawAnnotation(bounds,
+ String8::format("OffscreenLayerDraw|%" PRId64, uniqueID).c_str(),
+ nullptr);
+ mCurrentPageCanvas->drawPicture(state->offscreenRecorder->finishRecordingAsPicture());
+
+ // Reset the offscreen picture recorder
+ state->offscreenRecorder.reset();
+
+ return uniqueID;
+}
+
+void SkiaCapture::writeToFile() {
+ ATRACE_CALL();
+ // Pass mMultiPic and mOpenMultiPicStream to a background thread, which will
+ // handle the heavyweight serialization work and destroy them.
+ // mOpenMultiPicStream is released to a bare pointer because keeping it in
+ // a smart pointer makes the lambda non-copyable. The lambda is only called
+ // once, so this is safe.
+ SkFILEWStream* stream = mOpenMultiPicStream.release();
+ CommonPool::post([doc = std::move(mMultiPic), stream] {
+ ALOGD("Finalizing multi frame SKP");
+ doc->close();
+ delete stream;
+ ALOGD("Multi frame SKP complete.");
+ });
+ mCaptureRunning = false;
+}
+
+bool SkiaCapture::setupMultiFrameCapture() {
+ ATRACE_CALL();
+ ALOGD("Set up multi-frame capture, ms = %llu", mTimerInterval.count());
+
+ std::string captureFile;
+ // Attach a timestamp to the file.
+ base::StringAppendF(&captureFile, "%s_%lld.mskp", CAPTURED_FILENAME_BASE.c_str(),
+ std::chrono::steady_clock::now().time_since_epoch().count());
+ auto stream = std::make_unique<SkFILEWStream>(captureFile.c_str());
+ // We own this stream and need to hold it until close() finishes.
+ if (stream->isValid()) {
+ mOpenMultiPicStream = std::move(stream);
+ mSerialContext.reset(new SkSharingSerialContext());
+ SkSerialProcs procs;
+ procs.fImageProc = SkSharingSerialContext::serializeImage;
+ procs.fImageCtx = mSerialContext.get();
+ procs.fTypefaceProc = [](SkTypeface* tf, void* ctx) {
+ return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
+ };
+ // SkDocuments don't take ownership of the streams they write.
+ // we need to keep it until after mMultiPic.close()
+ // procs is passed as a pointer, but just as a method of having an optional default.
+ // procs doesn't need to outlive this Make call
+ // The last argument is a callback for the endPage behavior.
+ // See SkSharingProc.h for more explanation of this callback.
+ mMultiPic = SkMakeMultiPictureDocument(
+ mOpenMultiPicStream.get(), &procs,
+ [sharingCtx = mSerialContext.get()](const SkPicture* pic) {
+ SkSharingSerialContext::collectNonTextureImagesFromPicture(pic, sharingCtx);
+ });
+ mCaptureRunning = true;
+ return true;
+ } else {
+ ALOGE("Could not open \"%s\" for writing.", captureFile.c_str());
+ return false;
+ }
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/debug/SkiaCapture.h b/libs/renderengine/skia/debug/SkiaCapture.h
new file mode 100644
index 0000000..5e18e60
--- /dev/null
+++ b/libs/renderengine/skia/debug/SkiaCapture.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <SkDocument.h>
+#include <SkNWayCanvas.h>
+#include <SkPictureRecorder.h>
+#include <SkSurface.h>
+
+#include <chrono>
+#include <mutex>
+
+#include "CaptureTimer.h"
+#include "tools/SkSharingProc.h"
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+using namespace std::chrono_literals;
+
+/**
+ * Class that captures frames that are sent to Skia in Render Engine. It sets up
+ * a multi frame capture and writes it into a file on the device. The capture is
+ * done based on a timer.
+ */
+class SkiaCapture {
+ using Interval = std::chrono::milliseconds;
+
+public:
+ SkiaCapture() {}
+ virtual ~SkiaCapture();
+ // Called every frame. Normally returns early with screen canvas.
+ // But when capture is enabled, returns an nwaycanvas where commands are also recorded.
+ SkCanvas* tryCapture(SkSurface* surface);
+ // Called at the end of every frame.
+ void endCapture();
+ // Returns whether the capture is running.
+ bool isCaptureRunning() { return mCaptureRunning; }
+
+ // Offscreen state member variables are private to SkiaCapture, but the allocation
+ // and lifetime is managed by the caller. This enables nested offscreen
+ // captures to occur.
+ struct OffscreenState {
+ std::unique_ptr<SkPictureRecorder> offscreenRecorder;
+ std::unique_ptr<SkNWayCanvas> offscreenCanvas;
+ };
+ SkCanvas* tryOffscreenCapture(SkSurface* surface, OffscreenState* state);
+ uint64_t endOffscreenCapture(OffscreenState* state);
+
+private:
+ // Performs the first-frame work of a multi frame SKP capture. Returns true if successful.
+ bool setupMultiFrameCapture();
+
+ // Closes the recording and serializes sequence to a file.
+ void writeToFile();
+
+ // Multi frame serialization stream and writer used when serializing more than one frame.
+ std::unique_ptr<SkFILEWStream> mOpenMultiPicStream;
+ sk_sp<SkDocument> mMultiPic;
+ std::unique_ptr<SkSharingSerialContext> mSerialContext;
+ std::unique_ptr<SkNWayCanvas> mNwayCanvas;
+
+ SkCanvas* mCurrentPageCanvas = nullptr;
+
+ // Capturing and interval control.
+ bool mCaptureRunning = false;
+ CaptureTimer mTimer;
+ Interval mTimerInterval = 0ms;
+
+ // Mutex to ensure that a frame in progress when the timer fires is allowed to run to
+ // completion before we write the file to disk.
+ std::mutex mMutex;
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/debug/SkiaMemoryReporter.cpp b/libs/renderengine/skia/debug/SkiaMemoryReporter.cpp
new file mode 100644
index 0000000..f24a4f1
--- /dev/null
+++ b/libs/renderengine/skia/debug/SkiaMemoryReporter.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
+
+#include "SkiaMemoryReporter.h"
+
+#include <SkString.h>
+#include <android-base/stringprintf.h>
+#include <log/log_main.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+using base::StringAppendF;
+
+SkiaMemoryReporter::SkiaMemoryReporter(const std::vector<ResourcePair>& resourceMap, bool itemize)
+ : mResourceMap(resourceMap),
+ mItemize(itemize),
+ mTotalSize("bytes", 0),
+ mPurgeableSize("bytes", 0) {}
+
+const char* SkiaMemoryReporter::mapName(const char* resourceName) {
+ for (auto& resource : mResourceMap) {
+ if (SkStrContains(resourceName, resource.first)) {
+ return resource.second;
+ }
+ }
+ return nullptr;
+}
+
+void SkiaMemoryReporter::resetCurrentElement() {
+ mCurrentElement.clear();
+ mCurrentValues.clear();
+ mIsCurrentValueWrapped = false;
+}
+
+void SkiaMemoryReporter::processCurrentElement() {
+ // compute the top level element name using the map
+ const char* resourceName = mCurrentElement.empty() ? nullptr : mapName(mCurrentElement.c_str());
+
+ // if we don't have a resource name then we don't know how to label the
+ // data and should abort.
+ if (resourceName == nullptr) {
+ resetCurrentElement();
+ return;
+ }
+
+ // Only count elements that contain "size"; other values just provide metadata.
+ auto sizeResult = mCurrentValues.find("size");
+ if (sizeResult != mCurrentValues.end() && sizeResult->second.value > 0) {
+ if (!mIsCurrentValueWrapped) {
+ mTotalSize.value += sizeResult->second.value;
+ mTotalSize.count++;
+ }
+ } else {
+ resetCurrentElement();
+ return;
+ }
+
+ // find the purgeable size if one exists
+ auto purgeableResult = mCurrentValues.find("purgeable_size");
+ if (!mIsCurrentValueWrapped && purgeableResult != mCurrentValues.end()) {
+ mPurgeableSize.value += purgeableResult->second.value;
+ mPurgeableSize.count++;
+ }
+
+ // do we store this element in the wrapped list or the skia managed list
+ auto& results = mIsCurrentValueWrapped ? mWrappedResults : mResults;
+
+ // insert a copy of the element and all of its keys. We must make a copy here instead of
+ // std::move() as we will continue to use these values later in the function and again
+ // when we move on to process the next element.
+ results.insert({mCurrentElement, mCurrentValues});
+
+ // insert the item into its mapped category
+ auto result = results.find(resourceName);
+ if (result != results.end()) {
+ auto& resourceValues = result->second;
+ auto totalResult = resourceValues.find(sizeResult->first);
+ if (totalResult != resourceValues.end()) {
+ ALOGE_IF(sizeResult->second.units != totalResult->second.units,
+ "resource units do not match so the sum of resource type (%s) will be invalid",
+ resourceName);
+ totalResult->second.value += sizeResult->second.value;
+ totalResult->second.count++;
+ } else {
+ ALOGE("an entry (%s) should not exist in the results without a size", resourceName);
+ }
+ } else {
+ // only store the size for the top level resource
+ results.insert({resourceName, {{sizeResult->first, sizeResult->second}}});
+ }
+
+ resetCurrentElement();
+}
+
+void SkiaMemoryReporter::dumpNumericValue(const char* dumpName, const char* valueName,
+ const char* units, uint64_t value) {
+ if (mCurrentElement != dumpName) {
+ processCurrentElement();
+ mCurrentElement = dumpName;
+ }
+ mCurrentValues.insert({valueName, {units, value}});
+}
+
+void SkiaMemoryReporter::dumpWrappedState(const char* dumpName, bool isWrappedObject) {
+ if (mCurrentElement != dumpName) {
+ processCurrentElement();
+ mCurrentElement = dumpName;
+ }
+ mIsCurrentValueWrapped = isWrappedObject;
+}
+
+void SkiaMemoryReporter::logOutput(std::string& log, bool wrappedResources) {
+ // process the current element before logging
+ processCurrentElement();
+
+ const auto& resultsMap = wrappedResources ? mWrappedResults : mResults;
+
+ // log each individual element based on the resource map
+ for (const auto& resourceCategory : mResourceMap) {
+ // find the named item and print the totals
+ const auto categoryItem = resultsMap.find(resourceCategory.second);
+ if (categoryItem != resultsMap.end()) {
+ auto result = categoryItem->second.find("size");
+ if (result != categoryItem->second.end()) {
+ TraceValue traceValue = convertUnits(result->second);
+ const char* entry = (traceValue.count > 1) ? "entries" : "entry";
+ StringAppendF(&log, " %s: %.2f %s (%d %s)\n", categoryItem->first.c_str(),
+ traceValue.value, traceValue.units, traceValue.count, entry);
+ }
+ if (mItemize) {
+ for (const auto& individualItem : resultsMap) {
+ // if the individual item matches the category then print all its details or
+ // in the case of wrapped resources just print the wrapped size
+ const char* categoryMatch = mapName(individualItem.first.c_str());
+ if (categoryMatch && strcmp(categoryMatch, resourceCategory.second) == 0) {
+ auto result = individualItem.second.find("size");
+ TraceValue size = convertUnits(result->second);
+ StringAppendF(&log, " %s: size[%.2f %s]", individualItem.first.c_str(),
+ size.value, size.units);
+ if (!wrappedResources) {
+ for (const auto& itemValues : individualItem.second) {
+ if (strcmp("size", itemValues.first) == 0) {
+ continue;
+ }
+ TraceValue traceValue = convertUnits(itemValues.second);
+ if (traceValue.value == 0.0f) {
+ StringAppendF(&log, " %s[%s]", itemValues.first,
+ traceValue.units);
+ } else {
+ StringAppendF(&log, " %s[%.2f %s]", itemValues.first,
+ traceValue.value, traceValue.units);
+ }
+ }
+ }
+ StringAppendF(&log, "\n");
+ }
+ }
+ }
+ }
+ }
+}
+
+void SkiaMemoryReporter::logTotals(std::string& log) {
+ // process the current element before logging
+ processCurrentElement();
+
+ TraceValue total = convertUnits(mTotalSize);
+ TraceValue purgeable = convertUnits(mPurgeableSize);
+ StringAppendF(&log, " %.0f bytes, %.2f %s (%.2f %s is purgeable)\n", mTotalSize.value,
+ total.value, total.units, purgeable.value, purgeable.units);
+}
+
+SkiaMemoryReporter::TraceValue SkiaMemoryReporter::convertUnits(const TraceValue& value) {
+ TraceValue output(value);
+ if (SkString("bytes") == SkString(output.units) && output.value >= 1024) {
+ output.value = output.value / 1024.0f;
+ output.units = "KB";
+ }
+ if (SkString("KB") == SkString(output.units) && output.value >= 1024) {
+ output.value = output.value / 1024.0f;
+ output.units = "MB";
+ }
+ return output;
+}
+
+} /* namespace skia */
+} /* namespace renderengine */
+} /* namespace android */
diff --git a/libs/renderengine/skia/debug/SkiaMemoryReporter.h b/libs/renderengine/skia/debug/SkiaMemoryReporter.h
new file mode 100644
index 0000000..dbbd65b
--- /dev/null
+++ b/libs/renderengine/skia/debug/SkiaMemoryReporter.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#pragma once
+
+#include <SkTraceMemoryDump.h>
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+// Mapping of resource substrings (1st element) that if found within a trace "dumpName"
+// should be mapped to the category name (2nd element). All char* used in a resourcePair
+// are expected to have a lifetime longer than the SkiaMemoryReporter in which they are used.
+typedef std::pair<const char*, const char*> ResourcePair;
+
+/*
+ * Utility class for logging the CPU/GPU usage of Skia caches in a format that is specific
+ * to RenderEngine. HWUI has a similar logging class, but the data collected and the way
+ * it is formatted and reported on are intended to be unique to each use case.
+ */
+class SkiaMemoryReporter : public SkTraceMemoryDump {
+public:
+ /**
+ * Creates the reporter class that can be populated by various Skia entry points, like
+ * SkGraphics and GrContext, as well as format and log the results.
+ * @param resourceMap An array of values that maps a Skia dumpName into a user defined category.
+ * The first vector entry that matches the dumpName is used for the mapping.
+ * @param itemize if true when logging the categories the individual elements will be printed
+ * directly after the category details are printed. Otherwise, only the category
+ * totals will be printed.
+ */
+ SkiaMemoryReporter(const std::vector<ResourcePair>& resourceMap, bool itemize);
+ ~SkiaMemoryReporter() override {}
+
+ void logOutput(std::string& log, bool wrappedResources = false);
+ void logTotals(std::string& log);
+
+ void dumpNumericValue(const char* dumpName, const char* valueName, const char* units,
+ uint64_t value) override;
+
+ void dumpStringValue(const char* dumpName, const char* valueName, const char* value) override {
+ // for convenience we just store this in the same format as numerical values
+ dumpNumericValue(dumpName, valueName, value, 0);
+ }
+ void dumpWrappedState(const char* dumpName, bool isWrappedObject) override;
+
+ LevelOfDetail getRequestedDetails() const override {
+ return SkTraceMemoryDump::kLight_LevelOfDetail;
+ }
+
+ bool shouldDumpWrappedObjects() const override { return true; }
+ void setMemoryBacking(const char*, const char*, const char*) override {}
+ void setDiscardableMemoryBacking(const char*, const SkDiscardableMemory&) override {}
+
+private:
+ struct TraceValue {
+ TraceValue(const char* units, uint64_t value) : units(units), value(value), count(1) {}
+ TraceValue(const TraceValue& v) : units(v.units), value(v.value), count(v.count) {}
+
+ const char* units;
+ float value;
+ int count;
+ };
+
+ const char* mapName(const char* resourceName);
+ void processCurrentElement();
+ void resetCurrentElement();
+ TraceValue convertUnits(const TraceValue& value);
+
+ const std::vector<ResourcePair>& mResourceMap;
+ const bool mItemize;
+
+ // variables storing the size of all non-wrapped elements being dumped
+ TraceValue mTotalSize;
+ TraceValue mPurgeableSize;
+
+ // variables storing information on the current node being dumped
+ std::string mCurrentElement;
+ std::unordered_map<const char*, TraceValue> mCurrentValues;
+ bool mIsCurrentValueWrapped = false;
+
+ // variable that stores the final format of the data after the individual elements are processed
+ std::unordered_map<std::string, std::unordered_map<const char*, TraceValue>> mResults;
+ std::unordered_map<std::string, std::unordered_map<const char*, TraceValue>> mWrappedResults;
+};
+
+} /* namespace skia */
+} /* namespace renderengine */
+} /* namespace android */
\ No newline at end of file
diff --git a/libs/renderengine/skia/debug/record.sh b/libs/renderengine/skia/debug/record.sh
new file mode 100755
index 0000000..bc406d9
--- /dev/null
+++ b/libs/renderengine/skia/debug/record.sh
@@ -0,0 +1,106 @@
+# This script captures MSKP files from RenderEngine in a connected device.
+# this only functions when RenderEngine uses the Skia backend.
+# it triggers code in SkiaCapture.cpp.
+
+# for a newly flashed device, perform first time steps with
+# record.sh rootandsetup
+
+# record all frames that RenderEngine handles over the span of 2 seconds.
+# record.sh 2000
+
+if [ -z "$1" ]; then
+ printf 'Usage:\n record.sh rootandsetup\n'
+ printf ' record.sh MILLISECONDS\n\n'
+ exit 1
+elif [ "$1" == "rootandsetup" ]; then
+ # first time use requires these changes
+ adb root
+ adb shell setenforce 0
+ adb shell setprop debug.renderengine.backend "skiagl"
+ adb shell stop
+ adb shell start
+ exit 1;
+fi
+
+# name of the newest file in /data/user/ before starting
+oldname=$(adb shell ls -cr /data/user/ | head -n 1)
+
+# record frames for some number of milliseconds.
+adb shell setprop debug.renderengine.capture_skia_ms $1
+
+# give the device time to both record, and starting writing the file.
+# Total time needed to write the file depends on how much data was recorded.
+# the loop at the end waits for this.
+sleep $(($1 / 1000 + 2));
+
+# There is no guarantee that at least one frame passed through renderengine during that time
+# but as far as I know it always at least writes a 0-byte file with a new name, unless it crashes
+# the process it is recording.
+# /data/user/re_skiacapture_56204430551705.mskp
+
+# list the files here from newest to oldest, keep only the name of the newest.
+name=$(adb shell ls -cr /data/user/ | head -n 1)
+remote_path=/data/user/$name
+
+if [[ $oldname = $name ]]; then
+ echo "No new file written, probably no RenderEngine activity during recording period."
+ exit 1
+fi
+
+# return the size of a file in bytes
+adb_filesize() {
+ adb shell "wc -c \"$1\"" 2> /dev/null | awk '{print $1}'
+}
+
+mskp_size=$(adb_filesize "/data/user/$name")
+if [[ $mskp_size = "0" ]]; then
+ echo "Empty file, probably no RenderEngine activity during recording period."
+ exit 1
+fi
+
+spin() {
+ case "$spin" in
+ 1) printf '\b|';;
+ 2) printf '\b\\';;
+ 3) printf '\b-';;
+ *) printf '\b/';;
+ esac
+ spin=$(( ( ${spin:-0} + 1 ) % 4 ))
+ sleep $1
+}
+
+printf "MSKP captured, Waiting for file serialization to finish.\n"
+
+local_path=~/Downloads/$name
+
+# wait for the file size to stop changing
+
+timeout=$(( $(date +%s) + 300))
+last_size='0' # output of last size check command
+unstable=true # false once the file size stops changing
+counter=0 # used to perform size check only 1/sec though we update spinner 20/sec
+# loop until the file size is unchanged for 1 second.
+while [ $unstable != 0 ] ; do
+ spin 0.05
+ counter=$(( $counter+1 ))
+ if ! (( $counter % 20)) ; then
+ new_size=$(adb_filesize "$remote_path")
+ unstable=$(($new_size != $last_size))
+ last_size=$new_size
+ fi
+ if [ $(date +%s) -gt $timeout ] ; then
+ printf '\bTimed out.\n'
+ exit 3
+ fi
+done
+printf '\b'
+
+printf "MSKP file serialized: %s\n" $(echo $last_size | numfmt --to=iec)
+
+adb pull "$remote_path" "$local_path"
+if ! [ -f "$local_path" ] ; then
+ printf "something went wrong with `adb pull`."
+ exit 4
+fi
+adb shell rm "$remote_path"
+printf 'SKP saved to %s\n\n' "$local_path"
\ No newline at end of file
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
new file mode 100644
index 0000000..2028def
--- /dev/null
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "BlurFilter.h"
+#include <SkCanvas.h>
+#include <SkData.h>
+#include <SkPaint.h>
+#include <SkRRect.h>
+#include <SkRuntimeEffect.h>
+#include <SkSize.h>
+#include <SkString.h>
+#include <SkSurface.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+BlurFilter::BlurFilter() {
+ SkString blurString(R"(
+ uniform shader input;
+ uniform float2 in_blurOffset;
+ uniform float2 in_maxSizeXY;
+
+ half4 main(float2 xy) {
+ half4 c = sample(input, xy);
+ c += sample(input, float2( clamp( in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
+ clamp(in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
+ c += sample(input, float2( clamp( in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
+ clamp(-in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
+ c += sample(input, float2( clamp( -in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
+ clamp(in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
+ c += sample(input, float2( clamp( -in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
+ clamp(-in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
+
+ return half4(c.rgb * 0.2, 1.0);
+ }
+ )");
+
+ auto [blurEffect, error] = SkRuntimeEffect::MakeForShader(blurString);
+ if (!blurEffect) {
+ LOG_ALWAYS_FATAL("RuntimeShader error: %s", error.c_str());
+ }
+ mBlurEffect = std::move(blurEffect);
+
+ SkString mixString(R"(
+ uniform shader blurredInput;
+ uniform shader originalInput;
+ uniform float mixFactor;
+
+ half4 main(float2 xy) {
+ return half4(mix(sample(originalInput, xy), sample(blurredInput, xy), mixFactor));
+ }
+ )");
+
+ auto [mixEffect, mixError] = SkRuntimeEffect::MakeForShader(mixString);
+ if (!mixEffect) {
+ LOG_ALWAYS_FATAL("RuntimeShader error: %s", mixError.c_str());
+ }
+ mMixEffect = std::move(mixEffect);
+}
+
+sk_sp<SkImage> BlurFilter::generate(GrRecordingContext* context, const uint32_t blurRadius,
+ const sk_sp<SkImage> input, const SkRect& blurRect) const {
+ // Kawase is an approximation of Gaussian, but it behaves differently from it.
+ // A radius transformation is required for approximating them, and also to introduce
+ // non-integer steps, necessary to smoothly interpolate large radii.
+ float tmpRadius = (float)blurRadius / 6.0f;
+ float numberOfPasses = std::min(kMaxPasses, (uint32_t)ceil(tmpRadius));
+ float radiusByPasses = tmpRadius / (float)numberOfPasses;
+
+ // create blur surface with the bit depth and colorspace of the original surface
+ SkImageInfo scaledInfo = input->imageInfo().makeWH(blurRect.width() * kInputScale,
+ blurRect.height() * kInputScale);
+
+ const float stepX = radiusByPasses;
+ const float stepY = radiusByPasses;
+
+ // For sampling Skia's API expects the inverse of what logically seems appropriate. In this
+ // case you might expect Translate(blurRect.fLeft, blurRect.fTop) X Scale(kInverseInputScale)
+ // but instead we must do the inverse.
+ SkMatrix blurMatrix = SkMatrix::Translate(-blurRect.fLeft, -blurRect.fTop);
+ blurMatrix.postScale(kInputScale, kInputScale);
+
+ // start by downscaling and doing the first blur pass
+ SkSamplingOptions linear(SkFilterMode::kLinear, SkMipmapMode::kNone);
+ SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
+ blurBuilder.child("input") =
+ input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, blurMatrix);
+ blurBuilder.uniform("in_blurOffset") = SkV2{stepX * kInputScale, stepY * kInputScale};
+ blurBuilder.uniform("in_maxSizeXY") =
+ SkV2{blurRect.width() * kInputScale - 1, blurRect.height() * kInputScale - 1};
+
+ sk_sp<SkImage> tmpBlur(blurBuilder.makeImage(context, nullptr, scaledInfo, false));
+
+ // And now we'll build our chain of scaled blur stages
+ for (auto i = 1; i < numberOfPasses; i++) {
+ const float stepScale = (float)i * kInputScale;
+ blurBuilder.child("input") =
+ tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
+ blurBuilder.uniform("in_blurOffset") = SkV2{stepX * stepScale, stepY * stepScale};
+ blurBuilder.uniform("in_maxSizeXY") =
+ SkV2{blurRect.width() * kInputScale - 1, blurRect.height() * kInputScale - 1};
+ tmpBlur = blurBuilder.makeImage(context, nullptr, scaledInfo, false);
+ }
+
+ return tmpBlur;
+}
+
+static SkMatrix getShaderTransform(const SkCanvas* canvas, const SkRect& blurRect, float scale) {
+ // 1. Apply the blur shader matrix, which scales up the blured surface to its real size
+ auto matrix = SkMatrix::Scale(scale, scale);
+ // 2. Since the blurred surface has the size of the layer, we align it with the
+ // top left corner of the layer position.
+ matrix.postConcat(SkMatrix::Translate(blurRect.fLeft, blurRect.fTop));
+ // 3. Finally, apply the inverse canvas matrix. The snapshot made in the BlurFilter is in the
+ // original surface orientation. The inverse matrix has to be applied to align the blur
+ // surface with the current orientation/position of the canvas.
+ SkMatrix drawInverse;
+ if (canvas != nullptr && canvas->getTotalMatrix().invert(&drawInverse)) {
+ matrix.postConcat(drawInverse);
+ }
+ return matrix;
+}
+
+void BlurFilter::drawBlurRegion(SkCanvas* canvas, const BlurRegion& effectRegion,
+ const SkRect& blurRect, sk_sp<SkImage> blurredImage,
+ sk_sp<SkImage> input) {
+ ATRACE_CALL();
+
+ SkPaint paint;
+ paint.setAlphaf(effectRegion.alpha);
+ if (effectRegion.alpha == 1.0f) {
+ paint.setBlendMode(SkBlendMode::kSrc);
+ }
+
+ const auto blurMatrix = getShaderTransform(canvas, blurRect, kInverseInputScale);
+ SkSamplingOptions linearSampling(SkFilterMode::kLinear, SkMipmapMode::kNone);
+ const auto blurShader = blurredImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
+ linearSampling, &blurMatrix);
+
+ if (effectRegion.blurRadius < kMaxCrossFadeRadius) {
+ // For sampling Skia's API expects the inverse of what logically seems appropriate. In this
+ // case you might expect the matrix to simply be the canvas matrix.
+ SkMatrix inputMatrix;
+ if (!canvas->getTotalMatrix().invert(&inputMatrix)) {
+ ALOGE("matrix was unable to be inverted");
+ }
+
+ SkRuntimeShaderBuilder blurBuilder(mMixEffect);
+ blurBuilder.child("blurredInput") = blurShader;
+ blurBuilder.child("originalInput") =
+ input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linearSampling,
+ inputMatrix);
+ blurBuilder.uniform("mixFactor") = effectRegion.blurRadius / kMaxCrossFadeRadius;
+
+ paint.setShader(blurBuilder.makeShader(nullptr, true));
+ } else {
+ paint.setShader(blurShader);
+ }
+
+ // TODO we should AA at least the drawRoundRect which would mean no SRC blending
+ // TODO this round rect calculation doesn't match the one used to draw in RenderEngine
+ auto rect = SkRect::MakeLTRB(effectRegion.left, effectRegion.top, effectRegion.right,
+ effectRegion.bottom);
+
+ if (effectRegion.cornerRadiusTL > 0 || effectRegion.cornerRadiusTR > 0 ||
+ effectRegion.cornerRadiusBL > 0 || effectRegion.cornerRadiusBR > 0) {
+ const SkVector radii[4] =
+ {SkVector::Make(effectRegion.cornerRadiusTL, effectRegion.cornerRadiusTL),
+ SkVector::Make(effectRegion.cornerRadiusTR, effectRegion.cornerRadiusTR),
+ SkVector::Make(effectRegion.cornerRadiusBL, effectRegion.cornerRadiusBL),
+ SkVector::Make(effectRegion.cornerRadiusBR, effectRegion.cornerRadiusBR)};
+ SkRRect roundedRect;
+ roundedRect.setRectRadii(rect, radii);
+ canvas->drawRRect(roundedRect, paint);
+ } else {
+ canvas->drawRect(rect, paint);
+ }
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h
new file mode 100644
index 0000000..731ba11
--- /dev/null
+++ b/libs/renderengine/skia/filters/BlurFilter.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <SkCanvas.h>
+#include <SkImage.h>
+#include <SkRuntimeEffect.h>
+#include <SkSurface.h>
+#include <ui/BlurRegion.h>
+
+using namespace std;
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+/**
+ * This is an implementation of a Kawase blur, as described in here:
+ * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/
+ * 00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
+ */
+class BlurFilter {
+public:
+ // Downsample FBO to improve performance
+ static constexpr float kInputScale = 0.25f;
+ // Downsample scale factor used to improve performance
+ static constexpr float kInverseInputScale = 1.0f / kInputScale;
+ // Maximum number of render passes
+ static constexpr uint32_t kMaxPasses = 4;
+ // To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited
+ // image, up to this radius.
+ static constexpr float kMaxCrossFadeRadius = 30.0f;
+
+ explicit BlurFilter();
+ virtual ~BlurFilter(){};
+
+ // Execute blur, saving it to a texture
+ sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
+ const sk_sp<SkImage> blurInput, const SkRect& blurRect) const;
+
+ void drawBlurRegion(SkCanvas* canvas, const BlurRegion& blurRegion, const SkRect& blurRect,
+ sk_sp<SkImage> blurredImage, sk_sp<SkImage> input);
+
+private:
+ sk_sp<SkRuntimeEffect> mBlurEffect;
+ sk_sp<SkRuntimeEffect> mMixEffect;
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp
new file mode 100644
index 0000000..0fbd669
--- /dev/null
+++ b/libs/renderengine/skia/filters/LinearEffect.cpp
@@ -0,0 +1,477 @@
+/*
+ * Copyright 2020 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 "LinearEffect.h"
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <SkString.h>
+#include <utils/Trace.h>
+
+#include <optional>
+
+#include "log/log.h"
+#include "math/mat4.h"
+#include "system/graphics-base-v1.0.h"
+#include "ui/ColorSpace.h"
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+static void generateEOTF(ui::Dataspace dataspace, SkString& shader) {
+ switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ shader.append(R"(
+
+ float3 EOTF(float3 color) {
+ float m1 = (2610.0 / 4096.0) / 4.0;
+ float m2 = (2523.0 / 4096.0) * 128.0;
+ float c1 = (3424.0 / 4096.0);
+ float c2 = (2413.0 / 4096.0) * 32.0;
+ float c3 = (2392.0 / 4096.0) * 32.0;
+
+ float3 tmp = pow(clamp(color, 0.0, 1.0), 1.0 / float3(m2));
+ tmp = max(tmp - c1, 0.0) / (c2 - c3 * tmp);
+ return pow(tmp, 1.0 / float3(m1));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_HLG:
+ shader.append(R"(
+ float EOTF_channel(float channel) {
+ const float a = 0.17883277;
+ const float b = 0.28466892;
+ const float c = 0.55991073;
+ return channel <= 0.5 ? channel * channel / 3.0 :
+ (exp((channel - c) / a) + b) / 12.0;
+ }
+
+ float3 EOTF(float3 color) {
+ return float3(EOTF_channel(color.r), EOTF_channel(color.g),
+ EOTF_channel(color.b));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_LINEAR:
+ shader.append(R"(
+ float3 EOTF(float3 color) {
+ return color;
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_SRGB:
+ default:
+ shader.append(R"(
+
+ float EOTF_sRGB(float srgb) {
+ return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
+ }
+
+ float3 EOTF_sRGB(float3 srgb) {
+ return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+ }
+
+ float3 EOTF(float3 srgb) {
+ return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+ }
+ )");
+ break;
+ }
+}
+
+static void generateXYZTransforms(SkString& shader) {
+ shader.append(R"(
+ uniform float4x4 in_rgbToXyz;
+ uniform float4x4 in_xyzToRgb;
+ float3 ToXYZ(float3 rgb) {
+ return clamp((in_rgbToXyz * float4(rgb, 1.0)).rgb, 0.0, 1.0);
+ }
+
+ float3 ToRGB(float3 xyz) {
+ return clamp((in_xyzToRgb * float4(xyz, 1.0)).rgb, 0.0, 1.0);
+ }
+ )");
+}
+
+// Conversion from relative light to absolute light (maps from [0, 1] to [0, maxNits])
+static void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace, SkString& shader) {
+ switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ shader.append(R"(
+ float3 ScaleLuminance(float3 xyz) {
+ return xyz * 10000.0;
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_HLG:
+ shader.append(R"(
+ float3 ScaleLuminance(float3 xyz) {
+ return xyz * 1000.0 * pow(xyz.y, 0.2);
+ }
+ )");
+ break;
+ default:
+ shader.append(R"(
+ float3 ScaleLuminance(float3 xyz) {
+ return xyz * in_displayMaxLuminance;
+ }
+ )");
+ break;
+ }
+}
+
+static void generateToneMapInterpolation(ui::Dataspace inputDataspace,
+ ui::Dataspace outputDataspace, SkString& shader) {
+ switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ case HAL_DATASPACE_TRANSFER_HLG:
+ switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ shader.append(R"(
+ float3 ToneMap(float3 xyz) {
+ return xyz;
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_HLG:
+ // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so
+ // we'll clamp the luminance range in case we're mapping from PQ input to HLG
+ // output.
+ shader.append(R"(
+ float3 ToneMap(float3 xyz) {
+ return clamp(xyz, 0.0, 1000.0);
+ }
+ )");
+ break;
+ default:
+ // Here we're mapping from HDR to SDR content, so interpolate using a Hermitian
+ // polynomial onto the smaller luminance range.
+ shader.append(R"(
+ float3 ToneMap(float3 xyz) {
+ float maxInLumi = in_inputMaxLuminance;
+ float maxOutLumi = in_displayMaxLuminance;
+
+ float nits = xyz.y;
+
+ // clamp to max input luminance
+ nits = clamp(nits, 0.0, maxInLumi);
+
+ // scale [0.0, maxInLumi] to [0.0, maxOutLumi]
+ if (maxInLumi <= maxOutLumi) {
+ return xyz * (maxOutLumi / maxInLumi);
+ } else {
+ // three control points
+ const float x0 = 10.0;
+ const float y0 = 17.0;
+ float x1 = maxOutLumi * 0.75;
+ float y1 = x1;
+ float x2 = x1 + (maxInLumi - x1) / 2.0;
+ float y2 = y1 + (maxOutLumi - y1) * 0.75;
+
+ // horizontal distances between the last three control points
+ float h12 = x2 - x1;
+ float h23 = maxInLumi - x2;
+ // tangents at the last three control points
+ float m1 = (y2 - y1) / h12;
+ float m3 = (maxOutLumi - y2) / h23;
+ float m2 = (m1 + m3) / 2.0;
+
+ if (nits < x0) {
+ // scale [0.0, x0] to [0.0, y0] linearly
+ float slope = y0 / x0;
+ return xyz * slope;
+ } else if (nits < x1) {
+ // scale [x0, x1] to [y0, y1] linearly
+ float slope = (y1 - y0) / (x1 - x0);
+ nits = y0 + (nits - x0) * slope;
+ } else if (nits < x2) {
+ // scale [x1, x2] to [y1, y2] using Hermite interp
+ float t = (nits - x1) / h12;
+ nits = (y1 * (1.0 + 2.0 * t) + h12 * m1 * t) * (1.0 - t) * (1.0 - t) +
+ (y2 * (3.0 - 2.0 * t) + h12 * m2 * (t - 1.0)) * t * t;
+ } else {
+ // scale [x2, maxInLumi] to [y2, maxOutLumi] using Hermite interp
+ float t = (nits - x2) / h23;
+ nits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) * (1.0 - t) * (1.0 - t) +
+ (maxOutLumi * (3.0 - 2.0 * t) + h23 * m3 * (t - 1.0)) * t * t;
+ }
+ }
+
+ // color.y is greater than x0 and is thus non-zero
+ return xyz * (nits / xyz.y);
+ }
+ )");
+ break;
+ }
+ break;
+ default:
+ switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ case HAL_DATASPACE_TRANSFER_HLG:
+ // Map from SDR onto an HDR output buffer
+ // Here we use a polynomial curve to map from [0, displayMaxLuminance] onto
+ // [0, maxOutLumi] which is hard-coded to be 3000 nits.
+ shader.append(R"(
+ float3 ToneMap(float3 xyz) {
+ const float maxOutLumi = 3000.0;
+
+ const float x0 = 5.0;
+ const float y0 = 2.5;
+ float x1 = in_displayMaxLuminance * 0.7;
+ float y1 = maxOutLumi * 0.15;
+ float x2 = in_displayMaxLuminance * 0.9;
+ float y2 = maxOutLumi * 0.45;
+ float x3 = in_displayMaxLuminance;
+ float y3 = maxOutLumi;
+
+ float c1 = y1 / 3.0;
+ float c2 = y2 / 2.0;
+ float c3 = y3 / 1.5;
+
+ float nits = xyz.y;
+
+ if (nits <= x0) {
+ // scale [0.0, x0] to [0.0, y0] linearly
+ float slope = y0 / x0;
+ return xyz * slope;
+ } else if (nits <= x1) {
+ // scale [x0, x1] to [y0, y1] using a curve
+ float t = (nits - x0) / (x1 - x0);
+ nits = (1.0 - t) * (1.0 - t) * y0 + 2.0 * (1.0 - t) * t * c1 + t * t * y1;
+ } else if (nits <= x2) {
+ // scale [x1, x2] to [y1, y2] using a curve
+ float t = (nits - x1) / (x2 - x1);
+ nits = (1.0 - t) * (1.0 - t) * y1 + 2.0 * (1.0 - t) * t * c2 + t * t * y2;
+ } else {
+ // scale [x2, x3] to [y2, y3] using a curve
+ float t = (nits - x2) / (x3 - x2);
+ nits = (1.0 - t) * (1.0 - t) * y2 + 2.0 * (1.0 - t) * t * c3 + t * t * y3;
+ }
+
+ // xyz.y is greater than x0 and is thus non-zero
+ return xyz * (nits / xyz.y);
+ }
+ )");
+ break;
+ default:
+ // For completeness, this is tone-mapping from SDR to SDR, where this is just a
+ // no-op.
+ shader.append(R"(
+ float3 ToneMap(float3 xyz) {
+ return xyz;
+ }
+ )");
+ break;
+ }
+ break;
+ }
+}
+
+// Normalizes from absolute light back to relative light (maps from [0, maxNits] back to [0, 1])
+static void generateLuminanceNormalizationForOOTF(ui::Dataspace outputDataspace, SkString& shader) {
+ switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ shader.append(R"(
+ float3 NormalizeLuminance(float3 xyz) {
+ return xyz / 10000.0;
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_HLG:
+ shader.append(R"(
+ float3 NormalizeLuminance(float3 xyz) {
+ return xyz / 1000.0 * pow(xyz.y / 1000.0, -0.2 / 1.2);
+ }
+ )");
+ break;
+ default:
+ shader.append(R"(
+ float3 NormalizeLuminance(float3 xyz) {
+ return xyz / in_displayMaxLuminance;
+ }
+ )");
+ break;
+ }
+}
+
+static void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace,
+ SkString& shader) {
+ // Input uniforms
+ shader.append(R"(
+ uniform float in_displayMaxLuminance;
+ uniform float in_inputMaxLuminance;
+ )");
+
+ generateLuminanceScalesForOOTF(inputDataspace, shader);
+ generateToneMapInterpolation(inputDataspace, outputDataspace, shader);
+ generateLuminanceNormalizationForOOTF(outputDataspace, shader);
+
+ shader.append(R"(
+ float3 OOTF(float3 xyz) {
+ return NormalizeLuminance(ToneMap(ScaleLuminance(xyz)));
+ }
+ )");
+}
+
+static void generateOETF(ui::Dataspace dataspace, SkString& shader) {
+ switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ shader.append(R"(
+
+ float3 OETF(float3 xyz) {
+ float m1 = (2610.0 / 4096.0) / 4.0;
+ float m2 = (2523.0 / 4096.0) * 128.0;
+ float c1 = (3424.0 / 4096.0);
+ float c2 = (2413.0 / 4096.0) * 32.0;
+ float c3 = (2392.0 / 4096.0) * 32.0;
+
+ float3 tmp = pow(xyz, float3(m1));
+ tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
+ return pow(tmp, float3(m2));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_HLG:
+ shader.append(R"(
+ float OETF_channel(float channel) {
+ const float a = 0.17883277;
+ const float b = 0.28466892;
+ const float c = 0.55991073;
+ return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) :
+ a * log(12.0 * channel - b) + c;
+ }
+
+ float3 OETF(float3 linear) {
+ return float3(OETF_channel(linear.r), OETF_channel(linear.g),
+ OETF_channel(linear.b));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_LINEAR:
+ shader.append(R"(
+ float3 OETF(float3 linear) {
+ return linear;
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_SRGB:
+ default:
+ shader.append(R"(
+ float OETF_sRGB(float linear) {
+ return linear <= 0.0031308 ?
+ linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
+ }
+
+ float3 OETF_sRGB(float3 linear) {
+ return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+ }
+
+ float3 OETF(float3 linear) {
+ return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+ }
+ )");
+ break;
+ }
+}
+
+static void generateEffectiveOOTF(bool undoPremultipliedAlpha, SkString& shader) {
+ shader.append(R"(
+ uniform shader input;
+ half4 main(float2 xy) {
+ float4 c = float4(sample(input, xy));
+ )");
+ if (undoPremultipliedAlpha) {
+ shader.append(R"(
+ c.rgb = c.rgb / (c.a + 0.0019);
+ )");
+ }
+ shader.append(R"(
+ c.rgb = OETF(ToRGB(OOTF(ToXYZ(EOTF(c.rgb)))));
+ )");
+ if (undoPremultipliedAlpha) {
+ shader.append(R"(
+ c.rgb = c.rgb * (c.a + 0.0019);
+ )");
+ }
+ shader.append(R"(
+ return c;
+ }
+ )");
+}
+static ColorSpace toColorSpace(ui::Dataspace dataspace) {
+ switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
+ case HAL_DATASPACE_STANDARD_BT709:
+ return ColorSpace::sRGB();
+ break;
+ case HAL_DATASPACE_STANDARD_DCI_P3:
+ return ColorSpace::DisplayP3();
+ break;
+ case HAL_DATASPACE_STANDARD_BT2020:
+ return ColorSpace::BT2020();
+ break;
+ default:
+ return ColorSpace::sRGB();
+ break;
+ }
+}
+
+sk_sp<SkRuntimeEffect> buildRuntimeEffect(const LinearEffect& linearEffect) {
+ ATRACE_CALL();
+ SkString shaderString;
+ generateEOTF(linearEffect.inputDataspace, shaderString);
+ generateXYZTransforms(shaderString);
+ generateOOTF(linearEffect.inputDataspace, linearEffect.outputDataspace, shaderString);
+ generateOETF(linearEffect.outputDataspace, shaderString);
+ generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, shaderString);
+
+ auto [shader, error] = SkRuntimeEffect::MakeForShader(shaderString);
+ if (!shader) {
+ LOG_ALWAYS_FATAL("LinearColorFilter construction error: %s", error.c_str());
+ }
+ return shader;
+}
+
+sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader, const LinearEffect& linearEffect,
+ sk_sp<SkRuntimeEffect> runtimeEffect,
+ const mat4& colorTransform, float maxDisplayLuminance,
+ float maxMasteringLuminance, float maxContentLuminance) {
+ ATRACE_CALL();
+ SkRuntimeShaderBuilder effectBuilder(runtimeEffect);
+
+ effectBuilder.child("input") = shader;
+
+ if (linearEffect.inputDataspace == linearEffect.outputDataspace) {
+ effectBuilder.uniform("in_rgbToXyz") = mat4();
+ effectBuilder.uniform("in_xyzToRgb") = colorTransform;
+ } else {
+ ColorSpace inputColorSpace = toColorSpace(linearEffect.inputDataspace);
+ ColorSpace outputColorSpace = toColorSpace(linearEffect.outputDataspace);
+
+ effectBuilder.uniform("in_rgbToXyz") = mat4(inputColorSpace.getRGBtoXYZ());
+ effectBuilder.uniform("in_xyzToRgb") =
+ colorTransform * mat4(outputColorSpace.getXYZtoRGB());
+ }
+
+ effectBuilder.uniform("in_displayMaxLuminance") = maxDisplayLuminance;
+ effectBuilder.uniform("in_inputMaxLuminance") =
+ std::min(maxMasteringLuminance, maxContentLuminance);
+ return effectBuilder.makeShader(nullptr, false);
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/skia/filters/LinearEffect.h b/libs/renderengine/skia/filters/LinearEffect.h
new file mode 100644
index 0000000..20b8338
--- /dev/null
+++ b/libs/renderengine/skia/filters/LinearEffect.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <math/mat4.h>
+
+#include <optional>
+
+#include "SkRuntimeEffect.h"
+#include "SkShader.h"
+#include "ui/GraphicTypes.h"
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+/**
+ * Arguments for creating an effect that applies color transformations in linear XYZ space.
+ * A linear effect is decomposed into the following steps when operating on an image:
+ * 1. Electrical-Optical Transfer Function (EOTF) maps the input RGB signal into the intended
+ * relative display brightness of the scene in nits for each RGB channel
+ * 2. Transformation matrix from linear RGB brightness to linear XYZ, to operate on display
+ * luminance.
+ * 3. Opto-Optical Transfer Function (OOTF) applies a "rendering intent". This can include tone
+ * mapping to display SDR content alongside HDR content, or any number of subjective transformations
+ * 4. Transformation matrix from linear XYZ back to linear RGB brightness.
+ * 5. Opto-Electronic Transfer Function (OETF) maps the display brightness of the scene back to
+ * output RGB colors.
+ *
+ * For further reading, consult the recommendation in ITU-R BT.2390-4:
+ * https://www.itu.int/dms_pub/itu-r/opb/rep/R-REP-BT.2390-4-2018-PDF-E.pdf
+ *
+ * Skia normally attempts to do its own simple tone mapping, i.e., the working color space is
+ * intended to be the output surface. However, Skia does not support complex tone mapping such as
+ * polynomial interpolation. As such, this filter assumes that tone mapping has not yet been applied
+ * to the source colors. so that the tone mapping process is only applied once by this effect. Tone
+ * mapping is applied when presenting HDR content (content with HLG or PQ transfer functions)
+ * alongside other content, whereby maximum input luminance is mapped to maximum output luminance
+ * and intermediate values are interpolated.
+ */
+struct LinearEffect {
+ // Input dataspace of the source colors.
+ const ui::Dataspace inputDataspace = ui::Dataspace::SRGB;
+
+ // Working dataspace for the output surface, for conversion from linear space.
+ const ui::Dataspace outputDataspace = ui::Dataspace::SRGB;
+
+ // Sets whether alpha premultiplication must be undone.
+ // This is required if the source colors use premultiplied alpha and is not opaque.
+ const bool undoPremultipliedAlpha = false;
+};
+
+static inline bool operator==(const LinearEffect& lhs, const LinearEffect& rhs) {
+ return lhs.inputDataspace == rhs.inputDataspace && lhs.outputDataspace == rhs.outputDataspace &&
+ lhs.undoPremultipliedAlpha == rhs.undoPremultipliedAlpha;
+}
+
+struct LinearEffectHasher {
+ // Inspired by art/runtime/class_linker.cc
+ // Also this is what boost:hash_combine does
+ static size_t HashCombine(size_t seed, size_t val) {
+ return seed ^ (val + 0x9e3779b9 + (seed << 6) + (seed >> 2));
+ }
+ size_t operator()(const LinearEffect& le) const {
+ size_t result = std::hash<ui::Dataspace>{}(le.inputDataspace);
+ result = HashCombine(result, std::hash<ui::Dataspace>{}(le.outputDataspace));
+ return HashCombine(result, std::hash<bool>{}(le.undoPremultipliedAlpha));
+ }
+};
+
+sk_sp<SkRuntimeEffect> buildRuntimeEffect(const LinearEffect& linearEffect);
+
+// Generates a shader resulting from applying the a linear effect created from
+// LinearEffectArgs::buildEffect to an inputShader.
+// Optionally, a color transform may also be provided, which combines with the
+// matrix transforming from linear XYZ to linear RGB immediately before OETF.
+// We also provide additional HDR metadata upon creating the shader:
+// * The max display luminance is the max luminance of the physical display in nits
+// * The max mastering luminance is provided as the max luminance from the SMPTE 2086
+// standard.
+// * The max content luminance is provided as the max light level from the CTA 861.3
+// standard.
+sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> inputShader,
+ const LinearEffect& linearEffect,
+ sk_sp<SkRuntimeEffect> runtimeEffect,
+ const mat4& colorTransform, float maxDisplayLuminance,
+ float maxMasteringLuminance, float maxContentLuminance);
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp
index fdb3a6f..d0e19dd 100644
--- a/libs/renderengine/tests/Android.bp
+++ b/libs/renderengine/tests/Android.bp
@@ -23,15 +23,21 @@
cc_test {
name: "librenderengine_test",
- defaults: ["surfaceflinger_defaults"],
+ defaults: ["skia_deps", "surfaceflinger_defaults"],
test_suites: ["device-tests"],
srcs: [
"RenderEngineTest.cpp",
+ "RenderEngineThreadedTest.cpp",
+ ],
+ include_dirs: [
+ "external/skia/src/gpu",
],
static_libs: [
"libgmock",
"librenderengine",
+ "librenderengine_mocks",
],
+
shared_libs: [
"libbase",
"libcutils",
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 1ec9412..34ef0a4 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -14,20 +14,28 @@
* limitations under the License.
*/
+#undef LOG_TAG
+#define LOG_TAG "RenderEngineTest"
+
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
+
+#include <cutils/properties.h>
+#include <gtest/gtest.h>
+#include <renderengine/ExternalTexture.h>
+#include <renderengine/RenderEngine.h>
+#include <sync/sync.h>
+#include <ui/PixelFormat.h>
#include <chrono>
#include <condition_variable>
#include <fstream>
-#include <gtest/gtest.h>
-#include <cutils/properties.h>
-#include <renderengine/RenderEngine.h>
-#include <sync/sync.h>
-#include <ui/PixelFormat.h>
#include "../gl/GLESRenderEngine.h"
+#include "../skia/SkiaGLRenderEngine.h"
+#include "../threaded/RenderEngineThreaded.h"
constexpr int DEFAULT_DISPLAY_WIDTH = 128;
constexpr int DEFAULT_DISPLAY_HEIGHT = 256;
@@ -36,61 +44,174 @@
namespace android {
-struct RenderEngineTest : public ::testing::Test {
- static void SetUpTestSuite() {
+class RenderEngineFactory {
+public:
+ virtual ~RenderEngineFactory() = default;
+
+ virtual std::string name() = 0;
+ virtual renderengine::RenderEngine::RenderEngineType type() = 0;
+ virtual std::unique_ptr<renderengine::RenderEngine> createRenderEngine() = 0;
+ virtual std::unique_ptr<renderengine::gl::GLESRenderEngine> createGLESRenderEngine() {
+ return nullptr;
+ }
+};
+
+class GLESRenderEngineFactory : public RenderEngineFactory {
+public:
+ std::string name() override { return "GLESRenderEngineFactory"; }
+
+ renderengine::RenderEngine::RenderEngineType type() {
+ return renderengine::RenderEngine::RenderEngineType::GLES;
+ }
+
+ std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override {
+ return createGLESRenderEngine();
+ }
+
+ std::unique_ptr<renderengine::gl::GLESRenderEngine> createGLESRenderEngine() {
renderengine::RenderEngineCreationArgs reCreationArgs =
- renderengine::RenderEngineCreationArgs::Builder()
- .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
- .setImageCacheSize(1)
- .setUseColorManagerment(false)
- .setEnableProtectedContext(false)
- .setPrecacheToneMapperShaderOnly(false)
- .setSupportsBackgroundBlur(true)
- .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
- .build();
- sRE = renderengine::gl::GLESRenderEngine::create(reCreationArgs);
+ renderengine::RenderEngineCreationArgs::Builder()
+ .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
+ .setImageCacheSize(1)
+ .setUseColorManagerment(false)
+ .setEnableProtectedContext(false)
+ .setPrecacheToneMapperShaderOnly(false)
+ .setSupportsBackgroundBlur(true)
+ .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
+ .setRenderEngineType(type())
+ .build();
+ return renderengine::gl::GLESRenderEngine::create(reCreationArgs);
+ }
+};
- reCreationArgs.useColorManagement = true;
- sRECM = renderengine::gl::GLESRenderEngine::create(reCreationArgs);
+class GLESCMRenderEngineFactory : public RenderEngineFactory {
+public:
+ std::string name() override { return "GLESCMRenderEngineFactory"; }
+
+ renderengine::RenderEngine::RenderEngineType type() {
+ return renderengine::RenderEngine::RenderEngineType::GLES;
}
- static void TearDownTestSuite() {
- // The ordering here is important - sCurrentBuffer must live longer
- // than RenderEngine to avoid a null reference on tear-down.
- sRE = nullptr;
- sRECM = nullptr;
- sCurrentBuffer = nullptr;
+ std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override {
+ return createGLESRenderEngine();
}
- static sp<GraphicBuffer> allocateDefaultBuffer() {
- return new GraphicBuffer(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT,
- HAL_PIXEL_FORMAT_RGBA_8888, 1,
- GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
- GRALLOC_USAGE_HW_RENDER,
- "output");
+ std::unique_ptr<renderengine::gl::GLESRenderEngine> createGLESRenderEngine() override {
+ renderengine::RenderEngineCreationArgs reCreationArgs =
+ renderengine::RenderEngineCreationArgs::Builder()
+ .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
+ .setImageCacheSize(1)
+ .setEnableProtectedContext(false)
+ .setPrecacheToneMapperShaderOnly(false)
+ .setSupportsBackgroundBlur(true)
+ .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
+ .setRenderEngineType(type())
+ .setUseColorManagerment(true)
+ .build();
+ return renderengine::gl::GLESRenderEngine::create(reCreationArgs);
+ }
+};
+
+class SkiaGLESRenderEngineFactory : public RenderEngineFactory {
+public:
+ std::string name() override { return "SkiaGLRenderEngineFactory"; }
+
+ renderengine::RenderEngine::RenderEngineType type() {
+ return renderengine::RenderEngine::RenderEngineType::SKIA_GL;
+ }
+
+ std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override {
+ renderengine::RenderEngineCreationArgs reCreationArgs =
+ renderengine::RenderEngineCreationArgs::Builder()
+ .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
+ .setImageCacheSize(1)
+ .setEnableProtectedContext(false)
+ .setPrecacheToneMapperShaderOnly(false)
+ .setSupportsBackgroundBlur(true)
+ .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
+ .setRenderEngineType(type())
+ .build();
+ return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs);
+ }
+};
+
+class SkiaGLESCMRenderEngineFactory : public RenderEngineFactory {
+public:
+ std::string name() override { return "SkiaGLCMRenderEngineFactory"; }
+
+ renderengine::RenderEngine::RenderEngineType type() {
+ return renderengine::RenderEngine::RenderEngineType::SKIA_GL;
+ }
+
+ std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override {
+ renderengine::RenderEngineCreationArgs reCreationArgs =
+ renderengine::RenderEngineCreationArgs::Builder()
+ .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
+ .setImageCacheSize(1)
+ .setEnableProtectedContext(false)
+ .setPrecacheToneMapperShaderOnly(false)
+ .setSupportsBackgroundBlur(true)
+ .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
+ .setRenderEngineType(type())
+ .setUseColorManagerment(true)
+ .build();
+ return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs);
+ }
+};
+
+class RenderEngineTest : public ::testing::TestWithParam<std::shared_ptr<RenderEngineFactory>> {
+public:
+ std::shared_ptr<renderengine::ExternalTexture> allocateDefaultBuffer() {
+ return std::make_shared<
+ renderengine::
+ ExternalTexture>(new GraphicBuffer(DEFAULT_DISPLAY_WIDTH,
+ DEFAULT_DISPLAY_HEIGHT,
+ HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ GRALLOC_USAGE_SW_READ_OFTEN |
+ GRALLOC_USAGE_SW_WRITE_OFTEN |
+ GRALLOC_USAGE_HW_RENDER |
+ GRALLOC_USAGE_HW_TEXTURE,
+ "output"),
+ *mRE,
+ renderengine::ExternalTexture::Usage::READABLE |
+ renderengine::ExternalTexture::Usage::WRITEABLE);
}
// Allocates a 1x1 buffer to fill with a solid color
- static sp<GraphicBuffer> allocateSourceBuffer(uint32_t width, uint32_t height) {
- return new GraphicBuffer(width, height, HAL_PIXEL_FORMAT_RGBA_8888, 1,
- GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
- GRALLOC_USAGE_HW_TEXTURE,
- "input");
+ std::shared_ptr<renderengine::ExternalTexture> allocateSourceBuffer(uint32_t width,
+ uint32_t height) {
+ return std::make_shared<
+ renderengine::
+ ExternalTexture>(new GraphicBuffer(width, height,
+ HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ GRALLOC_USAGE_SW_READ_OFTEN |
+ GRALLOC_USAGE_SW_WRITE_OFTEN |
+ GRALLOC_USAGE_HW_TEXTURE,
+ "input"),
+ *mRE,
+ renderengine::ExternalTexture::Usage::READABLE |
+ renderengine::ExternalTexture::Usage::WRITEABLE);
}
- RenderEngineTest() { mBuffer = allocateDefaultBuffer(); }
+ RenderEngineTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
~RenderEngineTest() {
if (WRITE_BUFFER_TO_FILE_ON_FAILURE && ::testing::Test::HasFailure()) {
writeBufferToFile("/data/texture_out_");
}
for (uint32_t texName : mTexNames) {
- sRE->deleteTextures(1, &texName);
- EXPECT_FALSE(sRE->isTextureNameKnownForTesting(texName));
+ mRE->deleteTextures(1, &texName);
+ if (mGLESRE != nullptr) {
+ EXPECT_FALSE(mGLESRE->isTextureNameKnownForTesting(texName));
+ }
}
- for (uint32_t texName : mTexNamesCM) {
- sRECM->deleteTextures(1, &texName);
- }
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
}
void writeBufferToFile(const char* basename) {
@@ -106,20 +227,21 @@
}
uint8_t* pixels;
- mBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
- reinterpret_cast<void**>(&pixels));
+ mBuffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
file << "P6\n";
- file << mBuffer->getWidth() << "\n";
- file << mBuffer->getHeight() << "\n";
+ file << mBuffer->getBuffer()->getWidth() << "\n";
+ file << mBuffer->getBuffer()->getHeight() << "\n";
file << 255 << "\n";
- std::vector<uint8_t> outBuffer(mBuffer->getWidth() * mBuffer->getHeight() * 3);
+ std::vector<uint8_t> outBuffer(mBuffer->getBuffer()->getWidth() *
+ mBuffer->getBuffer()->getHeight() * 3);
auto outPtr = reinterpret_cast<uint8_t*>(outBuffer.data());
- for (int32_t j = 0; j < mBuffer->getHeight(); j++) {
- const uint8_t* src = pixels + (mBuffer->getStride() * j) * 4;
- for (int32_t i = 0; i < mBuffer->getWidth(); i++) {
+ for (int32_t j = 0; j < mBuffer->getBuffer()->getHeight(); j++) {
+ const uint8_t* src = pixels + (mBuffer->getBuffer()->getStride() * j) * 4;
+ for (int32_t i = 0; i < mBuffer->getBuffer()->getWidth(); i++) {
// Only copy R, G and B components
outPtr[0] = src[0];
outPtr[1] = src[1];
@@ -130,7 +252,7 @@
}
}
file.write(reinterpret_cast<char*>(outBuffer.data()), outBuffer.size());
- mBuffer->unlock();
+ mBuffer->getBuffer()->unlock();
}
void expectBufferColor(const Region& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
@@ -157,13 +279,13 @@
void expectBufferColor(const Rect& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
std::function<bool(const uint8_t* a, const uint8_t* b)> colorCompare) {
uint8_t* pixels;
- mBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
- reinterpret_cast<void**>(&pixels));
+ mBuffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
int32_t maxFails = 10;
int32_t fails = 0;
for (int32_t j = 0; j < region.getHeight(); j++) {
- const uint8_t* src =
- pixels + (mBuffer->getStride() * (region.top + j) + region.left) * 4;
+ const uint8_t* src = pixels +
+ (mBuffer->getBuffer()->getStride() * (region.top + j) + region.left) * 4;
for (int32_t i = 0; i < region.getWidth(); i++) {
const uint8_t expected[4] = {r, g, b, a};
bool equal = colorCompare(src, expected);
@@ -184,7 +306,7 @@
break;
}
}
- mBuffer->unlock();
+ mBuffer->getBuffer()->unlock();
}
void expectAlpha(const Rect& rect, uint8_t a) {
@@ -233,6 +355,26 @@
backgroundColor.a);
}
+ void expectShadowColorWithoutCaster(const FloatRect& casterBounds,
+ const renderengine::ShadowSettings& shadow,
+ const ubyte4& backgroundColor) {
+ const float shadowInset = shadow.length * -1.0f;
+ const Rect casterRect(casterBounds);
+ const Rect shadowRect =
+ Rect(casterRect).inset(shadowInset, shadowInset, shadowInset, shadowInset);
+
+ const Region backgroundRegion =
+ Region(fullscreenRect()).subtractSelf(casterRect).subtractSelf(shadowRect);
+
+ expectAlpha(shadowRect, 255);
+ // (0, 0, 0) fill on the bounds of the layer should be ignored.
+ expectBufferColor(casterRect, 255, 255, 255, 255, 254);
+
+ // verify background
+ expectBufferColor(backgroundRegion, backgroundColor.r, backgroundColor.g, backgroundColor.b,
+ backgroundColor.a);
+ }
+
static renderengine::ShadowSettings getShadowSettings(const vec2& casterPos, float shadowLength,
bool casterIsTranslucent) {
renderengine::ShadowSettings shadow;
@@ -258,16 +400,10 @@
}
void invokeDraw(renderengine::DisplaySettings settings,
- std::vector<const renderengine::LayerSettings*> layers,
- sp<GraphicBuffer> buffer,
- bool useColorManagement = false) {
+ std::vector<const renderengine::LayerSettings*> layers) {
base::unique_fd fence;
- status_t status = useColorManagement ?
- sRECM ->drawLayers(settings, layers, buffer->getNativeBuffer(), true,
- base::unique_fd(), &fence) :
- sRE->drawLayers(settings, layers, buffer->getNativeBuffer(), true,
- base::unique_fd(), &fence);
- sCurrentBuffer = buffer;
+ status_t status =
+ mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fence);
int fd = fence.release();
if (fd >= 0) {
@@ -276,21 +412,15 @@
}
ASSERT_EQ(NO_ERROR, status);
- if (layers.size() > 0) {
- if (useColorManagement) {
- ASSERT_TRUE(sRECM->isFramebufferImageCachedForTesting(buffer->getId()));
- } else {
- ASSERT_TRUE(sRE->isFramebufferImageCachedForTesting(buffer->getId()));
- }
+ if (layers.size() > 0 && mGLESRE != nullptr) {
+ ASSERT_TRUE(mGLESRE->isFramebufferImageCachedForTesting(mBuffer->getBuffer()->getId()));
}
}
void drawEmptyLayers() {
renderengine::DisplaySettings settings;
std::vector<const renderengine::LayerSettings*> layers;
- // Meaningless buffer since we don't do any drawing
- sp<GraphicBuffer> buffer = new GraphicBuffer();
- invokeDraw(settings, layers, buffer);
+ invokeDraw(settings, layers);
}
template <typename SourceVariant>
@@ -336,15 +466,12 @@
void fillBufferLayerTransform();
template <typename SourceVariant>
- void fillBufferWithColorTransform(bool useColorManagement = false);
+ void fillBufferWithColorTransform();
template <typename SourceVariant>
void fillBufferColorTransform();
template <typename SourceVariant>
- void fillBufferColorTransformCM();
-
- template <typename SourceVariant>
void fillBufferWithColorTransformZeroLayerAlpha();
template <typename SourceVariant>
@@ -385,38 +512,49 @@
const renderengine::ShadowSettings& shadow, const ubyte4& casterColor,
const ubyte4& backgroundColor);
- // Keep around the same renderengine object to save on initialization time.
- // For now, exercise the GL backend directly so that some caching specifics
- // can be tested without changing the interface.
- static std::unique_ptr<renderengine::gl::GLESRenderEngine> sRE;
- // renderengine object with Color Management enabled
- static std::unique_ptr<renderengine::gl::GLESRenderEngine> sRECM;
- // Dumb hack to avoid NPE in the EGL driver: the GraphicBuffer needs to
- // be freed *after* RenderEngine is destroyed, so that the EGL image is
- // destroyed first.
- static sp<GraphicBuffer> sCurrentBuffer;
+ void drawShadowWithoutCaster(const FloatRect& castingBounds,
+ const renderengine::ShadowSettings& shadow,
+ const ubyte4& backgroundColor);
- sp<GraphicBuffer> mBuffer;
+ void initializeRenderEngine();
+
+ std::unique_ptr<renderengine::RenderEngine> mRE;
+ std::shared_ptr<renderengine::ExternalTexture> mBuffer;
+ // GLESRenderEngine for testing GLES-specific behavior.
+ // Owened by mRE, but this is downcasted.
+ renderengine::gl::GLESRenderEngine* mGLESRE = nullptr;
std::vector<uint32_t> mTexNames;
- std::vector<uint32_t> mTexNamesCM;
};
-std::unique_ptr<renderengine::gl::GLESRenderEngine> RenderEngineTest::sRE = nullptr;
-std::unique_ptr<renderengine::gl::GLESRenderEngine> RenderEngineTest::sRECM = nullptr;
-
-sp<GraphicBuffer> RenderEngineTest::sCurrentBuffer = nullptr;
+void RenderEngineTest::initializeRenderEngine() {
+ const auto& renderEngineFactory = GetParam();
+ if (renderEngineFactory->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
+ // Only GLESRenderEngine exposes test-only methods. Provide a pointer to the
+ // GLESRenderEngine if we're using it so that we don't need to dynamic_cast
+ // every time.
+ std::unique_ptr<renderengine::gl::GLESRenderEngine> renderEngine =
+ renderEngineFactory->createGLESRenderEngine();
+ mGLESRE = renderEngine.get();
+ mRE = std::move(renderEngine);
+ } else {
+ mRE = renderEngineFactory->createRenderEngine();
+ }
+ mBuffer = allocateDefaultBuffer();
+}
struct ColorSourceVariant {
static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b,
- RenderEngineTest* /*fixture*/, bool /*useColorManagement*/ = false) {
+ RenderEngineTest* /*fixture*/) {
layer.source.solidColor = half3(r, g, b);
+ layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
}
};
struct RelaxOpaqueBufferVariant {
static void setOpaqueBit(renderengine::LayerSettings& layer) {
layer.source.buffer.isOpaque = false;
+ layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
}
static uint8_t getAlphaChannel() { return 255; }
@@ -425,37 +563,32 @@
struct ForceOpaqueBufferVariant {
static void setOpaqueBit(renderengine::LayerSettings& layer) {
layer.source.buffer.isOpaque = true;
+ layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
}
static uint8_t getAlphaChannel() {
// The isOpaque bit will override the alpha channel, so this should be
// arbitrary.
- return 10;
+ return 50;
}
};
template <typename OpaquenessVariant>
struct BufferSourceVariant {
static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b,
- RenderEngineTest* fixture,
- bool useColorManagement = false) {
- sp<GraphicBuffer> buf = RenderEngineTest::allocateSourceBuffer(1, 1);
+ RenderEngineTest* fixture) {
+ const auto buf = fixture->allocateSourceBuffer(1, 1);
uint32_t texName;
- if (useColorManagement) {
- fixture->sRECM->genTextures(1, &texName);
- fixture->mTexNamesCM.push_back(texName);
- } else {
- fixture->sRE->genTextures(1, &texName);
- fixture->mTexNames.push_back(texName);
- }
+ fixture->mRE->genTextures(1, &texName);
+ fixture->mTexNames.push_back(texName);
uint8_t* pixels;
- buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
- reinterpret_cast<void**>(&pixels));
+ buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
- for (int32_t j = 0; j < buf->getHeight(); j++) {
- uint8_t* iter = pixels + (buf->getStride() * j) * 4;
- for (int32_t i = 0; i < buf->getWidth(); i++) {
+ for (int32_t j = 0; j < buf->getBuffer()->getHeight(); j++) {
+ uint8_t* iter = pixels + (buf->getBuffer()->getStride() * j) * 4;
+ for (int32_t i = 0; i < buf->getBuffer()->getWidth(); i++) {
iter[0] = uint8_t(r * 255);
iter[1] = uint8_t(g * 255);
iter[2] = uint8_t(b * 255);
@@ -464,10 +597,11 @@
}
}
- buf->unlock();
+ buf->getBuffer()->unlock();
layer.source.buffer.buffer = buf;
layer.source.buffer.textureName = texName;
+ layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
OpaquenessVariant::setOpaqueBit(layer);
}
};
@@ -477,17 +611,19 @@
renderengine::DisplaySettings settings;
settings.physicalDisplay = fullscreenRect();
settings.clip = fullscreenRect();
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
std::vector<const renderengine::LayerSettings*> layers;
renderengine::LayerSettings layer;
+ layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
layer.geometry.boundaries = fullscreenRect().toFloatRect();
SourceVariant::fillColor(layer, r, g, b, this);
layer.alpha = a;
layers.push_back(&layer);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers);
}
template <typename SourceVariant>
@@ -517,18 +653,20 @@
template <typename SourceVariant>
void RenderEngineTest::fillRedOffsetBuffer() {
renderengine::DisplaySettings settings;
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
settings.physicalDisplay = offsetRect();
settings.clip = offsetRectAtZero();
std::vector<const renderengine::LayerSettings*> layers;
renderengine::LayerSettings layer;
+ layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
layer.geometry.boundaries = offsetRectAtZero().toFloatRect();
SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
layer.alpha = 1.0f;
layers.push_back(&layer);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers);
}
template <typename SourceVariant>
@@ -548,6 +686,7 @@
template <typename SourceVariant>
void RenderEngineTest::fillBufferCheckers(uint32_t orientationFlag) {
renderengine::DisplaySettings settings;
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
settings.physicalDisplay = fullscreenRect();
// Here logical space is 2x2
settings.clip = Rect(2, 2);
@@ -556,18 +695,21 @@
std::vector<const renderengine::LayerSettings*> layers;
renderengine::LayerSettings layerOne;
+ layerOne.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
Rect rectOne(0, 0, 1, 1);
layerOne.geometry.boundaries = rectOne.toFloatRect();
SourceVariant::fillColor(layerOne, 1.0f, 0.0f, 0.0f, this);
layerOne.alpha = 1.0f;
renderengine::LayerSettings layerTwo;
+ layerTwo.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
Rect rectTwo(0, 1, 1, 2);
layerTwo.geometry.boundaries = rectTwo.toFloatRect();
SourceVariant::fillColor(layerTwo, 0.0f, 1.0f, 0.0f, this);
layerTwo.alpha = 1.0f;
renderengine::LayerSettings layerThree;
+ layerThree.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
Rect rectThree(1, 0, 2, 1);
layerThree.geometry.boundaries = rectThree.toFloatRect();
SourceVariant::fillColor(layerThree, 0.0f, 0.0f, 1.0f, this);
@@ -577,7 +719,7 @@
layers.push_back(&layerTwo);
layers.push_back(&layerThree);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers);
}
template <typename SourceVariant>
@@ -650,10 +792,12 @@
settings.physicalDisplay = fullscreenRect();
// Here logical space is 2x2
settings.clip = Rect(2, 2);
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
std::vector<const renderengine::LayerSettings*> layers;
renderengine::LayerSettings layer;
+ layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
// Translate one pixel diagonally
layer.geometry.positionTransform = mat4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1);
@@ -663,7 +807,7 @@
layers.push_back(&layer);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers);
}
template <typename SourceVariant>
@@ -677,16 +821,18 @@
}
template <typename SourceVariant>
-void RenderEngineTest::fillBufferWithColorTransform(bool useColorManagement) {
+void RenderEngineTest::fillBufferWithColorTransform() {
renderengine::DisplaySettings settings;
settings.physicalDisplay = fullscreenRect();
settings.clip = Rect(1, 1);
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
std::vector<const renderengine::LayerSettings*> layers;
renderengine::LayerSettings layer;
+ layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
- SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this, useColorManagement);
+ SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this);
layer.alpha = 1.0f;
// construct a fake color matrix
@@ -700,7 +846,7 @@
layers.push_back(&layer);
- invokeDraw(settings, layers, mBuffer, useColorManagement);
+ invokeDraw(settings, layers);
}
template <typename SourceVariant>
@@ -710,12 +856,6 @@
}
template <typename SourceVariant>
-void RenderEngineTest::fillBufferColorTransformCM() {
- fillBufferWithColorTransform<SourceVariant>(true);
- expectBufferColor(fullscreenRect(), 126, 0, 0, 255, 1);
-}
-
-template <typename SourceVariant>
void RenderEngineTest::fillBufferWithColorTransformZeroLayerAlpha() {
renderengine::DisplaySettings settings;
settings.physicalDisplay = fullscreenRect();
@@ -730,16 +870,13 @@
// construct a fake color matrix
// simple inverse color
- settings.colorTransform = mat4(-1, 0, 0, 0,
- 0, -1, 0, 0,
- 0, 0, -1, 0,
- 1, 1, 1, 1);
+ settings.colorTransform = mat4(-1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 1, 1, 1, 1);
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
layers.push_back(&layer);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers);
}
template <typename SourceVariant>
@@ -753,10 +890,12 @@
renderengine::DisplaySettings settings;
settings.physicalDisplay = fullscreenRect();
settings.clip = fullscreenRect();
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
std::vector<const renderengine::LayerSettings*> layers;
renderengine::LayerSettings layer;
+ layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
layer.geometry.boundaries = fullscreenRect().toFloatRect();
layer.geometry.roundedCornersRadius = 5.0f;
layer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
@@ -765,7 +904,7 @@
layers.push_back(&layer);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers);
}
template <typename SourceVariant>
@@ -786,29 +925,25 @@
template <typename SourceVariant>
void RenderEngineTest::fillBufferAndBlurBackground() {
- char value[PROPERTY_VALUE_MAX];
- property_get("ro.surface_flinger.supports_background_blur", value, "0");
- if (!atoi(value)) {
- // This device doesn't support blurs, no-op.
- return;
- }
-
auto blurRadius = 50;
auto center = DEFAULT_DISPLAY_WIDTH / 2;
renderengine::DisplaySettings settings;
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
settings.physicalDisplay = fullscreenRect();
settings.clip = fullscreenRect();
std::vector<const renderengine::LayerSettings*> layers;
renderengine::LayerSettings backgroundLayer;
+ backgroundLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
backgroundLayer.geometry.boundaries = fullscreenRect().toFloatRect();
SourceVariant::fillColor(backgroundLayer, 0.0f, 1.0f, 0.0f, this);
backgroundLayer.alpha = 1.0f;
layers.push_back(&backgroundLayer);
renderengine::LayerSettings leftLayer;
+ leftLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
leftLayer.geometry.boundaries =
Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT).toFloatRect();
SourceVariant::fillColor(leftLayer, 1.0f, 0.0f, 0.0f, this);
@@ -816,17 +951,23 @@
layers.push_back(&leftLayer);
renderengine::LayerSettings blurLayer;
+ blurLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
blurLayer.geometry.boundaries = fullscreenRect().toFloatRect();
blurLayer.backgroundBlurRadius = blurRadius;
+ SourceVariant::fillColor(blurLayer, 0.0f, 0.0f, 1.0f, this);
blurLayer.alpha = 0;
layers.push_back(&blurLayer);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers);
- expectBufferColor(Rect(center - 1, center - 5, center, center + 5), 150, 150, 0, 255,
- 50 /* tolerance */);
- expectBufferColor(Rect(center, center - 5, center + 1, center + 5), 150, 150, 0, 255,
- 50 /* tolerance */);
+ // solid color
+ expectBufferColor(Rect(0, 0, 1, 1), 255, 0, 0, 255, 0 /* tolerance */);
+
+ if (mRE->supportsBackgroundBlur()) {
+ // blurred color (downsampling should result in the center color being close to 128)
+ expectBufferColor(Rect(center - 1, center - 5, center + 1, center + 5), 128, 128, 0, 255,
+ 10 /* tolerance */);
+ }
}
template <typename SourceVariant>
@@ -834,17 +975,19 @@
renderengine::DisplaySettings settings;
settings.physicalDisplay = fullscreenRect();
settings.clip = fullscreenRect();
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
std::vector<const renderengine::LayerSettings*> layersFirst;
renderengine::LayerSettings layerOne;
+ layerOne.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
layerOne.geometry.boundaries =
FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH / 3.0, DEFAULT_DISPLAY_HEIGHT / 3.0);
SourceVariant::fillColor(layerOne, 1.0f, 0.0f, 0.0f, this);
layerOne.alpha = 0.2;
layersFirst.push_back(&layerOne);
- invokeDraw(settings, layersFirst, mBuffer);
+ invokeDraw(settings, layersFirst);
expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3), 51, 0, 0, 51);
expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3 + 1, DEFAULT_DISPLAY_HEIGHT / 3 + 1,
DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
@@ -852,6 +995,7 @@
std::vector<const renderengine::LayerSettings*> layersSecond;
renderengine::LayerSettings layerTwo;
+ layerTwo.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
layerTwo.geometry.boundaries =
FloatRect(DEFAULT_DISPLAY_WIDTH / 3.0, DEFAULT_DISPLAY_HEIGHT / 3.0,
DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT);
@@ -859,7 +1003,7 @@
layerTwo.alpha = 1.0f;
layersSecond.push_back(&layerTwo);
- invokeDraw(settings, layersSecond, mBuffer);
+ invokeDraw(settings, layersSecond);
expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3), 0, 0, 0, 0);
expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3 + 1, DEFAULT_DISPLAY_HEIGHT / 3 + 1,
@@ -871,20 +1015,22 @@
renderengine::DisplaySettings settings;
settings.physicalDisplay = fullscreenRect();
settings.clip = Rect(1, 1);
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
std::vector<const renderengine::LayerSettings*> layers;
renderengine::LayerSettings layer;
+ layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
// Here will allocate a checker board texture, but transform texture
// coordinates so that only the upper left is applied.
- sp<GraphicBuffer> buf = allocateSourceBuffer(2, 2);
+ const auto buf = allocateSourceBuffer(2, 2);
uint32_t texName;
- RenderEngineTest::sRE->genTextures(1, &texName);
+ RenderEngineTest::mRE->genTextures(1, &texName);
this->mTexNames.push_back(texName);
uint8_t* pixels;
- buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
- reinterpret_cast<void**>(&pixels));
+ buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
// Red top left, Green top right, Blue bottom left, Black bottom right
pixels[0] = 255;
pixels[1] = 0;
@@ -898,7 +1044,7 @@
pixels[9] = 0;
pixels[10] = 255;
pixels[11] = 255;
- buf->unlock();
+ buf->getBuffer()->unlock();
layer.source.buffer.buffer = buf;
layer.source.buffer.textureName = texName;
@@ -909,7 +1055,7 @@
layers.push_back(&layer);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers);
}
void RenderEngineTest::fillBufferTextureTransform() {
@@ -926,19 +1072,19 @@
std::vector<const renderengine::LayerSettings*> layers;
renderengine::LayerSettings layer;
- sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
+ const auto buf = allocateSourceBuffer(1, 1);
uint32_t texName;
- RenderEngineTest::sRE->genTextures(1, &texName);
+ RenderEngineTest::mRE->genTextures(1, &texName);
this->mTexNames.push_back(texName);
uint8_t* pixels;
- buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
- reinterpret_cast<void**>(&pixels));
+ buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
pixels[0] = 255;
pixels[1] = 0;
pixels[2] = 0;
pixels[3] = 255;
- buf->unlock();
+ buf->getBuffer()->unlock();
layer.source.buffer.buffer = buf;
layer.source.buffer.textureName = texName;
@@ -948,7 +1094,7 @@
layers.push_back(&layer);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers);
}
void RenderEngineTest::fillBufferWithPremultiplyAlpha() {
@@ -965,19 +1111,19 @@
std::vector<const renderengine::LayerSettings*> layers;
renderengine::LayerSettings layer;
- sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
+ const auto buf = allocateSourceBuffer(1, 1);
uint32_t texName;
- RenderEngineTest::sRE->genTextures(1, &texName);
+ RenderEngineTest::mRE->genTextures(1, &texName);
this->mTexNames.push_back(texName);
uint8_t* pixels;
- buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
- reinterpret_cast<void**>(&pixels));
+ buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
pixels[0] = 255;
pixels[1] = 0;
pixels[2] = 0;
pixels[3] = 255;
- buf->unlock();
+ buf->getBuffer()->unlock();
layer.source.buffer.buffer = buf;
layer.source.buffer.textureName = texName;
@@ -987,7 +1133,7 @@
layers.push_back(&layer);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers);
}
void RenderEngineTest::fillBufferWithoutPremultiplyAlpha() {
@@ -1005,7 +1151,7 @@
// fake layer, without bounds should not render anything
renderengine::LayerSettings layer;
layers.push_back(&layer);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers);
}
void RenderEngineTest::clearRegion() {
@@ -1022,6 +1168,7 @@
const renderengine::ShadowSettings& shadow,
const ubyte4& casterColor, const ubyte4& backgroundColor) {
renderengine::DisplaySettings settings;
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
settings.physicalDisplay = fullscreenRect();
settings.clip = fullscreenRect();
@@ -1029,6 +1176,7 @@
// add background layer
renderengine::LayerSettings bgLayer;
+ bgLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
bgLayer.geometry.boundaries = fullscreenRect().toFloatRect();
ColorSourceVariant::fillColor(bgLayer, backgroundColor.r / 255.0f, backgroundColor.g / 255.0f,
backgroundColor.b / 255.0f, this);
@@ -1037,6 +1185,7 @@
// add shadow layer
renderengine::LayerSettings shadowLayer;
+ shadowLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
shadowLayer.geometry.boundaries = castingLayer.geometry.boundaries;
shadowLayer.alpha = castingLayer.alpha;
shadowLayer.shadow = shadow;
@@ -1044,32 +1193,107 @@
// add layer casting the shadow
renderengine::LayerSettings layer = castingLayer;
+ layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
SourceVariant::fillColor(layer, casterColor.r / 255.0f, casterColor.g / 255.0f,
casterColor.b / 255.0f, this);
layers.push_back(&layer);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers);
}
-TEST_F(RenderEngineTest, drawLayers_noLayersToDraw) {
+void RenderEngineTest::drawShadowWithoutCaster(const FloatRect& castingBounds,
+ const renderengine::ShadowSettings& shadow,
+ const ubyte4& backgroundColor) {
+ renderengine::DisplaySettings settings;
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<const renderengine::LayerSettings*> layers;
+
+ // add background layer
+ renderengine::LayerSettings bgLayer;
+ bgLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+ bgLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+ ColorSourceVariant::fillColor(bgLayer, backgroundColor.r / 255.0f, backgroundColor.g / 255.0f,
+ backgroundColor.b / 255.0f, this);
+ bgLayer.alpha = backgroundColor.a / 255.0f;
+ layers.push_back(&bgLayer);
+
+ // add shadow layer
+ renderengine::LayerSettings shadowLayer;
+ shadowLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+ shadowLayer.geometry.boundaries = castingBounds;
+ shadowLayer.alpha = 1.0f;
+ ColorSourceVariant::fillColor(shadowLayer, 0, 0, 0, this);
+ shadowLayer.shadow = shadow;
+ layers.push_back(&shadowLayer);
+
+ invokeDraw(settings, layers);
+}
+
+INSTANTIATE_TEST_SUITE_P(PerRenderEngineType, RenderEngineTest,
+ testing::Values(std::make_shared<GLESRenderEngineFactory>(),
+ std::make_shared<GLESCMRenderEngineFactory>(),
+ std::make_shared<SkiaGLESRenderEngineFactory>(),
+ std::make_shared<SkiaGLESCMRenderEngineFactory>()));
+
+TEST_P(RenderEngineTest, drawLayers_noLayersToDraw) {
+ initializeRenderEngine();
drawEmptyLayers();
}
-TEST_F(RenderEngineTest, drawLayers_nullOutputBuffer) {
+TEST_P(RenderEngineTest, drawLayers_withoutBuffers_withColorTransform) {
+ initializeRenderEngine();
+
renderengine::DisplaySettings settings;
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ // 255, 255, 255, 255 is full opaque white.
+ const ubyte4 backgroundColor(255.f, 255.f, 255.f, 255.f);
+ // Create layer with given color.
+ renderengine::LayerSettings bgLayer;
+ bgLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+ bgLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+ bgLayer.source.solidColor = half3(backgroundColor.r / 255.0f, backgroundColor.g / 255.0f,
+ backgroundColor.b / 255.0f);
+ bgLayer.alpha = backgroundColor.a / 255.0f;
+ // Transform the red color.
+ bgLayer.colorTransform = mat4(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
+
+ std::vector<const renderengine::LayerSettings*> layers;
+ layers.push_back(&bgLayer);
+
+ invokeDraw(settings, layers);
+
+ // Expect to see full opaque pixel (with inverted red from the transform).
+ expectBufferColor(Rect(0, 0, 10, 10), 0.f, backgroundColor.g, backgroundColor.b,
+ backgroundColor.a);
+}
+
+TEST_P(RenderEngineTest, drawLayers_nullOutputBuffer) {
+ initializeRenderEngine();
+
+ renderengine::DisplaySettings settings;
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
std::vector<const renderengine::LayerSettings*> layers;
renderengine::LayerSettings layer;
layer.geometry.boundaries = fullscreenRect().toFloatRect();
BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
layers.push_back(&layer);
base::unique_fd fence;
- status_t status = sRE->drawLayers(settings, layers, nullptr, true, base::unique_fd(), &fence);
+ status_t status = mRE->drawLayers(settings, layers, nullptr, true, base::unique_fd(), &fence);
ASSERT_EQ(BAD_VALUE, status);
}
-TEST_F(RenderEngineTest, drawLayers_nullOutputFence) {
+TEST_P(RenderEngineTest, drawLayers_nullOutputFence) {
+ initializeRenderEngine();
+
renderengine::DisplaySettings settings;
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
settings.physicalDisplay = fullscreenRect();
settings.clip = fullscreenRect();
@@ -1080,15 +1304,23 @@
layer.alpha = 1.0;
layers.push_back(&layer);
- status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true,
- base::unique_fd(), nullptr);
- sCurrentBuffer = mBuffer;
+ status_t status = mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), nullptr);
ASSERT_EQ(NO_ERROR, status);
expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
}
-TEST_F(RenderEngineTest, drawLayers_doesNotCacheFramebuffer) {
+TEST_P(RenderEngineTest, drawLayers_doesNotCacheFramebuffer) {
+ const auto& renderEngineFactory = GetParam();
+
+ if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+ // GLES-specific test
+ return;
+ }
+
+ initializeRenderEngine();
+
renderengine::DisplaySettings settings;
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
settings.physicalDisplay = fullscreenRect();
settings.clip = fullscreenRect();
@@ -1099,312 +1331,278 @@
layer.alpha = 1.0;
layers.push_back(&layer);
- status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), false,
- base::unique_fd(), nullptr);
- sCurrentBuffer = mBuffer;
+ status_t status = mRE->drawLayers(settings, layers, mBuffer, false, base::unique_fd(), nullptr);
ASSERT_EQ(NO_ERROR, status);
- ASSERT_FALSE(sRE->isFramebufferImageCachedForTesting(mBuffer->getId()));
+ ASSERT_FALSE(mGLESRE->isFramebufferImageCachedForTesting(mBuffer->getBuffer()->getId()));
expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
}
-TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) {
+TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) {
+ initializeRenderEngine();
fillRedBuffer<ColorSourceVariant>();
}
-TEST_F(RenderEngineTest, drawLayers_fillGreenBuffer_colorSource) {
+TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_colorSource) {
+ initializeRenderEngine();
fillGreenBuffer<ColorSourceVariant>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBlueBuffer_colorSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_colorSource) {
+ initializeRenderEngine();
fillBlueBuffer<ColorSourceVariant>();
}
-TEST_F(RenderEngineTest, drawLayers_fillRedTransparentBuffer_colorSource) {
+TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_colorSource) {
+ initializeRenderEngine();
fillRedTransparentBuffer<ColorSourceVariant>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_colorSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_colorSource) {
+ initializeRenderEngine();
fillBufferPhysicalOffset<ColorSourceVariant>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_colorSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_colorSource) {
+ initializeRenderEngine();
fillBufferCheckersRotate0<ColorSourceVariant>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_colorSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_colorSource) {
+ initializeRenderEngine();
fillBufferCheckersRotate90<ColorSourceVariant>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_colorSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_colorSource) {
+ initializeRenderEngine();
fillBufferCheckersRotate180<ColorSourceVariant>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_colorSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_colorSource) {
+ initializeRenderEngine();
fillBufferCheckersRotate270<ColorSourceVariant>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBufferLayerTransform_colorSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_colorSource) {
+ initializeRenderEngine();
fillBufferLayerTransform<ColorSourceVariant>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) {
+ initializeRenderEngine();
fillBufferColorTransform<ColorSourceVariant>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformCM_colorSource) {
- fillBufferColorTransformCM<ColorSourceVariant>();
-}
-
-TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_colorSource) {
- fillBufferColorTransformZeroLayerAlpha<ColorSourceVariant>();
-}
-
-TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) {
+ initializeRenderEngine();
fillBufferWithRoundedCorners<ColorSourceVariant>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_colorSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_colorSource) {
+ initializeRenderEngine();
+ fillBufferColorTransformZeroLayerAlpha<ColorSourceVariant>();
+}
+
+// TODO(b/186010146): reenable once swiftshader is happy with this test
+TEST_P(RenderEngineTest, DISABLED_drawLayers_fillBufferAndBlurBackground_colorSource) {
+ initializeRenderEngine();
fillBufferAndBlurBackground<ColorSourceVariant>();
}
-TEST_F(RenderEngineTest, drawLayers_overlayCorners_colorSource) {
+TEST_P(RenderEngineTest, drawLayers_overlayCorners_colorSource) {
+ initializeRenderEngine();
overlayCorners<ColorSourceVariant>();
}
-TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_opaqueBufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_opaqueBufferSource) {
+ initializeRenderEngine();
fillRedBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
-TEST_F(RenderEngineTest, drawLayers_fillGreenBuffer_opaqueBufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_opaqueBufferSource) {
+ initializeRenderEngine();
fillGreenBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBlueBuffer_opaqueBufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_opaqueBufferSource) {
+ initializeRenderEngine();
fillBlueBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
-TEST_F(RenderEngineTest, drawLayers_fillRedTransparentBuffer_opaqueBufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_opaqueBufferSource) {
+ initializeRenderEngine();
fillRedTransparentBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_opaqueBufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_opaqueBufferSource) {
+ initializeRenderEngine();
fillBufferPhysicalOffset<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_opaqueBufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_opaqueBufferSource) {
+ initializeRenderEngine();
fillBufferCheckersRotate0<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_opaqueBufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_opaqueBufferSource) {
+ initializeRenderEngine();
fillBufferCheckersRotate90<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_opaqueBufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_opaqueBufferSource) {
+ initializeRenderEngine();
fillBufferCheckersRotate180<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_opaqueBufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_opaqueBufferSource) {
+ initializeRenderEngine();
fillBufferCheckersRotate270<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBufferLayerTransform_opaqueBufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_opaqueBufferSource) {
+ initializeRenderEngine();
fillBufferLayerTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) {
+ initializeRenderEngine();
fillBufferColorTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformCM_opaqueBufferSource) {
- fillBufferColorTransformCM<BufferSourceVariant<ForceOpaqueBufferVariant>>();
-}
-
-TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_opaqueBufferSource) {
- fillBufferColorTransformZeroLayerAlpha<BufferSourceVariant<ForceOpaqueBufferVariant>>();
-}
-
-TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) {
+ initializeRenderEngine();
fillBufferWithRoundedCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_opaqueBufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_opaqueBufferSource) {
+ initializeRenderEngine();
+ fillBufferColorTransformZeroLayerAlpha<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+// TODO(b/186010146): reenable once swiftshader is happy with this test
+TEST_P(RenderEngineTest, DISABLED_drawLayers_fillBufferAndBlurBackground_opaqueBufferSource) {
+ initializeRenderEngine();
fillBufferAndBlurBackground<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
-TEST_F(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) {
+TEST_P(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) {
+ initializeRenderEngine();
overlayCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
-TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_bufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_bufferSource) {
+ initializeRenderEngine();
fillRedBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
-TEST_F(RenderEngineTest, drawLayers_fillGreenBuffer_bufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_bufferSource) {
+ initializeRenderEngine();
fillGreenBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBlueBuffer_bufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_bufferSource) {
+ initializeRenderEngine();
fillBlueBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
-TEST_F(RenderEngineTest, drawLayers_fillRedTransparentBuffer_bufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_bufferSource) {
+ initializeRenderEngine();
fillRedTransparentBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_bufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_bufferSource) {
+ initializeRenderEngine();
fillBufferPhysicalOffset<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_bufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_bufferSource) {
+ initializeRenderEngine();
fillBufferCheckersRotate0<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_bufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_bufferSource) {
+ initializeRenderEngine();
fillBufferCheckersRotate90<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_bufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_bufferSource) {
+ initializeRenderEngine();
fillBufferCheckersRotate180<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_bufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_bufferSource) {
+ initializeRenderEngine();
fillBufferCheckersRotate270<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBufferLayerTransform_bufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_bufferSource) {
+ initializeRenderEngine();
fillBufferLayerTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) {
+ initializeRenderEngine();
fillBufferColorTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformCM_bufferSource) {
- fillBufferColorTransformCM<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
-}
-
-TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_bufferSource) {
- fillBufferColorTransformZeroLayerAlpha<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
-}
-
-TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) {
+ initializeRenderEngine();
fillBufferWithRoundedCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_bufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_bufferSource) {
+ initializeRenderEngine();
+ fillBufferColorTransformZeroLayerAlpha<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+// TODO(b/186010146): reenable once swiftshader is happy with this test
+TEST_P(RenderEngineTest, DISABLED_drawLayers_fillBufferAndBlurBackground_bufferSource) {
+ initializeRenderEngine();
fillBufferAndBlurBackground<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
-TEST_F(RenderEngineTest, drawLayers_overlayCorners_bufferSource) {
+TEST_P(RenderEngineTest, drawLayers_overlayCorners_bufferSource) {
+ initializeRenderEngine();
overlayCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
-TEST_F(RenderEngineTest, drawLayers_fillBufferTextureTransform) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferTextureTransform) {
+ initializeRenderEngine();
fillBufferTextureTransform();
}
-TEST_F(RenderEngineTest, drawLayers_fillBuffer_premultipliesAlpha) {
+TEST_P(RenderEngineTest, drawLayers_fillBuffer_premultipliesAlpha) {
+ initializeRenderEngine();
fillBufferWithPremultiplyAlpha();
}
-TEST_F(RenderEngineTest, drawLayers_fillBuffer_withoutPremultiplyingAlpha) {
+TEST_P(RenderEngineTest, drawLayers_fillBuffer_withoutPremultiplyingAlpha) {
+ initializeRenderEngine();
fillBufferWithoutPremultiplyAlpha();
}
-TEST_F(RenderEngineTest, drawLayers_clearRegion) {
+TEST_P(RenderEngineTest, drawLayers_clearRegion) {
+ initializeRenderEngine();
clearRegion();
}
-TEST_F(RenderEngineTest, drawLayers_fillsBufferAndCachesImages) {
- renderengine::DisplaySettings settings;
- settings.physicalDisplay = fullscreenRect();
- settings.clip = fullscreenRect();
+TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) {
+ initializeRenderEngine();
- std::vector<const renderengine::LayerSettings*> layers;
+ const ubyte4 backgroundColor(255, 255, 255, 255);
+ const float shadowLength = 5.0f;
+ Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
+ casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+ renderengine::ShadowSettings settings =
+ getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+ false /* casterIsTranslucent */);
- renderengine::LayerSettings layer;
- layer.geometry.boundaries = fullscreenRect().toFloatRect();
- BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
-
- layers.push_back(&layer);
- invokeDraw(settings, layers, mBuffer);
- uint64_t bufferId = layer.source.buffer.buffer->getId();
- EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
- std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
- sRE->unbindExternalTextureBufferForTesting(bufferId);
- std::lock_guard<std::mutex> lock(barrier->mutex);
- ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
- [&]() REQUIRES(barrier->mutex) {
- return barrier->isOpen;
- }));
- EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
- EXPECT_EQ(NO_ERROR, barrier->result);
+ drawShadowWithoutCaster(casterBounds.toFloatRect(), settings, backgroundColor);
+ expectShadowColorWithoutCaster(casterBounds.toFloatRect(), settings, backgroundColor);
}
-TEST_F(RenderEngineTest, bindExternalBuffer_withNullBuffer) {
- status_t result = sRE->bindExternalTextureBuffer(0, nullptr, nullptr);
- ASSERT_EQ(BAD_VALUE, result);
-}
+TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) {
+ initializeRenderEngine();
-TEST_F(RenderEngineTest, bindExternalBuffer_cachesImages) {
- sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
- uint32_t texName;
- sRE->genTextures(1, &texName);
- mTexNames.push_back(texName);
-
- sRE->bindExternalTextureBuffer(texName, buf, nullptr);
- uint64_t bufferId = buf->getId();
- EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
- std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
- sRE->unbindExternalTextureBufferForTesting(bufferId);
- std::lock_guard<std::mutex> lock(barrier->mutex);
- ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
- [&]() REQUIRES(barrier->mutex) {
- return barrier->isOpen;
- }));
- EXPECT_EQ(NO_ERROR, barrier->result);
- EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
-}
-
-TEST_F(RenderEngineTest, cacheExternalBuffer_withNullBuffer) {
- std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
- sRE->cacheExternalTextureBufferForTesting(nullptr);
- std::lock_guard<std::mutex> lock(barrier->mutex);
- ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
- [&]() REQUIRES(barrier->mutex) {
- return barrier->isOpen;
- }));
- EXPECT_TRUE(barrier->isOpen);
- EXPECT_EQ(BAD_VALUE, barrier->result);
-}
-
-TEST_F(RenderEngineTest, cacheExternalBuffer_cachesImages) {
- sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
- uint64_t bufferId = buf->getId();
- std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
- sRE->cacheExternalTextureBufferForTesting(buf);
- {
- std::lock_guard<std::mutex> lock(barrier->mutex);
- ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
- [&]() REQUIRES(barrier->mutex) {
- return barrier->isOpen;
- }));
- EXPECT_EQ(NO_ERROR, barrier->result);
- }
- EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
- barrier = sRE->unbindExternalTextureBufferForTesting(bufferId);
- {
- std::lock_guard<std::mutex> lock(barrier->mutex);
- ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
- [&]() REQUIRES(barrier->mutex) {
- return barrier->isOpen;
- }));
- EXPECT_EQ(NO_ERROR, barrier->result);
- }
- EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
-}
-
-TEST_F(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) {
const ubyte4 casterColor(255, 0, 0, 255);
const ubyte4 backgroundColor(255, 255, 255, 255);
const float shadowLength = 5.0f;
@@ -1421,13 +1619,16 @@
expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
}
-TEST_F(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) {
+TEST_P(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) {
+ initializeRenderEngine();
+
const ubyte4 casterColor(255, 0, 0, 255);
const ubyte4 backgroundColor(255, 255, 255, 255);
const float shadowLength = 5.0f;
Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
renderengine::LayerSettings castingLayer;
+ castingLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
castingLayer.geometry.boundaries = casterBounds.toFloatRect();
castingLayer.alpha = 1.0f;
renderengine::ShadowSettings settings =
@@ -1438,13 +1639,16 @@
expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
}
-TEST_F(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) {
+TEST_P(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) {
+ initializeRenderEngine();
+
const ubyte4 casterColor(255, 0, 0, 255);
const ubyte4 backgroundColor(255, 255, 255, 255);
const float shadowLength = 5.0f;
Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
renderengine::LayerSettings castingLayer;
+ castingLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
castingLayer.geometry.boundaries = casterBounds.toFloatRect();
castingLayer.alpha = 1.0f;
renderengine::ShadowSettings settings =
@@ -1456,7 +1660,9 @@
expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
}
-TEST_F(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) {
+TEST_P(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) {
+ initializeRenderEngine();
+
const ubyte4 casterColor(255, 0, 0, 255);
const ubyte4 backgroundColor(255, 255, 255, 255);
const float shadowLength = 5.0f;
@@ -1476,7 +1682,9 @@
expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
}
-TEST_F(RenderEngineTest, drawLayers_fillShadow_translucentCasterWithAlpha) {
+TEST_P(RenderEngineTest, drawLayers_fillShadow_translucentCasterWithAlpha) {
+ initializeRenderEngine();
+
const ubyte4 casterColor(255, 0, 0, 255);
const ubyte4 backgroundColor(255, 255, 255, 255);
const float shadowLength = 5.0f;
@@ -1501,10 +1709,20 @@
backgroundColor.a);
}
-TEST_F(RenderEngineTest, cleanupPostRender_cleansUpOnce) {
+TEST_P(RenderEngineTest, cleanupPostRender_cleansUpOnce) {
+ const auto& renderEngineFactory = GetParam();
+
+ if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+ // GLES-specific test
+ return;
+ }
+
+ initializeRenderEngine();
+
renderengine::DisplaySettings settings;
settings.physicalDisplay = fullscreenRect();
settings.clip = fullscreenRect();
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
std::vector<const renderengine::LayerSettings*> layers;
renderengine::LayerSettings layer;
@@ -1514,25 +1732,33 @@
layers.push_back(&layer);
base::unique_fd fenceOne;
- sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true, base::unique_fd(),
- &fenceOne);
+ mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fenceOne);
base::unique_fd fenceTwo;
- sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true, std::move(fenceOne),
- &fenceTwo);
+ mRE->drawLayers(settings, layers, mBuffer, true, std::move(fenceOne), &fenceTwo);
const int fd = fenceTwo.get();
if (fd >= 0) {
sync_wait(fd, -1);
}
// Only cleanup the first time.
- EXPECT_TRUE(sRE->cleanupPostRender(
+ EXPECT_TRUE(mRE->cleanupPostRender(
renderengine::RenderEngine::CleanupMode::CLEAN_OUTPUT_RESOURCES));
- EXPECT_FALSE(sRE->cleanupPostRender(
+ EXPECT_FALSE(mRE->cleanupPostRender(
renderengine::RenderEngine::CleanupMode::CLEAN_OUTPUT_RESOURCES));
}
-TEST_F(RenderEngineTest, cleanupPostRender_whenCleaningAll_replacesTextureMemory) {
+TEST_P(RenderEngineTest, cleanupPostRender_whenCleaningAll_replacesTextureMemory) {
+ const auto& renderEngineFactory = GetParam();
+
+ if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+ // GLES-specific test
+ return;
+ }
+
+ initializeRenderEngine();
+
renderengine::DisplaySettings settings;
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
settings.physicalDisplay = fullscreenRect();
settings.clip = fullscreenRect();
@@ -1544,27 +1770,153 @@
layers.push_back(&layer);
base::unique_fd fence;
- sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true, base::unique_fd(), &fence);
+ mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fence);
const int fd = fence.get();
if (fd >= 0) {
sync_wait(fd, -1);
}
- uint64_t bufferId = layer.source.buffer.buffer->getId();
+ uint64_t bufferId = layer.source.buffer.buffer->getBuffer()->getId();
uint32_t texName = layer.source.buffer.textureName;
- EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
- EXPECT_EQ(bufferId, sRE->getBufferIdForTextureNameForTesting(texName));
+ EXPECT_TRUE(mGLESRE->isImageCachedForTesting(bufferId));
+ EXPECT_EQ(bufferId, mGLESRE->getBufferIdForTextureNameForTesting(texName));
- EXPECT_TRUE(sRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL));
+ EXPECT_TRUE(mRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL));
// Now check that our view of memory is good.
- EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
- EXPECT_EQ(std::nullopt, sRE->getBufferIdForTextureNameForTesting(bufferId));
- EXPECT_TRUE(sRE->isTextureNameKnownForTesting(texName));
+ EXPECT_FALSE(mGLESRE->isImageCachedForTesting(bufferId));
+ EXPECT_EQ(std::nullopt, mGLESRE->getBufferIdForTextureNameForTesting(bufferId));
+ EXPECT_TRUE(mGLESRE->isTextureNameKnownForTesting(texName));
+}
+
+TEST_P(RenderEngineTest, testRoundedCornersCrop) {
+ initializeRenderEngine();
+
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+
+ std::vector<const renderengine::LayerSettings*> layers;
+
+ renderengine::LayerSettings redLayer;
+ redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+ redLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+ redLayer.geometry.roundedCornersRadius = 5.0f;
+ redLayer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
+ // Red background.
+ redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
+ redLayer.alpha = 1.0f;
+
+ layers.push_back(&redLayer);
+
+ // Green layer with 1/3 size.
+ renderengine::LayerSettings greenLayer;
+ greenLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+ greenLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+ greenLayer.geometry.roundedCornersRadius = 5.0f;
+ // Bottom right corner is not going to be rounded.
+ greenLayer.geometry.roundedCornersCrop =
+ Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3, DEFAULT_DISPLAY_HEIGHT,
+ DEFAULT_DISPLAY_HEIGHT)
+ .toFloatRect();
+ greenLayer.source.solidColor = half3(0.0f, 1.0f, 0.0f);
+ greenLayer.alpha = 1.0f;
+
+ layers.push_back(&greenLayer);
+
+ invokeDraw(settings, layers);
+
+ // Corners should be ignored...
+ // Screen size: width is 128, height is 256.
+ expectBufferColor(Rect(0, 0, 1, 1), 0, 0, 0, 0);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH - 1, 0, DEFAULT_DISPLAY_WIDTH, 1), 0, 0, 0, 0);
+ expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT - 1, 1, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 0);
+ // Bottom right corner is kept out of the clipping, and it's green.
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH - 1, DEFAULT_DISPLAY_HEIGHT - 1,
+ DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ 0, 255, 0, 255);
+}
+
+TEST_P(RenderEngineTest, testClear) {
+ initializeRenderEngine();
+
+ const auto rect = fullscreenRect();
+ const renderengine::DisplaySettings display{
+ .physicalDisplay = rect,
+ .clip = rect,
+ };
+
+ const renderengine::LayerSettings redLayer{
+ .geometry.boundaries = rect.toFloatRect(),
+ .source.solidColor = half3(1.0f, 0.0f, 0.0f),
+ .alpha = 1.0f,
+ };
+
+ // This mimics prepareClearClientComposition. This layer should overwrite
+ // the redLayer, so that the buffer is transparent, rather than red.
+ const renderengine::LayerSettings clearLayer{
+ .geometry.boundaries = rect.toFloatRect(),
+ .source.solidColor = half3(0.0f, 0.0f, 0.0f),
+ .alpha = 0.0f,
+ .disableBlending = true,
+ };
+
+ std::vector<const renderengine::LayerSettings*> layers{&redLayer, &clearLayer};
+ invokeDraw(display, layers);
+ expectBufferColor(rect, 0, 0, 0, 0);
+}
+
+TEST_P(RenderEngineTest, testDisableBlendingBuffer) {
+ initializeRenderEngine();
+
+ const auto rect = Rect(0, 0, 1, 1);
+ const renderengine::DisplaySettings display{
+ .physicalDisplay = rect,
+ .clip = rect,
+ };
+
+ const renderengine::LayerSettings redLayer{
+ .geometry.boundaries = rect.toFloatRect(),
+ .source.solidColor = half3(1.0f, 0.0f, 0.0f),
+ .alpha = 1.0f,
+ };
+
+ // The next layer will overwrite redLayer with a GraphicBuffer that is green
+ // applied with a translucent alpha.
+ const auto buf = allocateSourceBuffer(1, 1);
+ {
+ uint8_t* pixels;
+ buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
+ pixels[0] = 0;
+ pixels[1] = 255;
+ pixels[2] = 0;
+ pixels[3] = 255;
+ buf->getBuffer()->unlock();
+ }
+
+ const renderengine::LayerSettings greenLayer{
+ .geometry.boundaries = rect.toFloatRect(),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = buf,
+ .usePremultipliedAlpha = true,
+ },
+ },
+ .alpha = 0.5f,
+ .disableBlending = true,
+ };
+
+ std::vector<const renderengine::LayerSettings*> layers{&redLayer, &greenLayer};
+ invokeDraw(display, layers);
+ expectBufferColor(rect, 0, 128, 0, 128);
}
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
new file mode 100644
index 0000000..e3917cc
--- /dev/null
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2020 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 <cutils/properties.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <renderengine/mock/RenderEngine.h>
+#include "../threaded/RenderEngineThreaded.h"
+
+namespace android {
+
+using testing::_;
+using testing::Eq;
+using testing::Mock;
+using testing::Return;
+
+struct RenderEngineThreadedTest : public ::testing::Test {
+ ~RenderEngineThreadedTest() {}
+
+ void SetUp() override {
+ mThreadedRE = renderengine::threaded::RenderEngineThreaded::create(
+ [this]() { return std::unique_ptr<renderengine::RenderEngine>(mRenderEngine); },
+ renderengine::RenderEngine::RenderEngineType::THREADED);
+ }
+
+ std::unique_ptr<renderengine::threaded::RenderEngineThreaded> mThreadedRE;
+ renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
+};
+
+TEST_F(RenderEngineThreadedTest, dump) {
+ std::string testString = "XYZ";
+ EXPECT_CALL(*mRenderEngine, dump(_));
+ mThreadedRE->dump(testString);
+}
+
+TEST_F(RenderEngineThreadedTest, primeCache) {
+ EXPECT_CALL(*mRenderEngine, primeCache());
+ mThreadedRE->primeCache();
+ // need to call ANY synchronous function after primeCache to ensure that primeCache has
+ // completed asynchronously before the test completes execution.
+ mThreadedRE->getContextPriority();
+}
+
+TEST_F(RenderEngineThreadedTest, genTextures) {
+ uint32_t texName;
+ EXPECT_CALL(*mRenderEngine, genTextures(1, &texName));
+ mThreadedRE->genTextures(1, &texName);
+}
+
+TEST_F(RenderEngineThreadedTest, deleteTextures) {
+ uint32_t texName;
+ EXPECT_CALL(*mRenderEngine, deleteTextures(1, &texName));
+ mThreadedRE->deleteTextures(1, &texName);
+}
+
+TEST_F(RenderEngineThreadedTest, getMaxTextureSize_returns20) {
+ size_t size = 20;
+ EXPECT_CALL(*mRenderEngine, getMaxTextureSize()).WillOnce(Return(size));
+ size_t result = mThreadedRE->getMaxTextureSize();
+ ASSERT_EQ(size, result);
+}
+
+TEST_F(RenderEngineThreadedTest, getMaxTextureSize_returns0) {
+ size_t size = 0;
+ EXPECT_CALL(*mRenderEngine, getMaxTextureSize()).WillOnce(Return(size));
+ size_t result = mThreadedRE->getMaxTextureSize();
+ ASSERT_EQ(size, result);
+}
+
+TEST_F(RenderEngineThreadedTest, getMaxViewportDims_returns20) {
+ size_t dims = 20;
+ EXPECT_CALL(*mRenderEngine, getMaxViewportDims()).WillOnce(Return(dims));
+ size_t result = mThreadedRE->getMaxViewportDims();
+ ASSERT_EQ(dims, result);
+}
+
+TEST_F(RenderEngineThreadedTest, getMaxViewportDims_returns0) {
+ size_t dims = 0;
+ EXPECT_CALL(*mRenderEngine, getMaxViewportDims()).WillOnce(Return(dims));
+ size_t result = mThreadedRE->getMaxViewportDims();
+ ASSERT_EQ(dims, result);
+}
+
+TEST_F(RenderEngineThreadedTest, isProtected_returnsFalse) {
+ EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(false));
+ status_t result = mThreadedRE->isProtected();
+ ASSERT_EQ(false, result);
+}
+
+TEST_F(RenderEngineThreadedTest, isProtected_returnsTrue) {
+ EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(true));
+ size_t result = mThreadedRE->isProtected();
+ ASSERT_EQ(true, result);
+}
+
+TEST_F(RenderEngineThreadedTest, supportsProtectedContent_returnsFalse) {
+ EXPECT_CALL(*mRenderEngine, supportsProtectedContent()).WillOnce(Return(false));
+ status_t result = mThreadedRE->supportsProtectedContent();
+ ASSERT_EQ(false, result);
+}
+
+TEST_F(RenderEngineThreadedTest, supportsProtectedContent_returnsTrue) {
+ EXPECT_CALL(*mRenderEngine, supportsProtectedContent()).WillOnce(Return(true));
+ status_t result = mThreadedRE->supportsProtectedContent();
+ ASSERT_EQ(true, result);
+}
+
+TEST_F(RenderEngineThreadedTest, useProtectedContext_returnsFalse) {
+ EXPECT_CALL(*mRenderEngine, useProtectedContext(false)).WillOnce(Return(false));
+ status_t result = mThreadedRE->useProtectedContext(false);
+ ASSERT_EQ(false, result);
+}
+
+TEST_F(RenderEngineThreadedTest, useProtectedContext_returnsTrue) {
+ EXPECT_CALL(*mRenderEngine, useProtectedContext(false)).WillOnce(Return(true));
+ status_t result = mThreadedRE->useProtectedContext(false);
+ ASSERT_EQ(true, result);
+}
+
+TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsFalse) {
+ EXPECT_CALL(*mRenderEngine,
+ cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL))
+ .WillOnce(Return(false));
+ status_t result =
+ mThreadedRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL);
+ ASSERT_EQ(false, result);
+}
+
+TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsTrue) {
+ EXPECT_CALL(*mRenderEngine,
+ cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL))
+ .WillOnce(Return(true));
+ status_t result =
+ mThreadedRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL);
+ ASSERT_EQ(true, result);
+}
+
+TEST_F(RenderEngineThreadedTest, supportsBackgroundBlur_returnsFalse) {
+ EXPECT_CALL(*mRenderEngine, supportsBackgroundBlur()).WillOnce(Return(false));
+ status_t result = mThreadedRE->supportsBackgroundBlur();
+ ASSERT_EQ(false, result);
+}
+
+TEST_F(RenderEngineThreadedTest, supportsBackgroundBlur_returnsTrue) {
+ EXPECT_CALL(*mRenderEngine, supportsBackgroundBlur()).WillOnce(Return(true));
+ status_t result = mThreadedRE->supportsBackgroundBlur();
+ ASSERT_EQ(true, result);
+}
+TEST_F(RenderEngineThreadedTest, drawLayers) {
+ renderengine::DisplaySettings settings;
+ std::vector<const renderengine::LayerSettings*> layers;
+ std::shared_ptr<renderengine::ExternalTexture> buffer = std::make_shared<
+ renderengine::ExternalTexture>(new GraphicBuffer(), *mRenderEngine,
+ renderengine::ExternalTexture::Usage::READABLE |
+ renderengine::ExternalTexture::Usage::WRITEABLE);
+ base::unique_fd bufferFence;
+ base::unique_fd drawFence;
+
+ EXPECT_CALL(*mRenderEngine, drawLayers)
+ .WillOnce([](const renderengine::DisplaySettings&,
+ const std::vector<const renderengine::LayerSettings*>&,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&, base::unique_fd*) -> status_t { return NO_ERROR; });
+
+ status_t result = mThreadedRE->drawLayers(settings, layers, buffer, false,
+ std::move(bufferFence), &drawFence);
+ ASSERT_EQ(NO_ERROR, result);
+}
+
+} // namespace android
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
new file mode 100644
index 0000000..c87a836
--- /dev/null
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -0,0 +1,314 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "RenderEngineThreaded.h"
+
+#include <sched.h>
+#include <chrono>
+#include <future>
+
+#include <android-base/stringprintf.h>
+#include <private/gui/SyncFeatures.h>
+#include <utils/Trace.h>
+
+#include "gl/GLESRenderEngine.h"
+
+using namespace std::chrono_literals;
+
+namespace android {
+namespace renderengine {
+namespace threaded {
+
+std::unique_ptr<RenderEngineThreaded> RenderEngineThreaded::create(CreateInstanceFactory factory,
+ RenderEngineType type) {
+ return std::make_unique<RenderEngineThreaded>(std::move(factory), type);
+}
+
+RenderEngineThreaded::RenderEngineThreaded(CreateInstanceFactory factory, RenderEngineType type)
+ : RenderEngine(type) {
+ ATRACE_CALL();
+
+ std::lock_guard lockThread(mThreadMutex);
+ mThread = std::thread(&RenderEngineThreaded::threadMain, this, factory);
+}
+
+RenderEngineThreaded::~RenderEngineThreaded() {
+ {
+ std::lock_guard lock(mThreadMutex);
+ mRunning = false;
+ mCondition.notify_one();
+ }
+
+ if (mThread.joinable()) {
+ mThread.join();
+ }
+}
+
+// NO_THREAD_SAFETY_ANALYSIS is because std::unique_lock presently lacks thread safety annotations.
+void RenderEngineThreaded::threadMain(CreateInstanceFactory factory) NO_THREAD_SAFETY_ANALYSIS {
+ ATRACE_CALL();
+
+ struct sched_param param = {0};
+ param.sched_priority = 2;
+ if (sched_setscheduler(0, SCHED_FIFO, ¶m) != 0) {
+ ALOGE("Couldn't set SCHED_FIFO");
+ }
+
+ mRenderEngine = factory();
+
+ std::unique_lock<std::mutex> lock(mThreadMutex);
+ pthread_setname_np(pthread_self(), mThreadName);
+
+ {
+ std::unique_lock<std::mutex> lock(mInitializedMutex);
+ mIsInitialized = true;
+ }
+ mInitializedCondition.notify_all();
+
+ while (mRunning) {
+ if (!mFunctionCalls.empty()) {
+ auto task = mFunctionCalls.front();
+ mFunctionCalls.pop();
+ task(*mRenderEngine);
+ }
+ mCondition.wait(lock, [this]() REQUIRES(mThreadMutex) {
+ return !mRunning || !mFunctionCalls.empty();
+ });
+ }
+
+ // we must release the RenderEngine on the thread that created it
+ mRenderEngine.reset();
+}
+
+void RenderEngineThreaded::waitUntilInitialized() const {
+ std::unique_lock<std::mutex> lock(mInitializedMutex);
+ mInitializedCondition.wait(lock, [=] { return mIsInitialized; });
+}
+
+void RenderEngineThreaded::primeCache() {
+ // This function is designed so it can run asynchronously, so we do not need to wait
+ // for the futures.
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::primeCache");
+ instance.primeCache();
+ });
+ }
+ mCondition.notify_one();
+}
+
+void RenderEngineThreaded::dump(std::string& result) {
+ std::promise<std::string> resultPromise;
+ std::future<std::string> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, &result](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::dump");
+ std::string localResult = result;
+ instance.dump(localResult);
+ resultPromise.set_value(std::move(localResult));
+ });
+ }
+ mCondition.notify_one();
+ // Note: This is an rvalue.
+ result.assign(resultFuture.get());
+}
+
+void RenderEngineThreaded::genTextures(size_t count, uint32_t* names) {
+ std::promise<void> resultPromise;
+ std::future<void> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, count, names](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::genTextures");
+ instance.genTextures(count, names);
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ resultFuture.wait();
+}
+
+void RenderEngineThreaded::deleteTextures(size_t count, uint32_t const* names) {
+ std::promise<void> resultPromise;
+ std::future<void> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, count, &names](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::deleteTextures");
+ instance.deleteTextures(count, names);
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ resultFuture.wait();
+}
+
+void RenderEngineThreaded::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
+ bool isRenderable) {
+ // This function is designed so it can run asynchronously, so we do not need to wait
+ // for the futures.
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([=](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::mapExternalTextureBuffer");
+ instance.mapExternalTextureBuffer(buffer, isRenderable);
+ });
+ }
+ mCondition.notify_one();
+}
+
+void RenderEngineThreaded::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+ // This function is designed so it can run asynchronously, so we do not need to wait
+ // for the futures.
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([=](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::unmapExternalTextureBuffer");
+ instance.unmapExternalTextureBuffer(buffer);
+ });
+ }
+ mCondition.notify_one();
+}
+
+size_t RenderEngineThreaded::getMaxTextureSize() const {
+ waitUntilInitialized();
+ return mRenderEngine->getMaxTextureSize();
+}
+
+size_t RenderEngineThreaded::getMaxViewportDims() const {
+ waitUntilInitialized();
+ return mRenderEngine->getMaxViewportDims();
+}
+
+bool RenderEngineThreaded::isProtected() const {
+ waitUntilInitialized();
+ // ensure that useProtectedContext is not currently being changed by some
+ // other thread.
+ std::lock_guard lock(mThreadMutex);
+ return mRenderEngine->isProtected();
+}
+
+bool RenderEngineThreaded::supportsProtectedContent() const {
+ waitUntilInitialized();
+ return mRenderEngine->supportsProtectedContent();
+}
+
+bool RenderEngineThreaded::useProtectedContext(bool useProtectedContext) {
+ std::promise<bool> resultPromise;
+ std::future<bool> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push(
+ [&resultPromise, useProtectedContext](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::useProtectedContext");
+ bool returnValue = instance.useProtectedContext(useProtectedContext);
+ resultPromise.set_value(returnValue);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+bool RenderEngineThreaded::cleanupPostRender(CleanupMode mode) {
+ std::promise<bool> resultPromise;
+ std::future<bool> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, mode](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::cleanupPostRender");
+ bool returnValue = instance.cleanupPostRender(mode);
+ resultPromise.set_value(returnValue);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+status_t RenderEngineThreaded::drawLayers(const DisplaySettings& display,
+ const std::vector<const LayerSettings*>& layers,
+ const std::shared_ptr<ExternalTexture>& buffer,
+ const bool useFramebufferCache,
+ base::unique_fd&& bufferFence,
+ base::unique_fd* drawFence) {
+ std::promise<status_t> resultPromise;
+ std::future<status_t> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, &display, &layers, &buffer, useFramebufferCache,
+ &bufferFence, &drawFence](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::drawLayers");
+ status_t status = instance.drawLayers(display, layers, buffer, useFramebufferCache,
+ std::move(bufferFence), drawFence);
+ resultPromise.set_value(status);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+void RenderEngineThreaded::cleanFramebufferCache() {
+ // This function is designed so it can run asynchronously, so we do not need to wait
+ // for the futures.
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::cleanFramebufferCache");
+ instance.cleanFramebufferCache();
+ });
+ }
+ mCondition.notify_one();
+}
+
+int RenderEngineThreaded::getContextPriority() {
+ std::promise<int> resultPromise;
+ std::future<int> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::getContextPriority");
+ int priority = instance.getContextPriority();
+ resultPromise.set_value(priority);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+bool RenderEngineThreaded::supportsBackgroundBlur() {
+ waitUntilInitialized();
+ return mRenderEngine->supportsBackgroundBlur();
+}
+
+void RenderEngineThreaded::onPrimaryDisplaySizeChanged(ui::Size size) {
+ // This function is designed so it can run asynchronously, so we do not need to wait
+ // for the futures.
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([size](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::onPrimaryDisplaySizeChanged");
+ instance.onPrimaryDisplaySizeChanged(size);
+ });
+ }
+ mCondition.notify_one();
+}
+
+} // namespace threaded
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
new file mode 100644
index 0000000..eb6098e
--- /dev/null
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <android-base/thread_annotations.h>
+#include <condition_variable>
+#include <mutex>
+#include <queue>
+#include <thread>
+
+#include "renderengine/RenderEngine.h"
+
+namespace android {
+namespace renderengine {
+namespace threaded {
+
+using CreateInstanceFactory = std::function<std::unique_ptr<renderengine::RenderEngine>()>;
+
+/**
+ * This class extends a basic RenderEngine class. It contains a thread. Each time a function of
+ * this class is called, we create a lambda function that is put on a queue. The main thread then
+ * executes the functions in order.
+ */
+class RenderEngineThreaded : public RenderEngine {
+public:
+ static std::unique_ptr<RenderEngineThreaded> create(CreateInstanceFactory factory,
+ RenderEngineType type);
+
+ RenderEngineThreaded(CreateInstanceFactory factory, RenderEngineType type);
+ ~RenderEngineThreaded() override;
+ void primeCache() override;
+
+ void dump(std::string& result) override;
+
+ void genTextures(size_t count, uint32_t* names) override;
+ void deleteTextures(size_t count, uint32_t const* names) override;
+ size_t getMaxTextureSize() const override;
+ size_t getMaxViewportDims() const override;
+
+ bool isProtected() const override;
+ bool supportsProtectedContent() const override;
+ bool useProtectedContext(bool useProtectedContext) override;
+ bool cleanupPostRender(CleanupMode mode) override;
+
+ status_t drawLayers(const DisplaySettings& display,
+ const std::vector<const LayerSettings*>& layers,
+ const std::shared_ptr<ExternalTexture>& buffer,
+ const bool useFramebufferCache, base::unique_fd&& bufferFence,
+ base::unique_fd* drawFence) override;
+
+ void cleanFramebufferCache() override;
+ int getContextPriority() override;
+ bool supportsBackgroundBlur() override;
+ void onPrimaryDisplaySizeChanged(ui::Size size) override;
+
+protected:
+ void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override;
+ void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
+
+private:
+ void threadMain(CreateInstanceFactory factory);
+ void waitUntilInitialized() const;
+
+ /* ------------------------------------------------------------------------
+ * Threading
+ */
+ const char* const mThreadName = "RenderEngine";
+ // Protects the creation and destruction of mThread.
+ mutable std::mutex mThreadMutex;
+ std::thread mThread GUARDED_BY(mThreadMutex);
+ bool mRunning GUARDED_BY(mThreadMutex) = true;
+ mutable std::queue<std::function<void(renderengine::RenderEngine& instance)>> mFunctionCalls
+ GUARDED_BY(mThreadMutex);
+ mutable std::condition_variable mCondition;
+
+ // Used to allow select thread safe methods to be accessed without requiring the
+ // method to be invoked on the RenderEngine thread
+ bool mIsInitialized = false;
+ mutable std::mutex mInitializedMutex;
+ mutable std::condition_variable mInitializedCondition;
+
+ /* ------------------------------------------------------------------------
+ * Render Engine
+ */
+ std::unique_ptr<renderengine::RenderEngine> mRenderEngine;
+};
+} // namespace threaded
+} // namespace renderengine
+} // namespace android
diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp
index 8ed09f8..a6cacad 100644
--- a/libs/sensor/ISensorServer.cpp
+++ b/libs/sensor/ISensorServer.cpp
@@ -91,13 +91,14 @@
}
virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName,
- int mode, const String16& opPackageName)
+ int mode, const String16& opPackageName, const String16& attributionTag)
{
Parcel data, reply;
data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
data.writeString8(packageName);
data.writeInt32(mode);
data.writeString16(opPackageName);
+ data.writeString16(attributionTag);
remote()->transact(CREATE_SENSOR_EVENT_CONNECTION, data, &reply);
return interface_cast<ISensorEventConnection>(reply.readStrongBinder());
}
@@ -170,8 +171,9 @@
String8 packageName = data.readString8();
int32_t mode = data.readInt32();
const String16& opPackageName = data.readString16();
+ const String16& attributionTag = data.readString16();
sp<ISensorEventConnection> connection(createSensorEventConnection(packageName, mode,
- opPackageName));
+ opPackageName, attributionTag));
reply->writeStrongBinder(IInterface::asBinder(connection));
return NO_ERROR;
}
@@ -216,14 +218,25 @@
int32_t type;
Vector<float> floats;
Vector<int32_t> ints;
+ uint32_t count;
handle = data.readInt32();
type = data.readInt32();
- floats.resize(data.readUint32());
+
+ count = data.readUint32();
+ if (count > (data.dataAvail() / sizeof(float))) {
+ return BAD_VALUE;
+ }
+ floats.resize(count);
for (auto &i : floats) {
i = data.readFloat();
}
- ints.resize(data.readUint32());
+
+ count = data.readUint32();
+ if (count > (data.dataAvail() / sizeof(int32_t))) {
+ return BAD_VALUE;
+ }
+ ints.resize(count);
for (auto &i : ints) {
i = data.readInt32();
}
diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp
index 9d817ae..240738d 100644
--- a/libs/sensor/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -468,6 +468,19 @@
mUuid.i64[1] = 0;
}
+void Sensor::capMinDelayMicros(int32_t cappedMinDelay) {
+ if (mMinDelay < cappedMinDelay) {
+ mMinDelay = cappedMinDelay;
+ }
+}
+
+void Sensor::capHighestDirectReportRateLevel(int32_t cappedRateLevel) {
+ if (cappedRateLevel < getHighestDirectReportRateLevel()) {
+ mFlags &= ~SENSOR_FLAG_MASK_DIRECT_REPORT;
+ mFlags |= cappedRateLevel << SENSOR_FLAG_SHIFT_DIRECT_REPORT;
+ }
+}
+
int32_t Sensor::getId() const {
return int32_t(mUuid.i64[0]);
}
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index a4a5d13..62f4b4e 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -225,13 +225,14 @@
return nullptr;
}
-sp<SensorEventQueue> SensorManager::createEventQueue(String8 packageName, int mode) {
+sp<SensorEventQueue> SensorManager::createEventQueue(
+ String8 packageName, int mode, String16 attributionTag) {
sp<SensorEventQueue> queue;
Mutex::Autolock _l(mLock);
while (assertStateLocked() == NO_ERROR) {
- sp<ISensorEventConnection> connection =
- mSensorServer->createSensorEventConnection(packageName, mode, mOpPackageName);
+ sp<ISensorEventConnection> connection = mSensorServer->createSensorEventConnection(
+ packageName, mode, mOpPackageName, attributionTag);
if (connection == nullptr) {
// SensorService just died or the app doesn't have required permissions.
ALOGE("createEventQueue: connection is NULL.");
diff --git a/libs/sensor/include/sensor/ISensorServer.h b/libs/sensor/include/sensor/ISensorServer.h
index 402678f..ce5c672 100644
--- a/libs/sensor/include/sensor/ISensorServer.h
+++ b/libs/sensor/include/sensor/ISensorServer.h
@@ -45,7 +45,7 @@
virtual Vector<Sensor> getDynamicSensorList(const String16& opPackageName) = 0;
virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName,
- int mode, const String16& opPackageName) = 0;
+ int mode, const String16& opPackageName, const String16& attributionTag) = 0;
virtual int32_t isDataInjectionEnabled() = 0;
virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName,
diff --git a/libs/sensor/include/sensor/Sensor.h b/libs/sensor/include/sensor/Sensor.h
index 324d443..374b68f 100644
--- a/libs/sensor/include/sensor/Sensor.h
+++ b/libs/sensor/include/sensor/Sensor.h
@@ -104,6 +104,9 @@
int32_t getId() const;
void setId(int32_t id);
+ void capMinDelayMicros(int32_t cappedMinDelay);
+ void capHighestDirectReportRateLevel(int32_t cappedRateLevel);
+
// LightFlattenable protocol
inline bool isFixedSize() const { return false; }
size_t getFlattenedSize() const;
diff --git a/libs/sensor/include/sensor/SensorManager.h b/libs/sensor/include/sensor/SensorManager.h
index f09c9c6..09ac7ed 100644
--- a/libs/sensor/include/sensor/SensorManager.h
+++ b/libs/sensor/include/sensor/SensorManager.h
@@ -59,7 +59,8 @@
ssize_t getSensorList(Sensor const* const** list);
ssize_t getDynamicSensorList(Vector<Sensor>& list);
Sensor const* getDefaultSensor(int type);
- sp<SensorEventQueue> createEventQueue(String8 packageName = String8(""), int mode = 0);
+ sp<SensorEventQueue> createEventQueue(
+ String8 packageName = String8(""), int mode = 0, String16 attributionTag = String16(""));
bool isDataInjectionEnabled();
int createDirectChannel(size_t size, int channelType, const native_handle_t *channelData);
void destroyDirectChannel(int channelNativeHandle);
diff --git a/libs/sensorprivacy/SensorPrivacyManager.cpp b/libs/sensorprivacy/SensorPrivacyManager.cpp
index f973cba..b3537ce 100644
--- a/libs/sensorprivacy/SensorPrivacyManager.cpp
+++ b/libs/sensorprivacy/SensorPrivacyManager.cpp
@@ -55,6 +55,22 @@
return service;
}
+bool SensorPrivacyManager::supportsSensorToggle(int sensor) {
+ if (mSupportedCache.find(sensor) == mSupportedCache.end()) {
+ sp<hardware::ISensorPrivacyManager> service = getService();
+ if (service != nullptr) {
+ bool result;
+ service->supportsSensorToggle(sensor, &result);
+ mSupportedCache[sensor] = result;
+ return result;
+ }
+ // if the SensorPrivacyManager is not available then assume sensor privacy feature isn't
+ // supported
+ return false;
+ }
+ return mSupportedCache[sensor];
+}
+
void SensorPrivacyManager::addSensorPrivacyListener(
const sp<hardware::ISensorPrivacyListener>& listener)
{
@@ -64,6 +80,17 @@
}
}
+status_t SensorPrivacyManager::addIndividualSensorPrivacyListener(int userId, int sensor,
+ const sp<hardware::ISensorPrivacyListener>& listener)
+{
+ sp<hardware::ISensorPrivacyManager> service = getService();
+ if (service != nullptr) {
+ return service->addIndividualSensorPrivacyListener(userId, sensor, listener)
+ .transactionError();
+ }
+ return UNEXPECTED_NULL;
+}
+
void SensorPrivacyManager::removeSensorPrivacyListener(
const sp<hardware::ISensorPrivacyListener>& listener)
{
@@ -85,6 +112,31 @@
return false;
}
+bool SensorPrivacyManager::isIndividualSensorPrivacyEnabled(int userId, int sensor)
+{
+ sp<hardware::ISensorPrivacyManager> service = getService();
+ if (service != nullptr) {
+ bool result;
+ service->isIndividualSensorPrivacyEnabled(userId, sensor, &result);
+ return result;
+ }
+ // if the SensorPrivacyManager is not available then assume sensor privacy is disabled
+ return false;
+}
+
+status_t SensorPrivacyManager::isIndividualSensorPrivacyEnabled(int userId, int sensor,
+ bool &returnVal)
+{
+ sp<hardware::ISensorPrivacyManager> service = getService();
+ if (service != nullptr) {
+ binder::Status res = service->isIndividualSensorPrivacyEnabled(userId, sensor, &returnVal);
+ return res.transactionError();
+ }
+ // if the SensorPrivacyManager is not available then assume sensor privacy is disabled
+ returnVal = false;
+ return UNKNOWN_ERROR;
+}
+
status_t SensorPrivacyManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient)
{
sp<hardware::ISensorPrivacyManager> service = getService();
diff --git a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
index 4c2d5db..c8ceeb8 100644
--- a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
+++ b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
@@ -20,11 +20,21 @@
/** @hide */
interface ISensorPrivacyManager {
+ boolean supportsSensorToggle(int sensor);
+
void addSensorPrivacyListener(in ISensorPrivacyListener listener);
+ void addIndividualSensorPrivacyListener(int userId, int sensor, in ISensorPrivacyListener listener);
+
void removeSensorPrivacyListener(in ISensorPrivacyListener listener);
boolean isSensorPrivacyEnabled();
+ boolean isIndividualSensorPrivacyEnabled(int userId, int sensor);
+
void setSensorPrivacy(boolean enable);
+
+ void setIndividualSensorPrivacy(int userId, int sensor, boolean enable);
+
+ void setIndividualSensorPrivacyForProfileGroup(int userId, int sensor, boolean enable);
}
diff --git a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
index 2546a68..fb4cbe9 100644
--- a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
+++ b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
@@ -22,17 +22,29 @@
#include <utils/threads.h>
+#include <unordered_map>
+
// ---------------------------------------------------------------------------
namespace android {
class SensorPrivacyManager
{
public:
+ enum {
+ INDIVIDUAL_SENSOR_MICROPHONE = 1,
+ INDIVIDUAL_SENSOR_CAMERA = 2
+ };
+
SensorPrivacyManager();
+ bool supportsSensorToggle(int sensor);
void addSensorPrivacyListener(const sp<hardware::ISensorPrivacyListener>& listener);
+ status_t addIndividualSensorPrivacyListener(int userId, int sensor,
+ const sp<hardware::ISensorPrivacyListener>& listener);
void removeSensorPrivacyListener(const sp<hardware::ISensorPrivacyListener>& listener);
bool isSensorPrivacyEnabled();
+ bool isIndividualSensorPrivacyEnabled(int userId, int sensor);
+ status_t isIndividualSensorPrivacyEnabled(int userId, int sensor, bool &result);
status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
status_t unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient);
@@ -41,6 +53,8 @@
Mutex mLock;
sp<hardware::ISensorPrivacyManager> mService;
sp<hardware::ISensorPrivacyManager> getService();
+
+ std::unordered_map<int, bool> mSupportedCache;
};
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 07760ab..74d17ce 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -29,6 +29,74 @@
],
}
+cc_defaults {
+ name: "libui-defaults",
+ clang: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ cppflags: [
+ "-Wextra",
+ ],
+
+ sanitize: {
+ integer_overflow: true,
+ misc_undefined: ["bounds"],
+ },
+
+}
+
+cc_library_static {
+ name: "libui-types",
+ vendor_available: true,
+ host_supported: true,
+ target: {
+ windows: {
+ enabled: true,
+ }
+ },
+
+ defaults: [
+ "libui-defaults",
+ ],
+
+ apex_available: [
+ "//apex_available:anyapex",
+ "//apex_available:platform",
+ ],
+ min_sdk_version: "apex_inherit",
+
+ shared_libs: [
+ "libbase",
+ "libutils",
+ ],
+
+ static_libs: [
+ "libarect",
+ "libmath",
+ ],
+
+ srcs: [
+ "ColorSpace.cpp",
+ "Rect.cpp",
+ "Region.cpp",
+ "Transform.cpp",
+ ],
+
+ export_include_dirs: [
+ "include",
+ "include_private",
+ "include_types",
+ ],
+
+ export_static_lib_headers: [
+ "libarect",
+ "libmath",
+ ],
+
+}
+
cc_library_shared {
name: "libui",
vendor_available: true,
@@ -52,8 +120,10 @@
},
srcs: [
- "ColorSpace.cpp",
"DebugUtils.cpp",
+ "DeviceProductInfo.cpp",
+ "DisplayMode.cpp",
+ "DynamicDisplayInfo.cpp",
"Fence.cpp",
"FenceTime.cpp",
"FrameStats.cpp",
@@ -67,11 +137,8 @@
"HdrCapabilities.cpp",
"PixelFormat.cpp",
"PublicFormat.cpp",
- "Rect.cpp",
- "Region.cpp",
"Size.cpp",
- "Transform.cpp",
- "UiConfig.cpp",
+ "StaticDisplayInfo.cpp",
],
include_dirs: [
@@ -82,8 +149,11 @@
"include_private",
],
- // Uncomment the following line to enable VALIDATE_REGIONS traces
- //defaults: ["libui-validate-regions-defaults"],
+ defaults: [
+ "libui-defaults",
+ // Uncomment the following line to enable VALIDATE_REGIONS traces
+ //defaults: ["libui-validate-regions-defaults"],
+ ],
shared_libs: [
"android.hardware.graphics.allocator@2.0",
@@ -117,6 +187,10 @@
"libmath",
],
+ whole_static_libs: [
+ "libui-types",
+ ],
+
// bufferhub is not used when building libgui for vendors
target: {
vendor: {
@@ -165,9 +239,11 @@
},
header_libs: [
"libnativewindow_headers",
+ "libmath_headers",
],
export_header_lib_headers: [
"libnativewindow_headers",
+ "libmath_headers",
],
min_sdk_version: "29",
}
@@ -188,6 +264,8 @@
name: "libui_host_common",
srcs: [
"Rect.cpp",
- "PixelFormat.cpp"
+ "Region.cpp",
+ "PixelFormat.cpp",
+ "Transform.cpp"
],
}
diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp
index f394635..1f006ce 100644
--- a/libs/ui/DebugUtils.cpp
+++ b/libs/ui/DebugUtils.cpp
@@ -321,10 +321,6 @@
return std::string("Unknown RenderIntent");
}
-std::string to_string(const android::Rect& rect) {
- return StringPrintf("(%4d,%4d,%4d,%4d)", rect.left, rect.top, rect.right, rect.bottom);
-}
-
std::string toString(const android::DeviceProductInfo::ManufactureOrModelDate& date) {
using ModelYear = android::DeviceProductInfo::ModelYear;
using ManufactureYear = android::DeviceProductInfo::ManufactureYear;
diff --git a/libs/ui/DeviceProductInfo.cpp b/libs/ui/DeviceProductInfo.cpp
new file mode 100644
index 0000000..4d6ce43
--- /dev/null
+++ b/libs/ui/DeviceProductInfo.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2020 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 <ui/DeviceProductInfo.h>
+
+#include <android-base/stringprintf.h>
+#include <ui/FlattenableHelpers.h>
+#include <utils/Log.h>
+
+#define RETURN_IF_ERROR(op) \
+ if (const status_t status = (op); status != OK) return status;
+
+namespace android {
+
+using base::StringAppendF;
+
+size_t DeviceProductInfo::getFlattenedSize() const {
+ return FlattenableHelpers::getFlattenedSize(name) +
+ FlattenableHelpers::getFlattenedSize(manufacturerPnpId) +
+ FlattenableHelpers::getFlattenedSize(productId) +
+ FlattenableHelpers::getFlattenedSize(manufactureOrModelDate) +
+ FlattenableHelpers::getFlattenedSize(relativeAddress);
+}
+
+status_t DeviceProductInfo::flatten(void* buffer, size_t size) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, name));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, manufacturerPnpId));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, productId));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, manufactureOrModelDate));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, relativeAddress));
+ return OK;
+}
+
+status_t DeviceProductInfo::unflatten(void const* buffer, size_t size) {
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &name));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &manufacturerPnpId));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &productId));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &manufactureOrModelDate));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &relativeAddress));
+ return OK;
+}
+
+void DeviceProductInfo::dump(std::string& result) const {
+ StringAppendF(&result, "{name=%s, ", name.c_str());
+ StringAppendF(&result, "manufacturerPnpId=%s, ", manufacturerPnpId.data());
+ StringAppendF(&result, "productId=%s, ", productId.c_str());
+
+ if (const auto* model = std::get_if<ModelYear>(&manufactureOrModelDate)) {
+ StringAppendF(&result, "modelYear=%u, ", model->year);
+ } else if (const auto* manufactureWeekAndYear =
+ std::get_if<ManufactureWeekAndYear>(&manufactureOrModelDate)) {
+ StringAppendF(&result, "manufactureWeek=%u, ", manufactureWeekAndYear->week);
+ StringAppendF(&result, "manufactureYear=%d, ", manufactureWeekAndYear->year);
+ } else if (const auto* manufactureYear =
+ std::get_if<ManufactureYear>(&manufactureOrModelDate)) {
+ StringAppendF(&result, "manufactureYear=%d, ", manufactureYear->year);
+ } else {
+ ALOGE("Unknown alternative for variant DeviceProductInfo::ManufactureOrModelDate");
+ }
+
+ result.append("relativeAddress=[");
+ for (size_t i = 0; i < relativeAddress.size(); i++) {
+ if (i != 0) {
+ result.append(", ");
+ }
+ StringAppendF(&result, "%u", relativeAddress[i]);
+ }
+ result.append("]}");
+}
+
+} // namespace android
diff --git a/libs/ui/DisplayMode.cpp b/libs/ui/DisplayMode.cpp
new file mode 100644
index 0000000..cf05dbf
--- /dev/null
+++ b/libs/ui/DisplayMode.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2021 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 <ui/DisplayMode.h>
+
+#include <cstdint>
+
+#include <ui/FlattenableHelpers.h>
+
+#define RETURN_IF_ERROR(op) \
+ if (const status_t status = (op); status != OK) return status;
+
+namespace android::ui {
+
+size_t DisplayMode::getFlattenedSize() const {
+ return FlattenableHelpers::getFlattenedSize(id) +
+ FlattenableHelpers::getFlattenedSize(resolution) +
+ FlattenableHelpers::getFlattenedSize(xDpi) +
+ FlattenableHelpers::getFlattenedSize(yDpi) +
+ FlattenableHelpers::getFlattenedSize(refreshRate) +
+ FlattenableHelpers::getFlattenedSize(appVsyncOffset) +
+ FlattenableHelpers::getFlattenedSize(sfVsyncOffset) +
+ FlattenableHelpers::getFlattenedSize(presentationDeadline) +
+ FlattenableHelpers::getFlattenedSize(group);
+}
+
+status_t DisplayMode::flatten(void* buffer, size_t size) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, id));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, resolution));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, xDpi));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, yDpi));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, refreshRate));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, appVsyncOffset));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, sfVsyncOffset));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, presentationDeadline));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, group));
+ return OK;
+}
+
+status_t DisplayMode::unflatten(const void* buffer, size_t size) {
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &id));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &resolution));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &xDpi));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &yDpi));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &refreshRate));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &appVsyncOffset));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &sfVsyncOffset));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &presentationDeadline));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &group));
+ return OK;
+}
+
+} // namespace android::ui
diff --git a/libs/ui/DynamicDisplayInfo.cpp b/libs/ui/DynamicDisplayInfo.cpp
new file mode 100644
index 0000000..d5c4ef0
--- /dev/null
+++ b/libs/ui/DynamicDisplayInfo.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2021 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 <ui/DynamicDisplayInfo.h>
+
+#include <cstdint>
+
+#include <ui/FlattenableHelpers.h>
+
+#define RETURN_IF_ERROR(op) \
+ if (const status_t status = (op); status != OK) return status;
+
+namespace android::ui {
+
+std::optional<ui::DisplayMode> DynamicDisplayInfo::getActiveDisplayMode() const {
+ for (const auto& currMode : supportedDisplayModes) {
+ if (currMode.id == activeDisplayModeId) {
+ return currMode;
+ }
+ }
+ return {};
+}
+
+size_t DynamicDisplayInfo::getFlattenedSize() const {
+ return FlattenableHelpers::getFlattenedSize(supportedDisplayModes) +
+ FlattenableHelpers::getFlattenedSize(activeDisplayModeId) +
+ FlattenableHelpers::getFlattenedSize(supportedColorModes) +
+ FlattenableHelpers::getFlattenedSize(activeColorMode) +
+ FlattenableHelpers::getFlattenedSize(hdrCapabilities) +
+ FlattenableHelpers::getFlattenedSize(autoLowLatencyModeSupported) +
+ FlattenableHelpers::getFlattenedSize(gameContentTypeSupported);
+}
+
+status_t DynamicDisplayInfo::flatten(void* buffer, size_t size) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, supportedDisplayModes));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, activeDisplayModeId));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, supportedColorModes));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, activeColorMode));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, hdrCapabilities));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, autoLowLatencyModeSupported));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, gameContentTypeSupported));
+ return OK;
+}
+
+status_t DynamicDisplayInfo::unflatten(const void* buffer, size_t size) {
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &supportedDisplayModes));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &activeDisplayModeId));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &supportedColorModes));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &activeColorMode));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &hdrCapabilities));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &autoLowLatencyModeSupported));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &gameContentTypeSupported));
+ return OK;
+}
+
+} // namespace android::ui
diff --git a/libs/ui/FenceTime.cpp b/libs/ui/FenceTime.cpp
index bdfe04b..538c1d2 100644
--- a/libs/ui/FenceTime.cpp
+++ b/libs/ui/FenceTime.cpp
@@ -97,6 +97,34 @@
return mState != State::INVALID;
}
+status_t FenceTime::wait(int timeout) {
+ // See if we already have a cached value we can return.
+ nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed);
+ if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+ return NO_ERROR;
+ }
+
+ // Hold a reference to the fence on the stack in case the class'
+ // reference is removed by another thread. This prevents the
+ // fence from being destroyed until the end of this method, where
+ // we conveniently do not have the lock held.
+ sp<Fence> fence;
+ {
+ // With the lock acquired this time, see if we have the cached
+ // value or if we need to poll the fence.
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!mFence.get()) {
+ // Another thread set the signal time just before we added the
+ // reference to mFence.
+ return NO_ERROR;
+ }
+ fence = mFence;
+ }
+
+ // Make the system call without the lock held.
+ return fence->wait(timeout);
+}
+
nsecs_t FenceTime::getSignalTime() {
// See if we already have a cached value we can return.
nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed);
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index f799ce4..636fbde 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -1052,7 +1052,7 @@
Gralloc4Allocator::Gralloc4Allocator(const Gralloc4Mapper& mapper) : mMapper(mapper) {
mAllocator = IAllocator::getService();
if (mAllocator == nullptr) {
- ALOGW("allocator 3.x is not supported");
+ ALOGW("allocator 4.x is not supported");
return;
}
}
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index 943d13e..91d2d58 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -83,20 +83,17 @@
KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
uint64_t total = 0;
result.append("GraphicBufferAllocator buffers:\n");
- const size_t c = list.size();
- for (size_t i=0 ; i<c ; i++) {
+ const size_t count = list.size();
+ StringAppendF(&result, "%10s | %11s | %18s | %s | %8s | %10s | %s\n", "Handle", "Size",
+ "W (Stride) x H", "Layers", "Format", "Usage", "Requestor");
+ for (size_t i = 0; i < count; i++) {
const alloc_rec_t& rec(list.valueAt(i));
- if (rec.size) {
- StringAppendF(&result,
- "%10p: %7.2f KiB | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64 " | %s\n",
- list.keyAt(i), static_cast<double>(rec.size) / 1024.0, rec.width, rec.stride, rec.height,
- rec.layerCount, rec.format, rec.usage, rec.requestorName.c_str());
- } else {
- StringAppendF(&result,
- "%10p: unknown | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64 " | %s\n",
- list.keyAt(i), rec.width, rec.stride, rec.height, rec.layerCount,
- rec.format, rec.usage, rec.requestorName.c_str());
- }
+ std::string sizeStr = (rec.size)
+ ? base::StringPrintf("%7.2f KiB", static_cast<double>(rec.size) / 1024.0)
+ : "unknown";
+ StringAppendF(&result, "%10p | %11s | %4u (%4u) x %4u | %6u | %8X | 0x%8" PRIx64 " | %s\n",
+ list.keyAt(i), sizeStr.c_str(), rec.width, rec.stride, rec.height,
+ rec.layerCount, rec.format, rec.usage, rec.requestorName.c_str());
total += rec.size;
}
StringAppendF(&result, "Total allocated by GraphicBufferAllocator (estimate): %.2f KB\n",
diff --git a/libs/ui/HdrCapabilities.cpp b/libs/ui/HdrCapabilities.cpp
index a5b3e89..aec2fac 100644
--- a/libs/ui/HdrCapabilities.cpp
+++ b/libs/ui/HdrCapabilities.cpp
@@ -23,10 +23,6 @@
#pragma clang diagnostic ignored "-Wundefined-reinterpret-cast"
#endif
-HdrCapabilities::~HdrCapabilities() = default;
-HdrCapabilities::HdrCapabilities(HdrCapabilities&& other) noexcept = default;
-HdrCapabilities& HdrCapabilities::operator=(HdrCapabilities&& other) noexcept = default;
-
size_t HdrCapabilities::getFlattenedSize() const {
return sizeof(mMaxLuminance) +
sizeof(mMaxAverageLuminance) +
diff --git a/libs/ui/PublicFormat.cpp b/libs/ui/PublicFormat.cpp
index 70e3ce7..78e82da 100644
--- a/libs/ui/PublicFormat.cpp
+++ b/libs/ui/PublicFormat.cpp
@@ -35,6 +35,8 @@
case PublicFormat::RAW_SENSOR:
case PublicFormat::RAW_DEPTH:
return HAL_PIXEL_FORMAT_RAW16;
+ case PublicFormat::RAW_DEPTH10:
+ return HAL_PIXEL_FORMAT_RAW10;
default:
// Most formats map 1:1
return static_cast<int>(f);
@@ -50,6 +52,7 @@
case PublicFormat::DEPTH_POINT_CLOUD:
case PublicFormat::DEPTH16:
case PublicFormat::RAW_DEPTH:
+ case PublicFormat::RAW_DEPTH10:
dataspace = Dataspace::DEPTH;
break;
case PublicFormat::RAW_SENSOR:
@@ -80,6 +83,13 @@
PublicFormat mapHalFormatDataspaceToPublicFormat(int format, android_dataspace dataSpace) {
Dataspace ds = static_cast<Dataspace>(dataSpace);
switch (format) {
+ case HAL_PIXEL_FORMAT_RAW10:
+ switch (ds) {
+ case Dataspace::DEPTH:
+ return PublicFormat::RAW_DEPTH10;
+ default:
+ return PublicFormat::RAW10;
+ }
case HAL_PIXEL_FORMAT_RGBA_8888:
case HAL_PIXEL_FORMAT_RGBX_8888:
case HAL_PIXEL_FORMAT_RGBA_FP16:
@@ -87,10 +97,10 @@
case HAL_PIXEL_FORMAT_RGB_888:
case HAL_PIXEL_FORMAT_RGB_565:
case HAL_PIXEL_FORMAT_Y8:
- case HAL_PIXEL_FORMAT_RAW10:
case HAL_PIXEL_FORMAT_RAW12:
case HAL_PIXEL_FORMAT_YCbCr_420_888:
case HAL_PIXEL_FORMAT_YV12:
+ case HAL_PIXEL_FORMAT_YCBCR_P010:
// Enums overlap in both name and value
return static_cast<PublicFormat>(format);
case HAL_PIXEL_FORMAT_RAW16:
diff --git a/libs/ui/Rect.cpp b/libs/ui/Rect.cpp
index 13fed3a..a8d6285 100644
--- a/libs/ui/Rect.cpp
+++ b/libs/ui/Rect.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <android-base/stringprintf.h>
#include <system/graphics.h>
#include <ui/Rect.h>
@@ -149,4 +150,13 @@
return result;
}
+std::string to_string(const android::Rect& rect) {
+ return android::base::StringPrintf("Rect(%d, %d, %d, %d)", rect.left, rect.top, rect.right,
+ rect.bottom);
+}
+
+void PrintTo(const Rect& rect, ::std::ostream* os) {
+ *os << to_string(rect);
+}
+
}; // namespace android
diff --git a/libs/ui/StaticDisplayInfo.cpp b/libs/ui/StaticDisplayInfo.cpp
new file mode 100644
index 0000000..b66b281
--- /dev/null
+++ b/libs/ui/StaticDisplayInfo.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2020 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 <ui/StaticDisplayInfo.h>
+
+#include <cstdint>
+
+#include <ui/FlattenableHelpers.h>
+
+#define RETURN_IF_ERROR(op) \
+ if (const status_t status = (op); status != OK) return status;
+
+namespace android::ui {
+
+size_t StaticDisplayInfo::getFlattenedSize() const {
+ return FlattenableHelpers::getFlattenedSize(connectionType) +
+ FlattenableHelpers::getFlattenedSize(density) +
+ FlattenableHelpers::getFlattenedSize(secure) +
+ FlattenableHelpers::getFlattenedSize(deviceProductInfo);
+}
+
+status_t StaticDisplayInfo::flatten(void* buffer, size_t size) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, connectionType));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, density));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, secure));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, deviceProductInfo));
+ return OK;
+}
+
+status_t StaticDisplayInfo::unflatten(void const* buffer, size_t size) {
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &connectionType));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &density));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &secure));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &deviceProductInfo));
+ return OK;
+}
+
+} // namespace android::ui
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index 06b6bfe..cd68c1c 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+#undef LOG_TAG
+#define LOG_TAG "Transform"
+
#include <math.h>
#include <android-base/stringprintf.h>
@@ -22,8 +25,7 @@
#include <ui/Transform.h>
#include <utils/String8.h>
-namespace android {
-namespace ui {
+namespace android::ui {
Transform::Transform() {
reset();
@@ -55,11 +57,9 @@
mMatrix[1][1] == other.mMatrix[1][1] && mMatrix[1][2] == other.mMatrix[1][2] &&
mMatrix[2][0] == other.mMatrix[2][0] && mMatrix[2][1] == other.mMatrix[2][1] &&
mMatrix[2][2] == other.mMatrix[2][2];
- ;
}
-Transform Transform::operator * (const Transform& rhs) const
-{
+Transform Transform::operator*(const Transform& rhs) const {
if (CC_LIKELY(mType == IDENTITY))
return rhs;
@@ -87,6 +87,19 @@
return r;
}
+Transform Transform::operator * (float value) const {
+ Transform r(*this);
+ const mat33& M(mMatrix);
+ mat33& R(r.mMatrix);
+ for (size_t i = 0; i < 3; i++) {
+ for (size_t j = 0; j < 2; j++) {
+ R[i][j] = M[i][j] * value;
+ }
+ }
+ r.type();
+ return r;
+}
+
Transform& Transform::operator=(const Transform& other) {
mMatrix = other.mMatrix;
mType = other.mType;
@@ -105,14 +118,30 @@
return mMatrix[2][1];
}
-float Transform::sx() const {
+float Transform::dsdx() const {
return mMatrix[0][0];
}
-float Transform::sy() const {
+float Transform::dtdx() const {
+ return mMatrix[1][0];
+}
+
+float Transform::dtdy() const {
+ return mMatrix[0][1];
+}
+
+float Transform::dsdy() const {
return mMatrix[1][1];
}
+float Transform::getScaleX() const {
+ return sqrt((dsdx() * dsdx()) + (dtdx() * dtdx()));
+}
+
+float Transform::getScaleY() const {
+ return sqrt((dtdy() * dtdy()) + (dsdy() * dsdy()));
+}
+
void Transform::reset() {
mType = IDENTITY;
for(size_t i = 0; i < 3; i++) {
@@ -122,8 +151,7 @@
}
}
-void Transform::set(float tx, float ty)
-{
+void Transform::set(float tx, float ty) {
mMatrix[2][0] = tx;
mMatrix[2][1] = ty;
mMatrix[2][2] = 1.0f;
@@ -135,8 +163,7 @@
}
}
-void Transform::set(float a, float b, float c, float d)
-{
+void Transform::set(float a, float b, float c, float d) {
mat33& M(mMatrix);
M[0][0] = a; M[1][0] = b;
M[0][1] = c; M[1][1] = d;
@@ -144,8 +171,7 @@
mType = UNKNOWN_TYPE;
}
-status_t Transform::set(uint32_t flags, float w, float h)
-{
+status_t Transform::set(uint32_t flags, float w, float h) {
if (flags & ROT_INVALID) {
// that's not allowed!
reset();
@@ -187,6 +213,15 @@
return NO_ERROR;
}
+void Transform::set(const std::array<float, 9>& matrix) {
+ mat33& M(mMatrix);
+ M[0][0] = matrix[0]; M[1][0] = matrix[1]; M[2][0] = matrix[2];
+ M[0][1] = matrix[3]; M[1][1] = matrix[4]; M[2][1] = matrix[5];
+ M[0][2] = matrix[6]; M[1][2] = matrix[7]; M[2][2] = matrix[8];
+ mType = UNKNOWN_TYPE;
+ type();
+}
+
vec2 Transform::transform(const vec2& v) const {
vec2 r;
const mat33& M(mMatrix);
@@ -204,18 +239,15 @@
return r;
}
-vec2 Transform::transform(int x, int y) const
-{
- return transform(vec2(x,y));
+vec2 Transform::transform(float x, float y) const {
+ return transform(vec2(x, y));
}
-Rect Transform::makeBounds(int w, int h) const
-{
+Rect Transform::makeBounds(int w, int h) const {
return transform( Rect(w, h) );
}
-Rect Transform::transform(const Rect& bounds, bool roundOutwards) const
-{
+Rect Transform::transform(const Rect& bounds, bool roundOutwards) const {
Rect r;
vec2 lt( bounds.left, bounds.top );
vec2 rt( bounds.right, bounds.top );
@@ -242,8 +274,7 @@
return r;
}
-FloatRect Transform::transform(const FloatRect& bounds) const
-{
+FloatRect Transform::transform(const FloatRect& bounds) const {
vec2 lt(bounds.left, bounds.top);
vec2 rt(bounds.right, bounds.top);
vec2 lb(bounds.left, bounds.bottom);
@@ -263,8 +294,7 @@
return r;
}
-Region Transform::transform(const Region& reg) const
-{
+Region Transform::transform(const Region& reg) const {
Region out;
if (CC_UNLIKELY(type() > TRANSLATE)) {
if (CC_LIKELY(preserveRects())) {
@@ -284,8 +314,7 @@
return out;
}
-uint32_t Transform::type() const
-{
+uint32_t Transform::type() const {
if (mType & UNKNOWN_TYPE) {
// recompute what this transform is
@@ -380,16 +409,18 @@
return type() & 0xFF;
}
-uint32_t Transform::getOrientation() const
-{
+uint32_t Transform::getOrientation() const {
return (type() >> 8) & 0xFF;
}
-bool Transform::preserveRects() const
-{
+bool Transform::preserveRects() const {
return (getOrientation() & ROT_INVALID) ? false : true;
}
+bool Transform::needsBilinearFiltering() const {
+ return (!preserveRects() || getType() >= ui::Transform::SCALE);
+}
+
mat4 Transform::asMatrix4() const {
// Internally Transform uses a 3x3 matrix since the transform is meant for
// two-dimensional values. An equivalent 4x4 matrix means inserting an extra
@@ -421,7 +452,43 @@
return m;
}
-void Transform::dump(std::string& out, const char* name) const {
+static std::string rotationToString(const uint32_t rotationFlags) {
+ switch (rotationFlags) {
+ case Transform::ROT_0:
+ return "ROT_0";
+ case Transform::FLIP_H:
+ return "FLIP_H";
+ case Transform::FLIP_V:
+ return "FLIP_V";
+ case Transform::ROT_90:
+ return "ROT_90";
+ case Transform::ROT_180:
+ return "ROT_180";
+ case Transform::ROT_270:
+ return "ROT_270";
+ case Transform::ROT_INVALID:
+ default:
+ return "ROT_INVALID";
+ }
+}
+
+static std::string transformToString(const uint32_t transform) {
+ if (transform == Transform::IDENTITY) {
+ return "IDENTITY";
+ }
+
+ if (transform == Transform::UNKNOWN) {
+ return "UNKNOWN";
+ }
+
+ std::string out;
+ if (transform & Transform::SCALE) out.append("SCALE ");
+ if (transform & Transform::ROTATE) out.append("ROTATE ");
+ if (transform & Transform::TRANSLATE) out.append("TRANSLATE");
+ return out;
+}
+
+void Transform::dump(std::string& out, const char* name, const char* prefix) const {
using android::base::StringAppendF;
type(); // Ensure the information in mType is up to date
@@ -429,40 +496,34 @@
const uint32_t type = mType;
const uint32_t orient = type >> 8;
- StringAppendF(&out, "%s 0x%08x (", name, orient);
+ out += prefix;
+ out += name;
+ out += " ";
if (orient & ROT_INVALID) {
- out.append("ROT_INVALID ");
- } else {
- if (orient & ROT_90) {
- out.append("ROT_90 ");
- } else {
- out.append("ROT_0 ");
- }
- if (orient & FLIP_V) out.append("FLIP_V ");
- if (orient & FLIP_H) out.append("FLIP_H ");
+ StringAppendF(&out, "0x%08x ", orient);
+ }
+ out += "(" + rotationToString(orient) + ") ";
+
+ if (type & UNKNOWN) {
+ StringAppendF(&out, "0x%02x ", type);
+ }
+ out += "(" + transformToString(type) + ")\n";
+
+ if (type == IDENTITY) {
+ return;
}
- StringAppendF(&out, ") 0x%02x (", type);
-
- if (!(type & (SCALE | ROTATE | TRANSLATE))) out.append("IDENTITY ");
- if (type & SCALE) out.append("SCALE ");
- if (type & ROTATE) out.append("ROTATE ");
- if (type & TRANSLATE) out.append("TRANSLATE ");
-
- out.append(")\n");
-
for (size_t i = 0; i < 3; i++) {
- StringAppendF(&out, " %.4f %.4f %.4f\n", static_cast<double>(mMatrix[0][i]),
+ StringAppendF(&out, "%s %.4f %.4f %.4f\n", prefix, static_cast<double>(mMatrix[0][i]),
static_cast<double>(mMatrix[1][i]), static_cast<double>(mMatrix[2][i]));
}
}
-void Transform::dump(const char* name) const {
+void Transform::dump(const char* name, const char* prefix) const {
std::string out;
- dump(out, name);
+ dump(out, name, prefix);
ALOGD("%s", out.c_str());
}
-} // namespace ui
-} // namespace android
+} // namespace android::ui
diff --git a/libs/ui/include/ui/BlurRegion.h b/libs/ui/include/ui/BlurRegion.h
new file mode 100644
index 0000000..69a586e
--- /dev/null
+++ b/libs/ui/include/ui/BlurRegion.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+#include <iosfwd>
+#include <iostream>
+
+namespace android {
+
+struct BlurRegion {
+ uint32_t blurRadius;
+ float cornerRadiusTL;
+ float cornerRadiusTR;
+ float cornerRadiusBL;
+ float cornerRadiusBR;
+ float alpha;
+ int left;
+ int top;
+ int right;
+ int bottom;
+};
+
+static inline void PrintTo(const BlurRegion& blurRegion, ::std::ostream* os) {
+ *os << "BlurRegion {";
+ *os << "\n .blurRadius = " << blurRegion.blurRadius;
+ *os << "\n .cornerRadiusTL = " << blurRegion.cornerRadiusTL;
+ *os << "\n .cornerRadiusTR = " << blurRegion.cornerRadiusTR;
+ *os << "\n .cornerRadiusBL = " << blurRegion.cornerRadiusBL;
+ *os << "\n .cornerRadiusBR = " << blurRegion.cornerRadiusBR;
+ *os << "\n .alpha = " << blurRegion.alpha;
+ *os << "\n .left = " << blurRegion.left;
+ *os << "\n .top = " << blurRegion.top;
+ *os << "\n .right = " << blurRegion.right;
+ *os << "\n .bottom = " << blurRegion.bottom;
+ *os << "\n}";
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/ui/include/ui/DebugUtils.h b/libs/ui/include/ui/DebugUtils.h
index 4685575..18cd487 100644
--- a/libs/ui/include/ui/DebugUtils.h
+++ b/libs/ui/include/ui/DebugUtils.h
@@ -34,5 +34,4 @@
std::string decodeColorTransform(android_color_transform colorTransform);
std::string decodePixelFormat(android::PixelFormat format);
std::string decodeRenderIntent(android::ui::RenderIntent renderIntent);
-std::string to_string(const android::Rect& rect);
std::string toString(const android::DeviceProductInfo&);
diff --git a/libs/ui/include/ui/DeviceProductInfo.h b/libs/ui/include/ui/DeviceProductInfo.h
index af00342..807a5d9 100644
--- a/libs/ui/include/ui/DeviceProductInfo.h
+++ b/libs/ui/include/ui/DeviceProductInfo.h
@@ -19,7 +19,12 @@
#include <array>
#include <cstdint>
#include <optional>
+#include <string>
+#include <type_traits>
#include <variant>
+#include <vector>
+
+#include <utils/Flattenable.h>
namespace android {
@@ -29,13 +34,7 @@
// Product-specific information about the display or the directly connected device on the
// display chain. For example, if the display is transitively connected, this field may contain
// product information about the intermediate device.
-struct DeviceProductInfo {
- static constexpr size_t TEXT_BUFFER_SIZE = 20;
- static constexpr size_t RELATIVE_ADDRESS_SIZE = 4;
-
- using RelativeAddress = std::array<uint8_t, RELATIVE_ADDRESS_SIZE>;
- static constexpr RelativeAddress NO_RELATIVE_ADDRESS = {0xff, 0xff, 0xff, 0xff};
-
+struct DeviceProductInfo : LightFlattenable<DeviceProductInfo> {
struct ModelYear {
uint32_t year;
};
@@ -48,21 +47,29 @@
};
// Display name.
- std::array<char, TEXT_BUFFER_SIZE> name;
+ std::string name;
// Manufacturer Plug and Play ID.
PnpId manufacturerPnpId;
// Manufacturer product ID.
- std::array<char, TEXT_BUFFER_SIZE> productId;
+ std::string productId;
using ManufactureOrModelDate = std::variant<ModelYear, ManufactureYear, ManufactureWeekAndYear>;
+ static_assert(std::is_trivially_copyable_v<ManufactureOrModelDate>);
ManufactureOrModelDate manufactureOrModelDate;
- // Relative address in the display network. Unavailable address is indicated
- // by all elements equal to 255.
+ // Relative address in the display network. Empty vector indicates that the
+ // address is unavailable.
// For example, for HDMI connected device this will be the physical address.
- RelativeAddress relativeAddress;
+ std::vector<uint8_t> relativeAddress;
+
+ bool isFixedSize() const { return false; }
+ size_t getFlattenedSize() const;
+ status_t flatten(void* buffer, size_t size) const;
+ status_t unflatten(void const* buffer, size_t size);
+
+ void dump(std::string& result) const;
};
} // namespace android
diff --git a/libs/ui/include/ui/DisplayId.h b/libs/ui/include/ui/DisplayId.h
new file mode 100644
index 0000000..f196ab9
--- /dev/null
+++ b/libs/ui/include/ui/DisplayId.h
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <optional>
+#include <string>
+
+namespace android {
+
+// ID of a physical or a virtual display. This class acts as a type safe wrapper around uint64_t.
+// The encoding of the ID is type-specific for bits 0 to 61.
+struct DisplayId {
+ // Flag indicating that the display is virtual.
+ static constexpr uint64_t FLAG_VIRTUAL = 1ULL << 63;
+
+ // Flag indicating that the ID is stable across reboots.
+ static constexpr uint64_t FLAG_STABLE = 1ULL << 62;
+
+ // TODO(b/162612135) Remove default constructor
+ DisplayId() = default;
+ constexpr DisplayId(const DisplayId&) = default;
+ DisplayId& operator=(const DisplayId&) = default;
+
+ uint64_t value;
+
+protected:
+ explicit constexpr DisplayId(uint64_t id) : value(id) {}
+};
+
+static_assert(sizeof(DisplayId) == sizeof(uint64_t));
+
+inline bool operator==(DisplayId lhs, DisplayId rhs) {
+ return lhs.value == rhs.value;
+}
+
+inline bool operator!=(DisplayId lhs, DisplayId rhs) {
+ return !(lhs == rhs);
+}
+
+inline std::string to_string(DisplayId displayId) {
+ return std::to_string(displayId.value);
+}
+
+// DisplayId of a physical display, such as the internal display or externally connected display.
+struct PhysicalDisplayId : DisplayId {
+ static constexpr std::optional<PhysicalDisplayId> tryCast(DisplayId id) {
+ if (id.value & FLAG_VIRTUAL) {
+ return std::nullopt;
+ }
+ return {PhysicalDisplayId(id)};
+ }
+
+ // Returns a stable ID based on EDID information.
+ static constexpr PhysicalDisplayId fromEdid(uint8_t port, uint16_t manufacturerId,
+ uint32_t modelHash) {
+ return PhysicalDisplayId(FLAG_STABLE, port, manufacturerId, modelHash);
+ }
+
+ // Returns an unstable ID. If EDID is available using "fromEdid" is preferred.
+ static constexpr PhysicalDisplayId fromPort(uint8_t port) {
+ constexpr uint16_t kManufacturerId = 0;
+ constexpr uint32_t kModelHash = 0;
+ return PhysicalDisplayId(0, port, kManufacturerId, kModelHash);
+ }
+
+ // TODO(b/162612135) Remove default constructor
+ PhysicalDisplayId() = default;
+ // TODO(b/162612135) Remove constructor
+ explicit constexpr PhysicalDisplayId(uint64_t id) : DisplayId(id) {}
+
+ constexpr uint16_t getManufacturerId() const { return static_cast<uint16_t>(value >> 40); }
+
+ constexpr uint8_t getPort() const { return static_cast<uint8_t>(value); }
+
+private:
+ constexpr PhysicalDisplayId(uint64_t flags, uint8_t port, uint16_t manufacturerId,
+ uint32_t modelHash)
+ : DisplayId(flags | (static_cast<uint64_t>(manufacturerId) << 40) |
+ (static_cast<uint64_t>(modelHash) << 8) | port) {}
+
+ explicit constexpr PhysicalDisplayId(DisplayId other) : DisplayId(other) {}
+};
+
+static_assert(sizeof(PhysicalDisplayId) == sizeof(uint64_t));
+
+struct VirtualDisplayId : DisplayId {
+ using BaseId = uint32_t;
+ // Flag indicating that this virtual display is backed by the GPU.
+ static constexpr uint64_t FLAG_GPU = 1ULL << 61;
+
+ static constexpr std::optional<VirtualDisplayId> tryCast(DisplayId id) {
+ if (id.value & FLAG_VIRTUAL) {
+ return {VirtualDisplayId(id)};
+ }
+ return std::nullopt;
+ }
+
+protected:
+ constexpr VirtualDisplayId(uint64_t flags, BaseId baseId)
+ : DisplayId(DisplayId::FLAG_VIRTUAL | flags | baseId) {}
+
+ explicit constexpr VirtualDisplayId(DisplayId other) : DisplayId(other) {}
+};
+
+struct HalVirtualDisplayId : VirtualDisplayId {
+ explicit constexpr HalVirtualDisplayId(BaseId baseId) : VirtualDisplayId(0, baseId) {}
+
+ static constexpr std::optional<HalVirtualDisplayId> tryCast(DisplayId id) {
+ if ((id.value & FLAG_VIRTUAL) && !(id.value & VirtualDisplayId::FLAG_GPU)) {
+ return {HalVirtualDisplayId(id)};
+ }
+ return std::nullopt;
+ }
+
+private:
+ explicit constexpr HalVirtualDisplayId(DisplayId other) : VirtualDisplayId(other) {}
+};
+
+struct GpuVirtualDisplayId : VirtualDisplayId {
+ explicit constexpr GpuVirtualDisplayId(BaseId baseId)
+ : VirtualDisplayId(VirtualDisplayId::FLAG_GPU, baseId) {}
+
+ static constexpr std::optional<GpuVirtualDisplayId> tryCast(DisplayId id) {
+ if ((id.value & FLAG_VIRTUAL) && (id.value & VirtualDisplayId::FLAG_GPU)) {
+ return {GpuVirtualDisplayId(id)};
+ }
+ return std::nullopt;
+ }
+
+private:
+ explicit constexpr GpuVirtualDisplayId(DisplayId other) : VirtualDisplayId(other) {}
+};
+
+// HalDisplayId is the ID of a display which is managed by HWC.
+// PhysicalDisplayId and HalVirtualDisplayId are implicitly convertible to HalDisplayId.
+struct HalDisplayId : DisplayId {
+ constexpr HalDisplayId(HalVirtualDisplayId other) : DisplayId(other) {}
+ constexpr HalDisplayId(PhysicalDisplayId other) : DisplayId(other) {}
+
+ static constexpr std::optional<HalDisplayId> tryCast(DisplayId id) {
+ if (GpuVirtualDisplayId::tryCast(id)) {
+ return std::nullopt;
+ }
+ return {HalDisplayId(id)};
+ }
+
+private:
+ explicit constexpr HalDisplayId(DisplayId other) : DisplayId(other) {}
+};
+
+static_assert(sizeof(VirtualDisplayId) == sizeof(uint64_t));
+static_assert(sizeof(HalVirtualDisplayId) == sizeof(uint64_t));
+static_assert(sizeof(GpuVirtualDisplayId) == sizeof(uint64_t));
+static_assert(sizeof(HalDisplayId) == sizeof(uint64_t));
+
+} // namespace android
+
+namespace std {
+
+template <>
+struct hash<android::DisplayId> {
+ size_t operator()(android::DisplayId displayId) const {
+ return hash<uint64_t>()(displayId.value);
+ }
+};
+
+template <>
+struct hash<android::PhysicalDisplayId> : hash<android::DisplayId> {};
+
+template <>
+struct hash<android::HalVirtualDisplayId> : hash<android::DisplayId> {};
+
+template <>
+struct hash<android::GpuVirtualDisplayId> : hash<android::DisplayId> {};
+
+template <>
+struct hash<android::HalDisplayId> : hash<android::DisplayId> {};
+
+} // namespace std
diff --git a/libs/ui/include/ui/DisplayConfig.h b/libs/ui/include/ui/DisplayMode.h
similarity index 61%
rename from libs/ui/include/ui/DisplayConfig.h
rename to libs/ui/include/ui/DisplayMode.h
index d6fbaab..56f68e7 100644
--- a/libs/ui/include/ui/DisplayConfig.h
+++ b/libs/ui/include/ui/DisplayMode.h
@@ -16,15 +16,21 @@
#pragma once
+#include <cstdint>
#include <type_traits>
#include <ui/Size.h>
+#include <utils/Flattenable.h>
#include <utils/Timers.h>
-namespace android {
+namespace android::ui {
-// Configuration supported by physical display.
-struct DisplayConfig {
+// This value is going to be serialized over binder so we prefer a fixed width type.
+using DisplayModeId = int32_t;
+
+// Mode supported by physical display.
+struct DisplayMode : LightFlattenable<DisplayMode> {
+ DisplayModeId id;
ui::Size resolution;
float xDpi = 0;
float yDpi = 0;
@@ -33,9 +39,12 @@
nsecs_t appVsyncOffset = 0;
nsecs_t sfVsyncOffset = 0;
nsecs_t presentationDeadline = 0;
- int configGroup = -1;
+ int32_t group = -1;
+
+ bool isFixedSize() const { return false; }
+ size_t getFlattenedSize() const;
+ status_t flatten(void* buffer, size_t size) const;
+ status_t unflatten(const void* buffer, size_t size);
};
-static_assert(std::is_trivially_copyable_v<DisplayConfig>);
-
-} // namespace android
+} // namespace android::ui
diff --git a/libs/ui/include/ui/DisplayState.h b/libs/ui/include/ui/DisplayState.h
index 64efc84..70a0d50 100644
--- a/libs/ui/include/ui/DisplayState.h
+++ b/libs/ui/include/ui/DisplayState.h
@@ -32,7 +32,7 @@
struct DisplayState {
LayerStack layerStack = NO_LAYER_STACK;
Rotation orientation = ROTATION_0;
- Size viewport;
+ Size layerStackSpaceRect;
};
static_assert(std::is_trivially_copyable_v<DisplayState>);
diff --git a/libs/ui/include/ui/DynamicDisplayInfo.h b/libs/ui/include/ui/DynamicDisplayInfo.h
new file mode 100644
index 0000000..a4c2f71
--- /dev/null
+++ b/libs/ui/include/ui/DynamicDisplayInfo.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#pragma once
+
+#include "DisplayMode.h"
+
+#include <cstdint>
+#include <optional>
+#include <vector>
+
+#include <ui/GraphicTypes.h>
+#include <ui/HdrCapabilities.h>
+#include <utils/Flattenable.h>
+
+namespace android::ui {
+
+// Information about a physical display which may change on hotplug reconnect.
+struct DynamicDisplayInfo : LightFlattenable<DynamicDisplayInfo> {
+ std::vector<ui::DisplayMode> supportedDisplayModes;
+
+ // This struct is going to be serialized over binder, so
+ // we can't use size_t because it may have different width
+ // in the client process.
+ int32_t activeDisplayModeId;
+
+ std::vector<ui::ColorMode> supportedColorModes;
+ ui::ColorMode activeColorMode;
+ HdrCapabilities hdrCapabilities;
+
+ // True if the display reports support for HDMI 2.1 Auto Low Latency Mode.
+ // For more information, see the HDMI 2.1 specification.
+ bool autoLowLatencyModeSupported;
+
+ // True if the display reports support for Game Content Type.
+ // For more information, see the HDMI 1.4 specification.
+ bool gameContentTypeSupported;
+
+ std::optional<ui::DisplayMode> getActiveDisplayMode() const;
+
+ bool isFixedSize() const { return false; }
+ size_t getFlattenedSize() const;
+ status_t flatten(void* buffer, size_t size) const;
+ status_t unflatten(const void* buffer, size_t size);
+};
+
+} // namespace android::ui
diff --git a/libs/ui/include/ui/FatVector.h b/libs/ui/include/ui/FatVector.h
index 25fe3a0..cb61e6a 100644
--- a/libs/ui/include/ui/FatVector.h
+++ b/libs/ui/include/ui/FatVector.h
@@ -82,6 +82,12 @@
this->reserve(SIZE);
}
+ FatVector(std::initializer_list<T> init)
+ : std::vector<T, InlineStdAllocator<T, SIZE>>(init,
+ InlineStdAllocator<T, SIZE>(mAllocation)) {
+ this->reserve(SIZE);
+ }
+
explicit FatVector(size_t capacity) : FatVector() { this->resize(capacity); }
private:
diff --git a/libs/ui/include/ui/FenceTime.h b/libs/ui/include/ui/FenceTime.h
index ecba7f7..ac75f43 100644
--- a/libs/ui/include/ui/FenceTime.h
+++ b/libs/ui/include/ui/FenceTime.h
@@ -112,6 +112,13 @@
// Returns a snapshot of the FenceTime in its current state.
Snapshot getSnapshot() const;
+ // wait waits for up to timeout milliseconds for the fence to signal. If
+ // the fence signals then NO_ERROR is returned. If the timeout expires
+ // before the fence signals then -ETIME is returned. A timeout of
+ // TIMEOUT_NEVER may be used to indicate that the call should wait
+ // indefinitely for the fence to signal.
+ status_t wait(int timeout);
+
void signalForTest(nsecs_t signalTime);
private:
diff --git a/libs/ui/include/ui/FloatRect.h b/libs/ui/include/ui/FloatRect.h
index bec2552..4c9c7b7 100644
--- a/libs/ui/include/ui/FloatRect.h
+++ b/libs/ui/include/ui/FloatRect.h
@@ -16,6 +16,7 @@
#pragma once
+#include <math/HashCombine.h>
#include <ostream>
namespace android {
@@ -48,6 +49,8 @@
float top = 0.0f;
float right = 0.0f;
float bottom = 0.0f;
+
+ constexpr bool isEmpty() const { return !(left < right && top < bottom); }
};
inline bool operator==(const FloatRect& a, const FloatRect& b) {
@@ -60,3 +63,13 @@
}
} // namespace android
+
+namespace std {
+
+template <>
+struct hash<android::FloatRect> {
+ size_t operator()(const android::FloatRect& rect) const {
+ return android::hashCombine(rect.left, rect.top, rect.right, rect.bottom);
+ }
+};
+} // namespace std
diff --git a/libs/ui/include/ui/HdrCapabilities.h b/libs/ui/include/ui/HdrCapabilities.h
index 65ac26c..813adde 100644
--- a/libs/ui/include/ui/HdrCapabilities.h
+++ b/libs/ui/include/ui/HdrCapabilities.h
@@ -36,18 +36,12 @@
mMaxAverageLuminance(maxAverageLuminance),
mMinLuminance(minLuminance) {}
- // Make this move-constructable and move-assignable
- HdrCapabilities(HdrCapabilities&& other) noexcept;
- HdrCapabilities& operator=(HdrCapabilities&& other) noexcept;
-
HdrCapabilities()
: mSupportedHdrTypes(),
mMaxLuminance(-1.0f),
mMaxAverageLuminance(-1.0f),
mMinLuminance(-1.0f) {}
- ~HdrCapabilities();
-
const std::vector<ui::Hdr>& getSupportedHdrTypes() const {
return mSupportedHdrTypes;
}
diff --git a/libs/ui/include/ui/PhysicalDisplayId.h b/libs/ui/include/ui/PhysicalDisplayId.h
deleted file mode 100644
index 1a345ac..0000000
--- a/libs/ui/include/ui/PhysicalDisplayId.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2019 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.
- */
-
-#pragma once
-
-#include <cinttypes>
-#include <cstdint>
-
-#define ANDROID_PHYSICAL_DISPLAY_ID_FORMAT PRIu64
-
-namespace android {
-
-using PhysicalDisplayId = uint64_t;
-
-constexpr uint8_t getPhysicalDisplayPort(PhysicalDisplayId displayId) {
- return static_cast<uint8_t>(displayId);
-}
-
-} // namespace android
diff --git a/libs/ui/include/ui/PublicFormat.h b/libs/ui/include/ui/PublicFormat.h
index 1152cc5..aa58805 100644
--- a/libs/ui/include/ui/PublicFormat.h
+++ b/libs/ui/include/ui/PublicFormat.h
@@ -50,9 +50,11 @@
JPEG = 0x100,
DEPTH_POINT_CLOUD = 0x101,
RAW_DEPTH = 0x1002, // @hide
+ RAW_DEPTH10 = 0x1003, // @hide
YV12 = 0x32315659,
Y8 = 0x20203859,
Y16 = 0x20363159, // @hide
+ YCBCR_P010 = 0x36,
DEPTH16 = 0x44363159,
DEPTH_JPEG = 0x69656963,
HEIC = 0x48454946,
diff --git a/libs/ui/include/ui/Rect.h b/libs/ui/include/ui/Rect.h
index 2f2229e..9e24a07 100644
--- a/libs/ui/include/ui/Rect.h
+++ b/libs/ui/include/ui/Rect.h
@@ -19,11 +19,12 @@
#include <ostream>
+#include <log/log.h>
#include <utils/Flattenable.h>
#include <utils/Log.h>
#include <utils/TypeHelpers.h>
-#include <log/log.h>
+#include <math/HashCombine.h>
#include <ui/FloatRect.h>
#include <ui/Point.h>
#include <ui/Size.h>
@@ -202,6 +203,15 @@
// the input.
Rect transform(uint32_t xform, int32_t width, int32_t height) const;
+ Rect scale(float scaleX, float scaleY) const {
+ return Rect(FloatRect(left * scaleX, top * scaleY, right * scaleX, bottom * scaleY));
+ }
+
+ Rect& scaleSelf(float scaleX, float scaleY) {
+ set(scale(scaleX, scaleY));
+ return *this;
+ }
+
// this calculates (Region(*this) - exclude).bounds() efficiently
Rect reduce(const Rect& exclude) const;
@@ -216,14 +226,22 @@
}
};
+std::string to_string(const android::Rect& rect);
+
// Defining PrintTo helps with Google Tests.
-static inline void PrintTo(const Rect& rect, ::std::ostream* os) {
- *os << "Rect(" << rect.left << ", " << rect.top << ", " << rect.right << ", " << rect.bottom
- << ")";
-}
+void PrintTo(const Rect& rect, ::std::ostream* os);
ANDROID_BASIC_TYPES_TRAITS(Rect)
}; // namespace android
+namespace std {
+template <>
+struct hash<android::Rect> {
+ size_t operator()(const android::Rect& rect) const {
+ return android::hashCombine(rect.left, rect.top, rect.right, rect.bottom);
+ }
+};
+} // namespace std
+
#endif // ANDROID_UI_RECT
diff --git a/libs/ui/include/ui/Region.h b/libs/ui/include/ui/Region.h
index 6bb7b8d..927c334 100644
--- a/libs/ui/include/ui/Region.h
+++ b/libs/ui/include/ui/Region.h
@@ -21,6 +21,7 @@
#include <sys/types.h>
#include <ostream>
+#include <math/HashCombine.h>
#include <ui/Rect.h>
#include <utils/Flattenable.h>
@@ -234,4 +235,17 @@
// ---------------------------------------------------------------------------
}; // namespace android
+namespace std {
+template <>
+struct hash<android::Region> {
+ size_t operator()(const android::Region& region) const {
+ size_t hash = 0;
+ for (const android::Rect& rect : region) {
+ android::hashCombineSingle(hash, rect);
+ }
+ return hash;
+ }
+};
+} // namespace std
+
#endif // ANDROID_UI_REGION_H
diff --git a/libs/ui/include/ui/Rotation.h b/libs/ui/include/ui/Rotation.h
index 89008f6..83d431d 100644
--- a/libs/ui/include/ui/Rotation.h
+++ b/libs/ui/include/ui/Rotation.h
@@ -41,6 +41,15 @@
return toRotation((toRotationInt(lhs) + toRotationInt(rhs)) % N);
}
+constexpr Rotation operator-(Rotation lhs, Rotation rhs) {
+ constexpr auto N = toRotationInt(ROTATION_270) + 1;
+ return toRotation((N + toRotationInt(lhs) - toRotationInt(rhs)) % N);
+}
+
+constexpr Rotation operator-(Rotation rotation) {
+ return ROTATION_0 - rotation;
+}
+
constexpr const char* toCString(Rotation rotation) {
switch (rotation) {
case ROTATION_0:
diff --git a/libs/ui/include/ui/DisplayInfo.h b/libs/ui/include/ui/StaticDisplayInfo.h
similarity index 74%
rename from libs/ui/include/ui/DisplayInfo.h
rename to libs/ui/include/ui/StaticDisplayInfo.h
index 897060c..e86ca29 100644
--- a/libs/ui/include/ui/DisplayInfo.h
+++ b/libs/ui/include/ui/StaticDisplayInfo.h
@@ -17,22 +17,25 @@
#pragma once
#include <optional>
-#include <type_traits>
#include <ui/DeviceProductInfo.h>
+#include <utils/Flattenable.h>
-namespace android {
+namespace android::ui {
enum class DisplayConnectionType { Internal, External };
// Immutable information about physical display.
-struct DisplayInfo {
+struct StaticDisplayInfo : LightFlattenable<StaticDisplayInfo> {
DisplayConnectionType connectionType = DisplayConnectionType::Internal;
float density = 0.f;
bool secure = false;
std::optional<DeviceProductInfo> deviceProductInfo;
+
+ bool isFixedSize() const { return false; }
+ size_t getFlattenedSize() const;
+ status_t flatten(void* buffer, size_t size) const;
+ status_t unflatten(void const* buffer, size_t size);
};
-static_assert(std::is_trivially_copyable_v<DisplayInfo>);
-
-} // namespace android
+} // namespace android::ui
diff --git a/libs/ui/include/ui/StretchEffect.h b/libs/ui/include/ui/StretchEffect.h
new file mode 100644
index 0000000..0803df3
--- /dev/null
+++ b/libs/ui/include/ui/StretchEffect.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#pragma once
+
+#include <utils/Flattenable.h>
+#include "FloatRect.h"
+
+#include <math.h>
+#include <type_traits>
+
+namespace android {
+
+struct StretchEffect : public LightFlattenablePod<StretchEffect> {
+ FloatRect area = {0, 0, 0, 0};
+ float vectorX = 0;
+ float vectorY = 0;
+ float maxAmount = 0;
+
+ bool operator==(const StretchEffect& other) const {
+ return area == other.area && vectorX == other.vectorX && vectorY == other.vectorY &&
+ maxAmount == other.maxAmount;
+ }
+
+ static bool isZero(float value) {
+ constexpr float NON_ZERO_EPSILON = 0.001f;
+ return fabsf(value) <= NON_ZERO_EPSILON;
+ }
+
+ bool isNoOp() const { return isZero(vectorX) && isZero(vectorY); }
+
+ bool hasEffect() const { return !isNoOp(); }
+
+ void sanitize() {
+ // If the area is empty, or the max amount is zero, then reset back to defaults
+ if (area.isEmpty() || isZero(maxAmount)) {
+ *this = StretchEffect{};
+ }
+ }
+};
+
+static_assert(std::is_trivially_copyable<StretchEffect>::value,
+ "StretchEffect must be trivially copyable to be flattenable");
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h
index c6bb598..a197b3b 100644
--- a/libs/ui/include/ui/Transform.h
+++ b/libs/ui/include/ui/Transform.h
@@ -18,10 +18,10 @@
#include <stdint.h>
#include <sys/types.h>
+#include <array>
#include <ostream>
#include <string>
-#include <hardware/hardware.h>
#include <math/mat4.h>
#include <math/vec2.h>
#include <math/vec3.h>
@@ -44,9 +44,9 @@
enum RotationFlags : uint32_t {
ROT_0 = 0,
- FLIP_H = HAL_TRANSFORM_FLIP_H,
- FLIP_V = HAL_TRANSFORM_FLIP_V,
- ROT_90 = HAL_TRANSFORM_ROT_90,
+ FLIP_H = 1, // HAL_TRANSFORM_FLIP_H
+ FLIP_V = 2, // HAL_TRANSFORM_FLIP_V
+ ROT_90 = 4, // HAL_TRANSFORM_ROT_90
ROT_180 = FLIP_H | FLIP_V,
ROT_270 = ROT_180 | ROT_90,
ROT_INVALID = 0x80
@@ -61,32 +61,43 @@
};
// query the transform
- bool preserveRects() const;
- uint32_t getType() const;
- uint32_t getOrientation() const;
+ bool preserveRects() const;
+
+ // Returns if bilinear filtering is needed after applying this transform to avoid aliasing.
+ bool needsBilinearFiltering() const;
+
+ uint32_t getType() const;
+ uint32_t getOrientation() const;
bool operator==(const Transform& other) const;
const vec3& operator [] (size_t i) const; // returns column i
float tx() const;
float ty() const;
- float sx() const;
- float sy() const;
+ float dsdx() const;
+ float dtdx() const;
+ float dtdy() const;
+ float dsdy() const;
+
+ float getScaleX() const;
+ float getScaleY() const;
// modify the transform
void reset();
void set(float tx, float ty);
void set(float a, float b, float c, float d);
status_t set(uint32_t flags, float w, float h);
+ void set(const std::array<float, 9>& matrix);
// transform data
Rect makeBounds(int w, int h) const;
- vec2 transform(int x, int y) const;
+ vec2 transform(float x, float y) const;
Region transform(const Region& reg) const;
Rect transform(const Rect& bounds,
bool roundOutwards = false) const;
FloatRect transform(const FloatRect& bounds) const;
Transform& operator = (const Transform& other);
Transform operator * (const Transform& rhs) const;
+ Transform operator * (float value) const;
// assumes the last row is < 0 , 0 , 1 >
vec2 transform(const vec2& v) const;
vec3 transform(const vec3& v) const;
@@ -97,10 +108,10 @@
Transform inverse() const;
// for debugging
- void dump(std::string& result, const char* name) const;
- void dump(const char* name) const;
+ void dump(std::string& result, const char* name, const char* prefix = "") const;
+ void dump(const char* name, const char* prefix = "") const;
- static RotationFlags toRotationFlags(Rotation);
+ static constexpr RotationFlags toRotationFlags(Rotation);
private:
struct mat33 {
@@ -125,7 +136,7 @@
*os << out;
}
-inline Transform::RotationFlags Transform::toRotationFlags(Rotation rotation) {
+inline constexpr Transform::RotationFlags Transform::toRotationFlags(Rotation rotation) {
switch (rotation) {
case ROTATION_0:
return ROT_0;
diff --git a/libs/ui/include/ui/UiConfig.h b/libs/ui/include/ui/UiConfig.h
deleted file mode 100644
index d1d6014..0000000
--- a/libs/ui/include/ui/UiConfig.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2012 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.
- */
-
-#ifndef ANDROID_UI_CONFIG_H
-#define ANDROID_UI_CONFIG_H
-
-#include <string>
-
-namespace android {
-
-// Append the libui configuration details to configStr.
-void appendUiConfigString(std::string& configStr);
-
-}; // namespace android
-
-#endif /*ANDROID_UI_CONFIG_H*/
diff --git a/libs/ui/include_private/ui/FlattenableHelpers.h b/libs/ui/include_private/ui/FlattenableHelpers.h
new file mode 100644
index 0000000..378f37f
--- /dev/null
+++ b/libs/ui/include_private/ui/FlattenableHelpers.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <numeric>
+#include <optional>
+#include <type_traits>
+#include <vector>
+
+#include <utils/Flattenable.h>
+
+#define RETURN_IF_ERROR(op) \
+ if (const status_t status = (op); status != OK) return status;
+
+namespace android {
+
+struct FlattenableHelpers {
+ // Helpers for reading and writing POD structures which are not LightFlattenable.
+ template <class T,
+ typename = std::enable_if_t<
+ std::conjunction_v<std::is_trivially_copyable<T>,
+ std::negation<std::is_base_of<LightFlattenable<T>, T>>>>>
+ static constexpr size_t getFlattenedSize(const T&) {
+ return sizeof(T);
+ }
+
+ template <class T,
+ typename = std::enable_if_t<
+ std::conjunction_v<std::is_trivially_copyable<T>,
+ std::negation<std::is_base_of<LightFlattenable<T>, T>>>>>
+ static status_t flatten(void** buffer, size_t* size, const T& value) {
+ if (*size < sizeof(T)) return NO_MEMORY;
+ FlattenableUtils::write(*buffer, *size, value);
+ return OK;
+ }
+
+ template <class T,
+ typename = std::enable_if_t<
+ std::conjunction_v<std::is_trivially_copyable<T>,
+ std::negation<std::is_base_of<LightFlattenable<T>, T>>>>>
+ static status_t unflatten(const void** buffer, size_t* size, T* value) {
+ if (*size < sizeof(T)) return NO_MEMORY;
+ FlattenableUtils::read(*buffer, *size, *value);
+ return OK;
+ }
+
+ // Helpers for reading and writing std::string
+ static size_t getFlattenedSize(const std::string& str) {
+ return sizeof(uint64_t) + str.length();
+ }
+
+ static status_t flatten(void** buffer, size_t* size, const std::string& str) {
+ if (*size < getFlattenedSize(str)) return NO_MEMORY;
+ flatten(buffer, size, (uint64_t)str.length());
+ memcpy(reinterpret_cast<char*>(*buffer), str.c_str(), str.length());
+ FlattenableUtils::advance(*buffer, *size, str.length());
+ return OK;
+ }
+
+ static status_t unflatten(const void** buffer, size_t* size, std::string* str) {
+ uint64_t length;
+ RETURN_IF_ERROR(unflatten(buffer, size, &length));
+ if (*size < length) return NO_MEMORY;
+ str->assign(reinterpret_cast<const char*>(*buffer), length);
+ FlattenableUtils::advance(*buffer, *size, length);
+ return OK;
+ }
+
+ // Helpers for reading and writing LightFlattenable
+ template <class T>
+ static size_t getFlattenedSize(const LightFlattenable<T>& value) {
+ return value.getFlattenedSize();
+ }
+
+ template <class T>
+ static status_t flatten(void** buffer, size_t* size, const LightFlattenable<T>& value) {
+ RETURN_IF_ERROR(value.flatten(*buffer, *size));
+ FlattenableUtils::advance(*buffer, *size, value.getFlattenedSize());
+ return OK;
+ }
+
+ template <class T>
+ static status_t unflatten(const void** buffer, size_t* size, LightFlattenable<T>* value) {
+ RETURN_IF_ERROR(value->unflatten(*buffer, *size));
+ FlattenableUtils::advance(*buffer, *size, value->getFlattenedSize());
+ return OK;
+ }
+
+ // Helpers for reading and writing std::optional
+ template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>>
+ static size_t getFlattenedSize(const std::optional<T>& value) {
+ return sizeof(bool) + (value ? getFlattenedSize(*value) : 0);
+ }
+
+ template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>>
+ static status_t flatten(void** buffer, size_t* size, const std::optional<T>& value) {
+ if (value) {
+ RETURN_IF_ERROR(flatten(buffer, size, true));
+ RETURN_IF_ERROR(flatten(buffer, size, *value));
+ } else {
+ RETURN_IF_ERROR(flatten(buffer, size, false));
+ }
+ return OK;
+ }
+
+ template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>>
+ static status_t unflatten(const void** buffer, size_t* size, std::optional<T>* value) {
+ bool isPresent;
+ RETURN_IF_ERROR(unflatten(buffer, size, &isPresent));
+ if (isPresent) {
+ *value = T();
+ RETURN_IF_ERROR(unflatten(buffer, size, &(**value)));
+ } else {
+ value->reset();
+ }
+ return OK;
+ }
+
+ // Helpers for reading and writing std::vector
+ template <class T>
+ static size_t getFlattenedSize(const std::vector<T>& value) {
+ return std::accumulate(value.begin(), value.end(), sizeof(uint64_t),
+ [](size_t sum, const T& element) {
+ return sum + getFlattenedSize(element);
+ });
+ }
+
+ template <class T>
+ static status_t flatten(void** buffer, size_t* size, const std::vector<T>& value) {
+ RETURN_IF_ERROR(flatten(buffer, size, (uint64_t)value.size()));
+ for (const auto& element : value) {
+ RETURN_IF_ERROR(flatten(buffer, size, element));
+ }
+ return OK;
+ }
+
+ template <class T>
+ static status_t unflatten(const void** buffer, size_t* size, std::vector<T>* value) {
+ uint64_t numElements;
+ RETURN_IF_ERROR(unflatten(buffer, size, &numElements));
+ // We don't need an extra size check since each iteration of the loop does that
+ std::vector<T> elements;
+ for (size_t i = 0; i < numElements; i++) {
+ T element;
+ RETURN_IF_ERROR(unflatten(buffer, size, &element));
+ elements.push_back(element);
+ }
+ *value = std::move(elements);
+ return OK;
+ }
+};
+
+} // namespace android
+
+#undef RETURN_IF_ERROR
\ No newline at end of file
diff --git a/libs/ui/include/ui/ColorSpace.h b/libs/ui/include_types/ui/ColorSpace.h
similarity index 100%
rename from libs/ui/include/ui/ColorSpace.h
rename to libs/ui/include_types/ui/ColorSpace.h
diff --git a/libs/ui/include_vndk/ui/ColorSpace.h b/libs/ui/include_vndk/ui/ColorSpace.h
index ddf70d5..7d2a6d3 120000
--- a/libs/ui/include_vndk/ui/ColorSpace.h
+++ b/libs/ui/include_vndk/ui/ColorSpace.h
@@ -1 +1 @@
-../../include/ui/ColorSpace.h
\ No newline at end of file
+../../include_types/ui/ColorSpace.h
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/DisplayConfig.h b/libs/ui/include_vndk/ui/DisplayConfig.h
deleted file mode 120000
index 1450319..0000000
--- a/libs/ui/include_vndk/ui/DisplayConfig.h
+++ /dev/null
@@ -1 +0,0 @@
-../../include/ui/DisplayConfig.h
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/DisplayId.h b/libs/ui/include_vndk/ui/DisplayId.h
new file mode 120000
index 0000000..73c9fe8
--- /dev/null
+++ b/libs/ui/include_vndk/ui/DisplayId.h
@@ -0,0 +1 @@
+../../include/ui/DisplayId.h
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/DisplayInfo.h b/libs/ui/include_vndk/ui/DisplayInfo.h
deleted file mode 120000
index 75f14cf..0000000
--- a/libs/ui/include_vndk/ui/DisplayInfo.h
+++ /dev/null
@@ -1 +0,0 @@
-../../include/ui/DisplayInfo.h
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/DisplayMode.h b/libs/ui/include_vndk/ui/DisplayMode.h
new file mode 120000
index 0000000..c87754a
--- /dev/null
+++ b/libs/ui/include_vndk/ui/DisplayMode.h
@@ -0,0 +1 @@
+../../include/ui/DisplayMode.h
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/PhysicalDisplayId.h b/libs/ui/include_vndk/ui/PhysicalDisplayId.h
deleted file mode 120000
index 6e3fb1e..0000000
--- a/libs/ui/include_vndk/ui/PhysicalDisplayId.h
+++ /dev/null
@@ -1 +0,0 @@
-../../include/ui/PhysicalDisplayId.h
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/StaticDisplayInfo.h b/libs/ui/include_vndk/ui/StaticDisplayInfo.h
new file mode 120000
index 0000000..541a7a3
--- /dev/null
+++ b/libs/ui/include_vndk/ui/StaticDisplayInfo.h
@@ -0,0 +1 @@
+../../include/ui/StaticDisplayInfo.h
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/UiConfig.h b/libs/ui/include_vndk/ui/UiConfig.h
deleted file mode 120000
index f580ce1..0000000
--- a/libs/ui/include_vndk/ui/UiConfig.h
+++ /dev/null
@@ -1 +0,0 @@
-../../include/ui/UiConfig.h
\ No newline at end of file
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index bc53346..516aad8 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -38,6 +38,20 @@
}
cc_test {
+ name: "DisplayId_test",
+ shared_libs: ["libui"],
+ srcs: ["DisplayId_test.cpp"],
+ cflags: ["-Wall", "-Werror"],
+}
+
+cc_test {
+ name: "FlattenableHelpers_test",
+ shared_libs: ["libui"],
+ srcs: ["FlattenableHelpers_test.cpp"],
+ cflags: ["-Wall", "-Werror"],
+}
+
+cc_test {
name: "GraphicBufferAllocator_test",
header_libs: [
"libnativewindow_headers",
@@ -87,6 +101,14 @@
}
cc_test {
+ name: "Rect_test",
+ test_suites: ["device-tests"],
+ shared_libs: ["libui"],
+ srcs: ["Rect_test.cpp"],
+ cflags: ["-Wall", "-Werror"],
+}
+
+cc_test {
name: "Size_test",
test_suites: ["device-tests"],
shared_libs: ["libui"],
diff --git a/libs/ui/tests/DisplayId_test.cpp b/libs/ui/tests/DisplayId_test.cpp
new file mode 100644
index 0000000..1d908b8
--- /dev/null
+++ b/libs/ui/tests/DisplayId_test.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2020 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 <ui/DisplayId.h>
+
+#include <gtest/gtest.h>
+
+namespace android::ui {
+
+TEST(DisplayIdTest, createPhysicalIdFromEdid) {
+ constexpr uint8_t port = 1;
+ constexpr uint16_t manufacturerId = 13;
+ constexpr uint32_t modelHash = 42;
+ PhysicalDisplayId id = PhysicalDisplayId::fromEdid(port, manufacturerId, modelHash);
+ EXPECT_EQ(port, id.getPort());
+ EXPECT_EQ(manufacturerId, id.getManufacturerId());
+ EXPECT_FALSE(VirtualDisplayId::tryCast(id));
+ EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
+ EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
+ EXPECT_TRUE(PhysicalDisplayId::tryCast(id));
+ EXPECT_TRUE(HalDisplayId::tryCast(id));
+}
+
+TEST(DisplayIdTest, createPhysicalIdFromPort) {
+ constexpr uint8_t port = 3;
+ PhysicalDisplayId id = PhysicalDisplayId::fromPort(port);
+ EXPECT_EQ(port, id.getPort());
+ EXPECT_FALSE(VirtualDisplayId::tryCast(id));
+ EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
+ EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
+ EXPECT_TRUE(PhysicalDisplayId::tryCast(id));
+ EXPECT_TRUE(HalDisplayId::tryCast(id));
+}
+
+TEST(DisplayIdTest, createGpuVirtualId) {
+ GpuVirtualDisplayId id(42);
+ EXPECT_TRUE(VirtualDisplayId::tryCast(id));
+ EXPECT_TRUE(GpuVirtualDisplayId::tryCast(id));
+ EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
+ EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
+ EXPECT_FALSE(HalDisplayId::tryCast(id));
+}
+
+TEST(DisplayIdTest, createHalVirtualId) {
+ HalVirtualDisplayId id(42);
+ EXPECT_TRUE(VirtualDisplayId::tryCast(id));
+ EXPECT_TRUE(HalVirtualDisplayId::tryCast(id));
+ EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
+ EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
+ EXPECT_TRUE(HalDisplayId::tryCast(id));
+}
+
+} // namespace android::ui
diff --git a/libs/ui/tests/FlattenableHelpers_test.cpp b/libs/ui/tests/FlattenableHelpers_test.cpp
new file mode 100644
index 0000000..44e20b5
--- /dev/null
+++ b/libs/ui/tests/FlattenableHelpers_test.cpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#define LOG_TAG "FlattenableHelpersTest"
+
+#include <ui/FlattenableHelpers.h>
+
+#include <gtest/gtest.h>
+#include <utils/Flattenable.h>
+#include <cstdint>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+namespace android {
+
+namespace {
+
+struct TestLightFlattenable : LightFlattenable<TestLightFlattenable> {
+ std::unique_ptr<int32_t> ptr;
+
+ bool isFixedSize() const { return true; }
+ size_t getFlattenedSize() const { return sizeof(int32_t); }
+
+ status_t flatten(void* buffer, size_t size) const {
+ FlattenableUtils::write(buffer, size, *ptr);
+ return OK;
+ }
+
+ status_t unflatten(void const* buffer, size_t size) {
+ int32_t value;
+ FlattenableUtils::read(buffer, size, value);
+ ptr = std::make_unique<int32_t>(value);
+ return OK;
+ }
+};
+
+class FlattenableHelpersTest : public testing::Test {
+public:
+ template <class T>
+ void testWriteThenRead(const T& value, size_t bufferSize) {
+ std::vector<int8_t> buffer(bufferSize);
+ auto rawBuffer = reinterpret_cast<void*>(buffer.data());
+ size_t size = buffer.size();
+ ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value));
+
+ auto rawReadBuffer = reinterpret_cast<const void*>(buffer.data());
+ size = buffer.size();
+ T valueRead;
+ ASSERT_EQ(OK, FlattenableHelpers::unflatten(&rawReadBuffer, &size, &valueRead));
+ EXPECT_EQ(value, valueRead);
+ }
+
+ template <class T>
+ void testTriviallyCopyable(const T& value) {
+ testWriteThenRead(value, sizeof(T));
+ }
+
+ template <class T>
+ void testWriteThenRead(const T& value) {
+ testWriteThenRead(value, FlattenableHelpers::getFlattenedSize(value));
+ }
+};
+
+TEST_F(FlattenableHelpersTest, TriviallyCopyable) {
+ testTriviallyCopyable(42);
+ testTriviallyCopyable(1LL << 63);
+ testTriviallyCopyable(false);
+ testTriviallyCopyable(true);
+ testTriviallyCopyable(std::optional<int>());
+ testTriviallyCopyable(std::optional<int>(4));
+}
+
+TEST_F(FlattenableHelpersTest, String) {
+ testWriteThenRead(std::string("Android"));
+ testWriteThenRead(std::string());
+}
+
+TEST_F(FlattenableHelpersTest, Vector) {
+ testWriteThenRead(std::vector<int>({1, 2, 3}));
+ testWriteThenRead(std::vector<int>());
+}
+
+TEST_F(FlattenableHelpersTest, OptionalOfLightFlattenable) {
+ std::vector<size_t> buffer;
+ constexpr int kInternalValue = 16;
+ {
+ std::optional<TestLightFlattenable> value =
+ TestLightFlattenable{.ptr = std::make_unique<int32_t>(kInternalValue)};
+ buffer.assign(FlattenableHelpers::getFlattenedSize(value), 0);
+ void* rawBuffer = reinterpret_cast<void*>(buffer.data());
+ size_t size = buffer.size();
+ ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value));
+ }
+
+ const void* rawReadBuffer = reinterpret_cast<const void*>(buffer.data());
+ size_t size = buffer.size();
+ std::optional<TestLightFlattenable> valueRead;
+ ASSERT_EQ(OK, FlattenableHelpers::unflatten(&rawReadBuffer, &size, &valueRead));
+ ASSERT_TRUE(valueRead.has_value());
+ EXPECT_EQ(kInternalValue, *valueRead->ptr);
+}
+
+TEST_F(FlattenableHelpersTest, NullOptionalOfLightFlattenable) {
+ std::vector<size_t> buffer;
+ {
+ std::optional<TestLightFlattenable> value;
+ buffer.assign(FlattenableHelpers::getFlattenedSize(value), 0);
+ void* rawBuffer = reinterpret_cast<void*>(buffer.data());
+ size_t size = buffer.size();
+ ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value));
+ }
+
+ const void* rawReadBuffer = reinterpret_cast<const void*>(buffer.data());
+ size_t size = buffer.size();
+ std::optional<TestLightFlattenable> valueRead;
+ ASSERT_EQ(OK, FlattenableHelpers::unflatten(&rawReadBuffer, &size, &valueRead));
+ ASSERT_FALSE(valueRead.has_value());
+}
+
+// If a struct is both trivially copyable and light flattenable we should treat it
+// as LigthFlattenable.
+TEST_F(FlattenableHelpersTest, TriviallyCopyableAndLightFlattenableIsFlattenedAsLightFlattenable) {
+ static constexpr int32_t kSizeTag = 1234567;
+ static constexpr int32_t kFlattenTag = 987654;
+ static constexpr int32_t kUnflattenTag = 5926582;
+
+ struct LightFlattenableAndTriviallyCopyable
+ : LightFlattenable<LightFlattenableAndTriviallyCopyable> {
+ int32_t value;
+
+ bool isFixedSize() const { return true; }
+ size_t getFlattenedSize() const { return kSizeTag; }
+
+ status_t flatten(void* buffer, size_t size) const {
+ FlattenableUtils::write(buffer, size, kFlattenTag);
+ return OK;
+ }
+
+ status_t unflatten(void const*, size_t) {
+ value = kUnflattenTag;
+ return OK;
+ }
+ };
+
+ {
+ // Verify that getFlattenedSize uses the LightFlattenable overload
+ LightFlattenableAndTriviallyCopyable foo;
+ EXPECT_EQ(kSizeTag, FlattenableHelpers::getFlattenedSize(foo));
+ }
+
+ {
+ // Verify that flatten uses the LightFlattenable overload
+ std::vector<int8_t> buffer(sizeof(int32_t));
+ auto rawBuffer = reinterpret_cast<void*>(buffer.data());
+ size_t size = buffer.size();
+ LightFlattenableAndTriviallyCopyable foo;
+ ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, foo));
+
+ auto rawReadBuffer = reinterpret_cast<const void*>(buffer.data());
+ int32_t value;
+ FlattenableHelpers::unflatten(&rawReadBuffer, &size, &value);
+ EXPECT_EQ(kFlattenTag, value);
+ }
+
+ {
+ // Verify that unflatten uses the LightFlattenable overload
+ std::vector<int8_t> buffer(sizeof(int32_t));
+ auto rawBuffer = reinterpret_cast<void*>(buffer.data());
+ size_t size = buffer.size();
+ int32_t value = 4;
+ ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value));
+
+ auto rawReadBuffer = reinterpret_cast<const void*>(buffer.data());
+
+ LightFlattenableAndTriviallyCopyable foo;
+ FlattenableHelpers::unflatten(&rawReadBuffer, &size, &foo);
+ EXPECT_EQ(kUnflattenTag, foo.value);
+ }
+}
+
+} // namespace
+} // namespace android
diff --git a/libs/ui/tests/Rect_test.cpp b/libs/ui/tests/Rect_test.cpp
new file mode 100644
index 0000000..9cc36bb
--- /dev/null
+++ b/libs/ui/tests/Rect_test.cpp
@@ -0,0 +1,291 @@
+/*
+ * Copyright 2020 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 <system/graphics.h>
+#include <ui/FloatRect.h>
+#include <ui/Point.h>
+#include <ui/Rect.h>
+#include <ui/Size.h>
+
+#include <gtest/gtest.h>
+
+namespace android::ui {
+
+TEST(RectTest, constructDefault) {
+ const Rect rect;
+ EXPECT_FALSE(rect.isValid());
+ EXPECT_TRUE(rect.isEmpty());
+}
+
+TEST(RectTest, constructFromWidthAndHeight) {
+ const Rect rect(100, 200);
+ EXPECT_TRUE(rect.isValid());
+ EXPECT_FALSE(rect.isEmpty());
+ EXPECT_EQ(0, rect.top);
+ EXPECT_EQ(0, rect.left);
+ EXPECT_EQ(100, rect.right);
+ EXPECT_EQ(200, rect.bottom);
+ EXPECT_EQ(100, rect.getWidth());
+ EXPECT_EQ(200, rect.getHeight());
+}
+
+TEST(RectTest, constructFromSize) {
+ const Rect rect(Size(100, 200));
+ EXPECT_TRUE(rect.isValid());
+ EXPECT_FALSE(rect.isEmpty());
+ EXPECT_EQ(0, rect.top);
+ EXPECT_EQ(0, rect.left);
+ EXPECT_EQ(100, rect.right);
+ EXPECT_EQ(200, rect.bottom);
+ EXPECT_EQ(100, rect.getWidth());
+ EXPECT_EQ(200, rect.getHeight());
+}
+
+TEST(RectTest, constructFromLTRB) {
+ const Rect rect(11, 12, 14, 14);
+ EXPECT_TRUE(rect.isValid());
+ EXPECT_FALSE(rect.isEmpty());
+ EXPECT_EQ(11, rect.left);
+ EXPECT_EQ(12, rect.top);
+ EXPECT_EQ(14, rect.right);
+ EXPECT_EQ(14, rect.bottom);
+ EXPECT_EQ(3, rect.getWidth());
+ EXPECT_EQ(2, rect.getHeight());
+}
+
+TEST(RectTest, constructFromPoints) {
+ const Rect rect(Point(11, 12), Point(14, 14));
+ EXPECT_TRUE(rect.isValid());
+ EXPECT_FALSE(rect.isEmpty());
+ EXPECT_EQ(11, rect.left);
+ EXPECT_EQ(12, rect.top);
+ EXPECT_EQ(14, rect.right);
+ EXPECT_EQ(14, rect.bottom);
+ EXPECT_EQ(3, rect.getWidth());
+ EXPECT_EQ(2, rect.getHeight());
+}
+
+TEST(RectTest, constructFromFloatRect) {
+ {
+ const Rect rect(FloatRect(10, 20, 30, 40));
+ EXPECT_TRUE(rect.isValid());
+ EXPECT_FALSE(rect.isEmpty());
+ EXPECT_EQ(10, rect.left);
+ EXPECT_EQ(20, rect.top);
+ EXPECT_EQ(30, rect.right);
+ EXPECT_EQ(40, rect.bottom);
+ }
+ // Construct with floating point error
+ {
+ constexpr float kError = 1e-3;
+ const Rect rect(FloatRect(10 - kError, 20 - kError, 30 - kError, 40 - kError));
+ EXPECT_TRUE(rect.isValid());
+ EXPECT_FALSE(rect.isEmpty());
+ EXPECT_EQ(10, rect.left);
+ EXPECT_EQ(20, rect.top);
+ EXPECT_EQ(30, rect.right);
+ EXPECT_EQ(40, rect.bottom);
+ }
+}
+
+TEST(RectTest, makeInvalid) {
+ Rect rect(10, 20, 60, 60);
+ EXPECT_TRUE(rect.isValid());
+ rect.makeInvalid();
+ EXPECT_FALSE(rect.isValid());
+}
+
+TEST(RectTest, clear) {
+ Rect rect(10, 20, 60, 60);
+ EXPECT_FALSE(rect.isEmpty());
+ rect.clear();
+ EXPECT_TRUE(rect.isEmpty());
+}
+
+TEST(RectTest, getSize) {
+ const Rect rect(10, 20, 60, 60);
+ EXPECT_EQ(Size(50, 40), rect.getSize());
+}
+
+TEST(RectTest, getBounds) {
+ const Rect rect(10, 20, 60, 60);
+ const Rect bounds = rect.getBounds();
+ EXPECT_EQ(0, bounds.left);
+ EXPECT_EQ(0, bounds.top);
+ EXPECT_EQ(50, bounds.right);
+ EXPECT_EQ(40, bounds.bottom);
+ EXPECT_EQ(rect.getSize(), bounds.getSize());
+}
+
+TEST(RectTest, getCornerPoints) {
+ const Rect rect(10, 20, 50, 60);
+ EXPECT_EQ(Point(10, 20), rect.leftTop());
+ EXPECT_EQ(Point(10, 60), rect.leftBottom());
+ EXPECT_EQ(Point(50, 20), rect.rightTop());
+ EXPECT_EQ(Point(50, 60), rect.rightBottom());
+}
+
+TEST(RectTest, operatorEquals) {
+ const Rect rect(10, 20, 50, 60);
+ EXPECT_EQ(rect, rect);
+ EXPECT_NE(Rect(0, 20, 50, 60), rect);
+ EXPECT_NE(Rect(10, 0, 50, 60), rect);
+ EXPECT_NE(Rect(10, 20, 0, 60), rect);
+ EXPECT_NE(Rect(10, 20, 50, 0), rect);
+}
+
+TEST(RectTest, operatorsPlusMinus) {
+ Rect rect = Rect(10, 20, 50, 60) + Point(1, 2);
+ EXPECT_EQ(Rect(11, 22, 51, 62), rect);
+ rect -= Point(1, 2);
+ EXPECT_EQ(Rect(10, 20, 50, 60), rect);
+
+ rect = Rect(10, 20, 50, 60) - Point(1, 2);
+ EXPECT_EQ(Rect(9, 18, 49, 58), rect);
+ rect += Point(1, 2);
+ EXPECT_EQ(Rect(10, 20, 50, 60), rect);
+}
+
+TEST(RectTest, scale) {
+ Rect rect(10, 20, 50, 60);
+ EXPECT_EQ(Rect(20, 60, 100, 180), rect.scale(2.f, 3.f));
+ rect.scaleSelf(2.f, 3.f);
+ EXPECT_EQ(Rect(20, 60, 100, 180), rect);
+
+ rect = Rect(10, 20, 50, 60);
+ constexpr float kError = 1e-3;
+ EXPECT_EQ(Rect(20, 60, 100, 180), rect.scale(2.f - kError, 3.f - kError));
+ rect.scaleSelf(2.f - kError, 3.f - kError);
+ EXPECT_EQ(Rect(20, 60, 100, 180), rect);
+}
+
+TEST(RectTest, inset) {
+ Rect rect(10, 20, 50, 60);
+ rect.inset(0, 0, 0, 0);
+ EXPECT_EQ(Rect(10, 20, 50, 60), rect);
+ rect.inset(1, 2, 3, 4);
+ EXPECT_EQ(Rect(11, 22, 47, 56), rect);
+}
+
+TEST(RectTest, intersect) {
+ const Rect rect(10, 20, 50, 60);
+ Rect intersection;
+
+ // Intersect with self is self
+ intersection.makeInvalid();
+ EXPECT_TRUE(rect.intersect(rect, &intersection));
+ EXPECT_EQ(Rect(10, 20, 50, 60), intersection);
+
+ // Intersect with rect contained in us
+ const Rect insideRect(11, 21, 45, 55);
+ intersection.makeInvalid();
+ EXPECT_TRUE(rect.intersect(insideRect, &intersection));
+ EXPECT_EQ(insideRect, intersection);
+
+ // Intersect with rect we are contained in
+ intersection.makeInvalid();
+ EXPECT_TRUE(insideRect.intersect(rect, &intersection));
+ EXPECT_EQ(insideRect, intersection);
+
+ // Empty intersection
+ intersection.makeInvalid();
+ EXPECT_FALSE(rect.intersect(Rect(100, 202, 150, 260), &intersection));
+ EXPECT_TRUE(intersection.isEmpty());
+
+ // Partial intersection
+ const Rect other(30, 40, 70, 80);
+ intersection.makeInvalid();
+ EXPECT_TRUE(rect.intersect(other, &intersection));
+ EXPECT_EQ(Rect(30, 40, 50, 60), intersection);
+
+ // Intersetion is commutative
+ intersection.makeInvalid();
+ EXPECT_TRUE(other.intersect(rect, &intersection));
+ EXPECT_EQ(Rect(30, 40, 50, 60), intersection);
+}
+
+TEST(RectTest, reduce) {
+ const Rect rect(10, 20, 50, 60);
+
+ // Reduce with self is empty
+ EXPECT_TRUE(rect.reduce(rect).isEmpty());
+
+ // Reduce with rect entirely inside is a noop
+ const Rect insideRect(11, 21, 45, 55);
+ EXPECT_EQ(rect, rect.reduce(insideRect));
+
+ // Reduce with rect entirely outside is empty
+ EXPECT_TRUE(insideRect.reduce(rect).isEmpty());
+
+ // Reduce with rect on the right
+ EXPECT_EQ(Rect(10, 20, 20, 60), rect.reduce(Rect(20, 0, 60, 70)));
+
+ // Reduce with rect on the left
+ EXPECT_EQ(Rect(40, 20, 50, 60), rect.reduce(Rect(0, 0, 40, 70)));
+
+ // Reduce with rect at the top
+ EXPECT_EQ(Rect(10, 40, 50, 60), rect.reduce(Rect(0, 0, 70, 40)));
+
+ // Reduce with rect at the bottom
+ EXPECT_EQ(Rect(10, 20, 50, 40), rect.reduce(Rect(0, 40, 70, 70)));
+}
+
+TEST(RectTest, transform) {
+ const int32_t width = 100, height = 200;
+ const Rect rect(1, 1, 2, 3);
+ EXPECT_EQ(Rect(98, 1, 99, 3), rect.transform(HAL_TRANSFORM_FLIP_H, width, height));
+ EXPECT_EQ(Rect(1, 197, 2, 199), rect.transform(HAL_TRANSFORM_FLIP_V, width, height));
+ EXPECT_EQ(Rect(197, 1, 199, 2), rect.transform(HAL_TRANSFORM_ROT_90, width, height));
+ EXPECT_EQ(Rect(98, 197, 99, 199), rect.transform(HAL_TRANSFORM_ROT_180, width, height));
+ EXPECT_EQ(Rect(1, 98, 3, 99), rect.transform(HAL_TRANSFORM_ROT_270, width, height));
+}
+
+TEST(RectTest, toFloatRect) {
+ const Rect rect(10, 20, 50, 60);
+ const FloatRect floatRect = rect.toFloatRect();
+ EXPECT_EQ(FloatRect(10.f, 20.f, 50.f, 60.f), floatRect);
+}
+
+TEST(RectTest, RectHash) {
+ const std::vector<Rect> rects = {
+ Rect(10, 20, 50, 60), Rect(11, 20, 50, 60), Rect(11, 21, 50, 60),
+ Rect(11, 21, 51, 60), Rect(11, 21, 51, 61),
+ };
+
+ for (const auto& a : rects) {
+ for (const auto& b : rects) {
+ const bool hashEq = std::hash<Rect>{}(a) == std::hash<Rect>{}(b);
+ EXPECT_EQ(a == b, hashEq);
+ }
+ }
+}
+
+TEST(RectTest, FloatRectHash) {
+ const std::vector<FloatRect> floatRects = {
+ Rect(10, 20, 50, 60).toFloatRect(), Rect(11, 20, 50, 60).toFloatRect(),
+ Rect(11, 21, 50, 60).toFloatRect(), Rect(11, 21, 51, 60).toFloatRect(),
+ Rect(11, 21, 51, 61).toFloatRect(),
+ };
+
+ for (const auto& a : floatRects) {
+ for (const auto& b : floatRects) {
+ const bool hashEq = std::hash<FloatRect>{}(a) == std::hash<FloatRect>{}(b);
+ EXPECT_EQ(a == b, hashEq);
+ }
+ }
+}
+
+} // namespace android::ui
diff --git a/libs/ui/tests/Region_test.cpp b/libs/ui/tests/Region_test.cpp
index c6b826d..74924bd 100644
--- a/libs/ui/tests/Region_test.cpp
+++ b/libs/ui/tests/Region_test.cpp
@@ -167,5 +167,17 @@
ASSERT_TRUE(touchableRegion.contains(50, 50));
}
+TEST_F(RegionTest, RegionHash) {
+ Region region1;
+ region1.addRectUnchecked(10, 20, 30, 40);
+ region1.addRectUnchecked(40, 30, 20, 10);
+
+ Region region2;
+ region2.addRectUnchecked(11, 20, 30, 40);
+ region2.addRectUnchecked(40, 31, 20, 10);
+
+ EXPECT_NE(std::hash<Region>{}(region1), std::hash<Region>{}(region2));
+}
+
}; // namespace android
diff --git a/libs/ui/tests/Size_test.cpp b/libs/ui/tests/Size_test.cpp
index 38f37ad..5f75aea 100644
--- a/libs/ui/tests/Size_test.cpp
+++ b/libs/ui/tests/Size_test.cpp
@@ -33,8 +33,7 @@
#include <gtest/gtest.h>
-namespace android {
-namespace ui {
+namespace android::ui {
TEST(SizeTest, BasicConstructionAndEqualityComparison) {
Size s(123, 456);
@@ -215,5 +214,4 @@
ClampTest(uint32_t(0), int32_t(0));
}
-} // namespace ui
-} // namespace android
+} // namespace android::ui
diff --git a/libs/ui/tests/TEST_MAPPING b/libs/ui/tests/TEST_MAPPING
index 7fcd7de..eece18e 100644
--- a/libs/ui/tests/TEST_MAPPING
+++ b/libs/ui/tests/TEST_MAPPING
@@ -2,6 +2,9 @@
"presubmit": [
{
"name": "Size_test"
+ },
+ {
+ "name": "Rect_test"
}
]
}
diff --git a/libs/vibrator/Android.bp b/libs/vibrator/Android.bp
index 11b09bd..83c250a 100644
--- a/libs/vibrator/Android.bp
+++ b/libs/vibrator/Android.bp
@@ -23,6 +23,8 @@
cc_library {
name: "libvibrator",
+ vendor_available: true,
+ double_loadable: true,
shared_libs: [
"libbinder",
diff --git a/libs/vibrator/ExternalVibrationUtils.cpp b/libs/vibrator/ExternalVibrationUtils.cpp
new file mode 100644
index 0000000..749c568
--- /dev/null
+++ b/libs/vibrator/ExternalVibrationUtils.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2020 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 <cstring>
+
+#include <math.h>
+
+#include <vibrator/ExternalVibrationUtils.h>
+
+namespace android::os {
+
+namespace {
+static constexpr float HAPTIC_SCALE_VERY_LOW_RATIO = 2.0f / 3.0f;
+static constexpr float HAPTIC_SCALE_LOW_RATIO = 3.0f / 4.0f;
+static constexpr float HAPTIC_MAX_AMPLITUDE_FLOAT = 1.0f;
+
+float getHapticScaleGamma(HapticScale scale) {
+ switch (scale) {
+ case HapticScale::VERY_LOW:
+ return 2.0f;
+ case HapticScale::LOW:
+ return 1.5f;
+ case HapticScale::HIGH:
+ return 0.5f;
+ case HapticScale::VERY_HIGH:
+ return 0.25f;
+ default:
+ return 1.0f;
+ }
+}
+
+float getHapticMaxAmplitudeRatio(HapticScale scale) {
+ switch (scale) {
+ case HapticScale::VERY_LOW:
+ return HAPTIC_SCALE_VERY_LOW_RATIO;
+ case HapticScale::LOW:
+ return HAPTIC_SCALE_LOW_RATIO;
+ case HapticScale::NONE:
+ case HapticScale::HIGH:
+ case HapticScale::VERY_HIGH:
+ return 1.0f;
+ default:
+ return 0.0f;
+ }
+}
+
+} // namespace
+
+bool isValidHapticScale(HapticScale scale) {
+ switch (scale) {
+ case HapticScale::MUTE:
+ case HapticScale::VERY_LOW:
+ case HapticScale::LOW:
+ case HapticScale::NONE:
+ case HapticScale::HIGH:
+ case HapticScale::VERY_HIGH:
+ return true;
+ }
+ return false;
+}
+
+void scaleHapticData(float* buffer, size_t length, HapticScale scale) {
+ if (!isValidHapticScale(scale) || scale == HapticScale::NONE) {
+ return;
+ }
+ if (scale == HapticScale::MUTE) {
+ memset(buffer, 0, length * sizeof(float));
+ return;
+ }
+ float gamma = getHapticScaleGamma(scale);
+ float maxAmplitudeRatio = getHapticMaxAmplitudeRatio(scale);
+ for (size_t i = 0; i < length; i++) {
+ float sign = buffer[i] >= 0 ? 1.0 : -1.0;
+ buffer[i] = powf(fabsf(buffer[i] / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma)
+ * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * sign;
+ }
+}
+
+} // namespace android::os
diff --git a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
new file mode 100644
index 0000000..20045d0
--- /dev/null
+++ b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef ANDROID_EXTERNAL_VIBRATION_UTILS_H
+#define ANDROID_EXTERNAL_VIBRATION_UTILS_H
+
+#include <android/os/IExternalVibratorService.h>
+
+namespace android::os {
+
+enum class HapticScale {
+ MUTE = IExternalVibratorService::SCALE_MUTE,
+ VERY_LOW = IExternalVibratorService::SCALE_VERY_LOW,
+ LOW = IExternalVibratorService::SCALE_LOW,
+ NONE = IExternalVibratorService::SCALE_NONE,
+ HIGH = IExternalVibratorService::SCALE_HIGH,
+ VERY_HIGH = IExternalVibratorService::SCALE_VERY_HIGH,
+};
+
+bool isValidHapticScale(HapticScale scale);
+
+void scaleHapticData(float* buffer, size_t length, HapticScale scale);
+
+} // namespace android::os
+
+#endif // ANDROID_EXTERNAL_VIBRATION_UTILS_H
diff --git a/libs/vr/libbroadcastring/Android.bp b/libs/vr/libbroadcastring/Android.bp
index 2eb2f9f..d4538f1 100644
--- a/libs/vr/libbroadcastring/Android.bp
+++ b/libs/vr/libbroadcastring/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp
index 45bdd35..583ad1d 100644
--- a/libs/vr/libbufferhub/Android.bp
+++ b/libs/vr/libbufferhub/Android.bp
@@ -18,8 +18,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libbufferhubqueue/Android.bp b/libs/vr/libbufferhubqueue/Android.bp
index f372bd7..0bda798 100644
--- a/libs/vr/libbufferhubqueue/Android.bp
+++ b/libs/vr/libbufferhubqueue/Android.bp
@@ -18,8 +18,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libbufferhubqueue/benchmarks/Android.bp b/libs/vr/libbufferhubqueue/benchmarks/Android.bp
index fc1f376..e33e03b 100644
--- a/libs/vr/libbufferhubqueue/benchmarks/Android.bp
+++ b/libs/vr/libbufferhubqueue/benchmarks/Android.bp
@@ -5,8 +5,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libbufferhubqueue/tests/Android.bp b/libs/vr/libbufferhubqueue/tests/Android.bp
index e883916..33a0d75 100644
--- a/libs/vr/libbufferhubqueue/tests/Android.bp
+++ b/libs/vr/libbufferhubqueue/tests/Android.bp
@@ -5,8 +5,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libdisplay/Android.bp b/libs/vr/libdisplay/Android.bp
index 365a676..b0ed950 100644
--- a/libs/vr/libdisplay/Android.bp
+++ b/libs/vr/libdisplay/Android.bp
@@ -18,8 +18,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libdvr/Android.bp b/libs/vr/libdvr/Android.bp
index 83c30d7..96023dd 100644
--- a/libs/vr/libdvr/Android.bp
+++ b/libs/vr/libdvr/Android.bp
@@ -19,8 +19,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libdvr/tests/Android.bp b/libs/vr/libdvr/tests/Android.bp
index 4ed80a4..fe7feb8 100644
--- a/libs/vr/libdvr/tests/Android.bp
+++ b/libs/vr/libdvr/tests/Android.bp
@@ -18,8 +18,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libdvrcommon/Android.bp b/libs/vr/libdvrcommon/Android.bp
index 9e1e516..fe4dfc7 100644
--- a/libs/vr/libdvrcommon/Android.bp
+++ b/libs/vr/libdvrcommon/Android.bp
@@ -18,8 +18,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libpdx_default_transport/Android.bp b/libs/vr/libpdx_default_transport/Android.bp
index ea73d7a..8046857 100644
--- a/libs/vr/libpdx_default_transport/Android.bp
+++ b/libs/vr/libpdx_default_transport/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libpdx_uds/Android.bp b/libs/vr/libpdx_uds/Android.bp
index 532d1a7..216ca9f 100644
--- a/libs/vr/libpdx_uds/Android.bp
+++ b/libs/vr/libpdx_uds/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libperformance/Android.bp b/libs/vr/libperformance/Android.bp
index 5beee35..38bf4ea 100644
--- a/libs/vr/libperformance/Android.bp
+++ b/libs/vr/libperformance/Android.bp
@@ -18,8 +18,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp
index 8aca9a5..bf848af 100644
--- a/libs/vr/libvrflinger/Android.bp
+++ b/libs/vr/libvrflinger/Android.bp
@@ -18,8 +18,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libvrflinger/tests/Android.bp b/libs/vr/libvrflinger/tests/Android.bp
index dafd354..095f556 100644
--- a/libs/vr/libvrflinger/tests/Android.bp
+++ b/libs/vr/libvrflinger/tests/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libvrsensor/Android.bp b/libs/vr/libvrsensor/Android.bp
index 8f566a0..40a5099 100644
--- a/libs/vr/libvrsensor/Android.bp
+++ b/libs/vr/libvrsensor/Android.bp
@@ -18,8 +18,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/opengl/Android.bp b/opengl/Android.bp
index 16ce15b..b15694b 100644
--- a/opengl/Android.bp
+++ b/opengl/Android.bp
@@ -66,6 +66,7 @@
cc_library_headers {
name: "gl_headers",
+ host_supported: true,
vendor_available: true,
export_include_dirs: ["include"],
llndk: {
diff --git a/opengl/include/EGL/eglext_angle.h b/opengl/include/EGL/eglext_angle.h
index 0556ea1..e753e0d 100644
--- a/opengl/include/EGL/eglext_angle.h
+++ b/opengl/include/EGL/eglext_angle.h
@@ -4,12 +4,12 @@
// found in the LICENSE file.
//
// eglext_angle.h: ANGLE modifications to the eglext.h header file.
-// Currently we don't include this file directly, we patch eglext.h
-// to include it implicitly so it is visible throughout our code.
#ifndef INCLUDE_EGL_EGLEXT_ANGLE_
#define INCLUDE_EGL_EGLEXT_ANGLE_
+#include <EGL/eglext.h>
+
// clang-format off
#ifndef EGL_ANGLE_robust_resource_initialization
@@ -186,6 +186,26 @@
#define EGL_EXTENSIONS_ENABLED_ANGLE 0x345F
#endif /* EGL_ANGLE_create_context_extensions_enabled */
+#ifndef EGL_ANGLE_feature_control
+#define EGL_ANGLE_feature_control 1
+#define EGL_FEATURE_NAME_ANGLE 0x3460
+#define EGL_FEATURE_CATEGORY_ANGLE 0x3461
+#define EGL_FEATURE_DESCRIPTION_ANGLE 0x3462
+#define EGL_FEATURE_BUG_ANGLE 0x3463
+#define EGL_FEATURE_STATUS_ANGLE 0x3464
+#define EGL_FEATURE_COUNT_ANGLE 0x3465
+#define EGL_FEATURE_OVERRIDES_ENABLED_ANGLE 0x3466
+#define EGL_FEATURE_OVERRIDES_DISABLED_ANGLE 0x3467
+#define EGL_FEATURE_CONDITION_ANGLE 0x3468
+#define EGL_FEATURE_ALL_DISABLED_ANGLE 0x3469
+typedef const char *(EGLAPIENTRYP PFNEGLQUERYSTRINGIANGLEPROC) (EGLDisplay dpy, EGLint name, EGLint index);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYDISPLAYATTRIBANGLEPROC) (EGLDisplay dpy, EGLint attribute, EGLAttrib *value);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI const char *EGLAPIENTRY eglQueryStringiANGLE(EGLDisplay dpy, EGLint name, EGLint index);
+EGLAPI EGLBoolean EGLAPIENTRY eglQueryDisplayAttribANGLE(EGLDisplay dpy, EGLint attribute, EGLAttrib *value);
+#endif
+#endif /* EGL_ANGLE_feature_control */
+
// clang-format on
#endif // INCLUDE_EGL_EGLEXT_ANGLE_
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index daaaf88..c9fce8a 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -111,11 +111,6 @@
"libbacktrace",
"libbase",
],
- target: {
- vendor: {
- exclude_shared_libs: ["libgraphicsenv"],
- },
- },
}
cc_library_static {
diff --git a/opengl/libs/EGL/BlobCache.cpp b/opengl/libs/EGL/BlobCache.cpp
index a27c09f..beca7f1 100644
--- a/opengl/libs/EGL/BlobCache.cpp
+++ b/opengl/libs/EGL/BlobCache.cpp
@@ -18,11 +18,11 @@
#include "BlobCache.h"
+#include <android-base/properties.h>
#include <errno.h>
#include <inttypes.h>
-
-#include <android-base/properties.h>
#include <log/log.h>
+
#include <chrono>
namespace android {
@@ -36,8 +36,8 @@
// BlobCache::Header::mDeviceVersion value
static const uint32_t blobCacheDeviceVersion = 1;
-BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize):
- mMaxTotalSize(maxTotalSize),
+BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize)
+ : mMaxTotalSize(maxTotalSize),
mMaxKeySize(maxKeySize),
mMaxValueSize(maxValueSize),
mTotalSize(0) {
@@ -52,21 +52,21 @@
ALOGV("initializing random seed using %lld", (unsigned long long)now);
}
-void BlobCache::set(const void* key, size_t keySize, const void* value,
- size_t valueSize) {
+void BlobCache::set(const void* key, size_t keySize, const void* value, size_t valueSize) {
if (mMaxKeySize < keySize) {
- ALOGV("set: not caching because the key is too large: %zu (limit: %zu)",
- keySize, mMaxKeySize);
+ ALOGV("set: not caching because the key is too large: %zu (limit: %zu)", keySize,
+ mMaxKeySize);
return;
}
if (mMaxValueSize < valueSize) {
- ALOGV("set: not caching because the value is too large: %zu (limit: %zu)",
- valueSize, mMaxValueSize);
+ ALOGV("set: not caching because the value is too large: %zu (limit: %zu)", valueSize,
+ mMaxValueSize);
return;
}
if (mMaxTotalSize < keySize + valueSize) {
ALOGV("set: not caching because the combined key/value size is too "
- "large: %zu (limit: %zu)", keySize + valueSize, mMaxTotalSize);
+ "large: %zu (limit: %zu)",
+ keySize + valueSize, mMaxTotalSize);
return;
}
if (keySize == 0) {
@@ -95,16 +95,16 @@
continue;
} else {
ALOGV("set: not caching new key/value pair because the "
- "total cache size limit would be exceeded: %zu "
- "(limit: %zu)",
- keySize + valueSize, mMaxTotalSize);
+ "total cache size limit would be exceeded: %zu "
+ "(limit: %zu)",
+ keySize + valueSize, mMaxTotalSize);
break;
}
}
mCacheEntries.insert(index, CacheEntry(keyBlob, valueBlob));
mTotalSize = newTotalSize;
- ALOGV("set: created new cache entry with %zu byte key and %zu byte value",
- keySize, valueSize);
+ ALOGV("set: created new cache entry with %zu byte key and %zu byte value", keySize,
+ valueSize);
} else {
// Update the existing cache entry.
std::shared_ptr<Blob> valueBlob(new Blob(value, valueSize, true));
@@ -117,25 +117,25 @@
continue;
} else {
ALOGV("set: not caching new value because the total cache "
- "size limit would be exceeded: %zu (limit: %zu)",
- keySize + valueSize, mMaxTotalSize);
+ "size limit would be exceeded: %zu (limit: %zu)",
+ keySize + valueSize, mMaxTotalSize);
break;
}
}
index->setValue(valueBlob);
mTotalSize = newTotalSize;
ALOGV("set: updated existing cache entry with %zu byte key and %zu byte "
- "value", keySize, valueSize);
+ "value",
+ keySize, valueSize);
}
break;
}
}
-size_t BlobCache::get(const void* key, size_t keySize, void* value,
- size_t valueSize) {
+size_t BlobCache::get(const void* key, size_t keySize, void* value, size_t valueSize) {
if (mMaxKeySize < keySize) {
- ALOGV("get: not searching because the key is too large: %zu (limit %zu)",
- keySize, mMaxKeySize);
+ ALOGV("get: not searching because the key is too large: %zu (limit %zu)", keySize,
+ mMaxKeySize);
return 0;
}
std::shared_ptr<Blob> cacheKey(new Blob(key, keySize, false));
@@ -154,8 +154,8 @@
ALOGV("get: copying %zu bytes to caller's buffer", valueBlobSize);
memcpy(value, valueBlob->getData(), valueBlobSize);
} else {
- ALOGV("get: caller's buffer is too small for value: %zu (needs %zu)",
- valueSize, valueBlobSize);
+ ALOGV("get: caller's buffer is too small for value: %zu (needs %zu)", valueSize,
+ valueBlobSize);
}
return valueBlobSize;
}
@@ -167,7 +167,7 @@
size_t BlobCache::getFlattenedSize() const {
auto buildId = base::GetProperty("ro.build.id", "");
size_t size = align4(sizeof(Header) + buildId.size());
- for (const CacheEntry& e : mCacheEntries) {
+ for (const CacheEntry& e : mCacheEntries) {
std::shared_ptr<Blob> const& keyBlob = e.getKey();
std::shared_ptr<Blob> const& valueBlob = e.getValue();
size += align4(sizeof(EntryHeader) + keyBlob->getSize() + valueBlob->getSize());
@@ -193,7 +193,7 @@
// Write cache entries
uint8_t* byteBuffer = reinterpret_cast<uint8_t*>(buffer);
off_t byteOffset = align4(sizeof(Header) + header->mBuildIdLength);
- for (const CacheEntry& e : mCacheEntries) {
+ for (const CacheEntry& e : mCacheEntries) {
std::shared_ptr<Blob> const& keyBlob = e.getKey();
std::shared_ptr<Blob> const& valueBlob = e.getValue();
size_t keySize = keyBlob->getSize();
@@ -259,8 +259,7 @@
return -EINVAL;
}
- const EntryHeader* eheader = reinterpret_cast<const EntryHeader*>(
- &byteBuffer[byteOffset]);
+ const EntryHeader* eheader = reinterpret_cast<const EntryHeader*>(&byteBuffer[byteOffset]);
size_t keySize = eheader->mKeySize;
size_t valueSize = eheader->mValueSize;
size_t entrySize = sizeof(EntryHeader) + keySize + valueSize;
@@ -304,10 +303,8 @@
return mTotalSize > mMaxTotalSize / 2;
}
-BlobCache::Blob::Blob(const void* data, size_t size, bool copyData) :
- mData(copyData ? malloc(size) : data),
- mSize(size),
- mOwnsData(copyData) {
+BlobCache::Blob::Blob(const void* data, size_t size, bool copyData)
+ : mData(copyData ? malloc(size) : data), mSize(size), mOwnsData(copyData) {
if (data != nullptr && copyData) {
memcpy(const_cast<void*>(mData), data, size);
}
@@ -335,19 +332,13 @@
return mSize;
}
-BlobCache::CacheEntry::CacheEntry() {
-}
+BlobCache::CacheEntry::CacheEntry() {}
-BlobCache::CacheEntry::CacheEntry(
- const std::shared_ptr<Blob>& key, const std::shared_ptr<Blob>& value):
- mKey(key),
- mValue(value) {
-}
+BlobCache::CacheEntry::CacheEntry(const std::shared_ptr<Blob>& key,
+ const std::shared_ptr<Blob>& value)
+ : mKey(key), mValue(value) {}
-BlobCache::CacheEntry::CacheEntry(const CacheEntry& ce):
- mKey(ce.mKey),
- mValue(ce.mValue) {
-}
+BlobCache::CacheEntry::CacheEntry(const CacheEntry& ce) : mKey(ce.mKey), mValue(ce.mValue) {}
bool BlobCache::CacheEntry::operator<(const CacheEntry& rhs) const {
return *mKey < *rhs.mKey;
diff --git a/opengl/libs/EGL/BlobCache.h b/opengl/libs/EGL/BlobCache.h
index e5c5e5b..50b4e4c 100644
--- a/opengl/libs/EGL/BlobCache.h
+++ b/opengl/libs/EGL/BlobCache.h
@@ -54,8 +54,7 @@
// 0 < keySize
// value != NULL
// 0 < valueSize
- void set(const void* key, size_t keySize, const void* value,
- size_t valueSize);
+ void set(const void* key, size_t keySize, const void* value, size_t valueSize);
// get retrieves from the cache the binary value associated with a given
// binary key. If the key is present in the cache then the length of the
@@ -75,7 +74,6 @@
// 0 <= valueSize
size_t get(const void* key, size_t keySize, void* value, size_t valueSize);
-
// getFlattenedSize returns the number of bytes needed to store the entire
// serialized cache.
size_t getFlattenedSize() const;
@@ -168,7 +166,6 @@
void setValue(const std::shared_ptr<Blob>& value);
private:
-
// mKey is the key that identifies the cache entry.
std::shared_ptr<Blob> mKey;
@@ -245,6 +242,6 @@
std::vector<CacheEntry> mCacheEntries;
};
-}
+} // namespace android
#endif // ANDROID_BLOB_CACHE_H
diff --git a/opengl/libs/EGL/BlobCache_test.cpp b/opengl/libs/EGL/BlobCache_test.cpp
index cf67cf4..d31373b 100644
--- a/opengl/libs/EGL/BlobCache_test.cpp
+++ b/opengl/libs/EGL/BlobCache_test.cpp
@@ -14,25 +14,24 @@
** limitations under the License.
*/
+#include "BlobCache.h"
+
#include <fcntl.h>
+#include <gtest/gtest.h>
#include <stdio.h>
#include <memory>
-#include <gtest/gtest.h>
-
-#include "BlobCache.h"
-
namespace android {
-template<typename T> using sp = std::shared_ptr<T>;
+template <typename T>
+using sp = std::shared_ptr<T>;
class BlobCacheTest : public ::testing::Test {
protected:
-
enum {
OK = 0,
- BAD_VALUE = -EINVAL
+ BAD_VALUE = -EINVAL,
};
enum {
@@ -41,19 +40,15 @@
MAX_TOTAL_SIZE = 13,
};
- virtual void SetUp() {
- mBC.reset(new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE));
- }
+ virtual void SetUp() { mBC.reset(new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE)); }
- virtual void TearDown() {
- mBC.reset();
- }
+ virtual void TearDown() { mBC.reset(); }
std::unique_ptr<BlobCache> mBC;
};
TEST_F(BlobCacheTest, CacheSingleValueSucceeds) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
mBC->set("abcd", 4, "efgh", 4);
ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
ASSERT_EQ('e', buf[0]);
@@ -63,7 +58,7 @@
}
TEST_F(BlobCacheTest, CacheTwoValuesSucceeds) {
- unsigned char buf[2] = { 0xee, 0xee };
+ unsigned char buf[2] = {0xee, 0xee};
mBC->set("ab", 2, "cd", 2);
mBC->set("ef", 2, "gh", 2);
ASSERT_EQ(size_t(2), mBC->get("ab", 2, buf, 2));
@@ -75,9 +70,9 @@
}
TEST_F(BlobCacheTest, GetOnlyWritesInsideBounds) {
- unsigned char buf[6] = { 0xee, 0xee, 0xee, 0xee, 0xee, 0xee };
+ unsigned char buf[6] = {0xee, 0xee, 0xee, 0xee, 0xee, 0xee};
mBC->set("abcd", 4, "efgh", 4);
- ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf+1, 4));
+ ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf + 1, 4));
ASSERT_EQ(0xee, buf[0]);
ASSERT_EQ('e', buf[1]);
ASSERT_EQ('f', buf[2]);
@@ -87,7 +82,7 @@
}
TEST_F(BlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) {
- unsigned char buf[3] = { 0xee, 0xee, 0xee };
+ unsigned char buf[3] = {0xee, 0xee, 0xee};
mBC->set("abcd", 4, "efgh", 4);
ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 3));
ASSERT_EQ(0xee, buf[0]);
@@ -101,7 +96,7 @@
}
TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
mBC->set("abcd", 4, "efgh", 4);
mBC->set("abcd", 4, "ijkl", 4);
ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
@@ -112,9 +107,9 @@
}
TEST_F(BlobCacheTest, SecondSetKeepsFirstValueIfTooLarge) {
- unsigned char buf[MAX_VALUE_SIZE+1] = { 0xee, 0xee, 0xee, 0xee };
+ unsigned char buf[MAX_VALUE_SIZE + 1] = {0xee, 0xee, 0xee, 0xee};
mBC->set("abcd", 4, "efgh", 4);
- mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1);
+ mBC->set("abcd", 4, buf, MAX_VALUE_SIZE + 1);
ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
ASSERT_EQ('e', buf[0]);
ASSERT_EQ('f', buf[1]);
@@ -123,13 +118,13 @@
}
TEST_F(BlobCacheTest, DoesntCacheIfKeyIsTooBig) {
- char key[MAX_KEY_SIZE+1];
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
- for (int i = 0; i < MAX_KEY_SIZE+1; i++) {
+ char key[MAX_KEY_SIZE + 1];
+ unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
+ for (int i = 0; i < MAX_KEY_SIZE + 1; i++) {
key[i] = 'a';
}
- mBC->set(key, MAX_KEY_SIZE+1, "bbbb", 4);
- ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE+1, buf, 4));
+ mBC->set(key, MAX_KEY_SIZE + 1, "bbbb", 4);
+ ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE + 1, buf, 4));
ASSERT_EQ(0xee, buf[0]);
ASSERT_EQ(0xee, buf[1]);
ASSERT_EQ(0xee, buf[2]);
@@ -137,16 +132,16 @@
}
TEST_F(BlobCacheTest, DoesntCacheIfValueIsTooBig) {
- char buf[MAX_VALUE_SIZE+1];
- for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
+ char buf[MAX_VALUE_SIZE + 1];
+ for (int i = 0; i < MAX_VALUE_SIZE + 1; i++) {
buf[i] = 'b';
}
- mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1);
- for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
+ mBC->set("abcd", 4, buf, MAX_VALUE_SIZE + 1);
+ for (int i = 0; i < MAX_VALUE_SIZE + 1; i++) {
buf[i] = 0xee;
}
- ASSERT_EQ(size_t(0), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE+1));
- for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
+ ASSERT_EQ(size_t(0), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE + 1));
+ for (int i = 0; i < MAX_VALUE_SIZE + 1; i++) {
SCOPED_TRACE(i);
ASSERT_EQ(0xee, buf[i]);
}
@@ -174,7 +169,7 @@
TEST_F(BlobCacheTest, CacheMaxKeySizeSucceeds) {
char key[MAX_KEY_SIZE];
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
for (int i = 0; i < MAX_KEY_SIZE; i++) {
key[i] = 'a';
}
@@ -195,8 +190,7 @@
for (int i = 0; i < MAX_VALUE_SIZE; i++) {
buf[i] = 0xee;
}
- ASSERT_EQ(size_t(MAX_VALUE_SIZE), mBC->get("abcd", 4, buf,
- MAX_VALUE_SIZE));
+ ASSERT_EQ(size_t(MAX_VALUE_SIZE), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE));
for (int i = 0; i < MAX_VALUE_SIZE; i++) {
SCOPED_TRACE(i);
ASSERT_EQ('b', buf[i]);
@@ -223,7 +217,7 @@
}
TEST_F(BlobCacheTest, CacheMinKeyAndValueSizeSucceeds) {
- unsigned char buf[1] = { 0xee };
+ unsigned char buf[1] = {0xee};
mBC->set("x", 1, "y", 1);
ASSERT_EQ(size_t(1), mBC->get("x", 1, buf, 1));
ASSERT_EQ('y', buf[0]);
@@ -258,13 +252,13 @@
}
// Count the number of entries in the cache.
int numCached = 0;
- for (int i = 0; i < maxEntries+1; i++) {
+ for (int i = 0; i < maxEntries + 1; i++) {
uint8_t k = i;
if (mBC->get(&k, 1, nullptr, 0) == 1) {
numCached++;
}
}
- ASSERT_EQ(maxEntries/2 + 1, numCached);
+ ASSERT_EQ(maxEntries / 2 + 1, numCached);
}
class BlobCacheFlattenTest : public BlobCacheTest {
@@ -291,7 +285,7 @@
};
TEST_F(BlobCacheFlattenTest, FlattenOneValue) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
mBC->set("abcd", 4, "efgh", 4);
roundTrip();
ASSERT_EQ(size_t(4), mBC2->get("abcd", 4, buf, 4));
@@ -359,7 +353,7 @@
}
TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadMagic) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
mBC->set("abcd", 4, "efgh", 4);
size_t size = mBC->getFlattenedSize();
@@ -376,7 +370,7 @@
}
TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheVersion) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
mBC->set("abcd", 4, "efgh", 4);
size_t size = mBC->getFlattenedSize();
@@ -395,7 +389,7 @@
}
TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheDeviceVersion) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
mBC->set("abcd", 4, "efgh", 4);
size_t size = mBC->getFlattenedSize();
@@ -414,7 +408,7 @@
}
TEST_F(BlobCacheFlattenTest, UnflattenCatchesBufferTooSmall) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
mBC->set("abcd", 4, "efgh", 4);
size_t size = mBC->getFlattenedSize();
diff --git a/opengl/libs/EGL/CallStack.h b/opengl/libs/EGL/CallStack.h
index 0e2a9b3..b7fdf97 100644
--- a/opengl/libs/EGL/CallStack.h
+++ b/opengl/libs/EGL/CallStack.h
@@ -16,8 +16,9 @@
#pragma once
-#include <log/log.h>
#include <backtrace/Backtrace.h>
+#include <log/log.h>
+
#include <memory>
class CallStack {
@@ -30,9 +31,8 @@
if (backtrace->Unwind(2)) {
for (size_t i = 0, c = backtrace->NumFrames(); i < c; i++) {
__android_log_print(ANDROID_LOG_DEBUG, logtag, "%s",
- backtrace->FormatFrameData(i).c_str());
+ backtrace->FormatFrameData(i).c_str());
}
}
}
};
-
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index d66ef2b..76fd7f0 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -17,27 +17,23 @@
//#define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <EGL/Loader.h>
-
-#include <string>
-
-#include <dirent.h>
-#include <dlfcn.h>
+#include "EGL/Loader.h"
#include <android-base/properties.h>
#include <android/dlext.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <graphicsenv/GraphicsEnv.h>
#include <log/log.h>
#include <utils/Timers.h>
-
-#ifndef __ANDROID_VNDK__
-#include <graphicsenv/GraphicsEnv.h>
-#endif
#include <vndksupport/linker.h>
+#include <string>
+
+#include "EGL/eglext_angle.h"
#include "egl_platform_entries.h"
#include "egl_trace.h"
#include "egldefs.h"
-#include <EGL/eglext_angle.h>
namespace android {
@@ -159,13 +155,11 @@
return true;
}
-#ifndef __ANDROID_VNDK__
// Return true if updated driver namespace is set.
ns = android::GraphicsEnv::getInstance().getDriverNamespace();
if (ns) {
return true;
}
-#endif
return false;
}
@@ -276,7 +270,7 @@
// will set cnx->useAngle appropriately.
// Do this here so that we use ANGLE path when driver is ANGLE (e.g. loaded as native),
// not just loading ANGLE as option.
- init_angle_backend(hnd->dso[0], cnx);
+ init_angle_backend(hnd->dso[2], cnx);
}
LOG_ALWAYS_FATAL_IF(!hnd,
@@ -370,7 +364,7 @@
f = (__eglMustCastToProperFunctionPointerType)gl_unimplemented;
/*
- * GL_EXT_debug_label is special, we always report it as
+ * GL_EXT_debug_marker is special, we always report it as
* supported, it's handled by GLES_trace. If GLES_trace is not
* enabled, then these are no-ops.
*/
@@ -520,6 +514,8 @@
if (so) {
return so;
}
+ ALOGE("Could not load %s from updatable gfx driver namespace: %s.", name.c_str(),
+ dlerror());
}
return nullptr;
}
@@ -557,12 +553,8 @@
}
void Loader::init_angle_backend(void* dso, egl_connection_t* cnx) {
- void* eglCreateDeviceANGLE = nullptr;
-
- ALOGV("dso: %p", dso);
- eglCreateDeviceANGLE = dlsym(dso, "eglCreateDeviceANGLE");
- ALOGV("eglCreateDeviceANGLE: %p", eglCreateDeviceANGLE);
- if (eglCreateDeviceANGLE) {
+ void* pANGLEGetDisplayPlatform = dlsym(dso, "ANGLEGetDisplayPlatform");
+ if (pANGLEGetDisplayPlatform) {
ALOGV("ANGLE GLES library in use");
cnx->useAngle = true;
} else {
@@ -573,7 +565,7 @@
Loader::driver_t* Loader::attempt_to_load_updated_driver(egl_connection_t* cnx) {
ATRACE_CALL();
-#ifndef __ANDROID_VNDK__
+
android_namespace_t* ns = android::GraphicsEnv::getInstance().getDriverNamespace();
if (!ns) {
return nullptr;
@@ -603,9 +595,6 @@
hnd->set(dso, GLESv2);
}
return hnd;
-#else
- return nullptr;
-#endif
}
Loader::driver_t* Loader::attempt_to_load_system_driver(egl_connection_t* cnx, const char* suffix,
diff --git a/opengl/libs/EGL/Loader.h b/opengl/libs/EGL/Loader.h
index 7b2d7c9..81742ab 100644
--- a/opengl/libs/EGL/Loader.h
+++ b/opengl/libs/EGL/Loader.h
@@ -1,29 +1,26 @@
-/*
+/*
** Copyright 2009, 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
+ ** 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
+ ** 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
+ ** 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.
*/
#ifndef ANDROID_EGL_LOADER_H
#define ANDROID_EGL_LOADER_H
+#include <EGL/egl.h>
#include <stdint.h>
-#include <EGL/egl.h>
-
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
struct egl_connection_t;
@@ -62,16 +59,12 @@
void initialize_api(void* dso, egl_connection_t* cnx, uint32_t mask);
void init_angle_backend(void* dso, egl_connection_t* cnx);
- static __attribute__((noinline))
- void init_api(void* dso,
- char const * const * api,
- char const * const * ref_api,
- __eglMustCastToProperFunctionPointerType* curr,
- getProcAddressType getProcAddress);
+ static __attribute__((noinline)) void init_api(void* dso, const char* const* api,
+ const char* const* ref_api,
+ __eglMustCastToProperFunctionPointerType* curr,
+ getProcAddressType getProcAddress);
};
-// ----------------------------------------------------------------------------
}; // namespace android
-// ----------------------------------------------------------------------------
#endif /* ANDROID_EGL_LOADER_H */
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 43f7a07..e5b9e14 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -14,45 +14,35 @@
** limitations under the License.
*/
+#include <EGL/egl.h>
+#include <android-base/properties.h>
+#include <log/log.h>
#include <stdlib.h>
-#include <EGL/egl.h>
-
-#include <android-base/properties.h>
-
-#include <log/log.h>
-
#include "../egl_impl.h"
-
-#include "egldefs.h"
-#include "egl_tls.h"
-#include "egl_display.h"
-#include "egl_object.h"
-#include "egl_layers.h"
#include "CallStack.h"
#include "Loader.h"
+#include "egl_display.h"
+#include "egl_layers.h"
+#include "egl_object.h"
+#include "egl_tls.h"
+#include "egldefs.h"
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
egl_connection_t gEGLImpl;
gl_hooks_t gHooks[2];
gl_hooks_t gHooksNoContext;
pthread_key_t gGLWrapperKey = -1;
-// ----------------------------------------------------------------------------
-
-void setGLHooksThreadSpecific(gl_hooks_t const *value) {
+void setGLHooksThreadSpecific(gl_hooks_t const* value) {
setGlThreadSpecific(value);
}
-/*****************************************************************************/
-
static int gl_no_context() {
if (egl_tls_t::logNoContextCall()) {
- char const* const error = "call to OpenGL ES API with "
- "no current context (logged once per thread)";
+ const char* const error = "call to OpenGL ES API with "
+ "no current context (logged once per thread)";
if (LOG_NDEBUG) {
ALOGE(error);
} else {
@@ -65,10 +55,9 @@
return 0;
}
-static void early_egl_init(void)
-{
+static void early_egl_init(void) {
int numHooks = sizeof(gHooksNoContext) / sizeof(EGLFuncPointer);
- EGLFuncPointer *iter = reinterpret_cast<EGLFuncPointer*>(&gHooksNoContext);
+ EGLFuncPointer* iter = reinterpret_cast<EGLFuncPointer*>(&gHooksNoContext);
for (int hook = 0; hook < numHooks; ++hook) {
*(iter++) = reinterpret_cast<EGLFuncPointer>(gl_no_context);
}
@@ -76,75 +65,40 @@
setGLHooksThreadSpecific(&gHooksNoContext);
}
-static pthread_once_t once_control = PTHREAD_ONCE_INIT;
-static int sEarlyInitState = pthread_once(&once_control, &early_egl_init);
-
-// ----------------------------------------------------------------------------
-
-egl_display_ptr validate_display(EGLDisplay dpy) {
- egl_display_ptr dp = get_display(dpy);
- if (!dp)
- return setError(EGL_BAD_DISPLAY, egl_display_ptr(nullptr));
- if (!dp->isReady())
- return setError(EGL_NOT_INITIALIZED, egl_display_ptr(nullptr));
-
- return dp;
-}
-
-egl_display_ptr validate_display_connection(EGLDisplay dpy,
- egl_connection_t*& cnx) {
- cnx = nullptr;
- egl_display_ptr dp = validate_display(dpy);
- if (!dp)
- return dp;
- cnx = &gEGLImpl;
- if (cnx->dso == nullptr) {
- return setError(EGL_BAD_CONFIG, egl_display_ptr(nullptr));
- }
- return dp;
-}
-
-// ----------------------------------------------------------------------------
-
-const GLubyte * egl_get_string_for_current_context(GLenum name) {
+const GLubyte* egl_get_string_for_current_context(GLenum name) {
// NOTE: returning NULL here will fall-back to the default
// implementation.
EGLContext context = egl_tls_t::getContext();
- if (context == EGL_NO_CONTEXT)
- return nullptr;
+ if (context == EGL_NO_CONTEXT) return nullptr;
- egl_context_t const * const c = get_context(context);
+ const egl_context_t* const c = get_context(context);
if (c == nullptr) // this should never happen, by construction
return nullptr;
- if (name != GL_EXTENSIONS)
- return nullptr;
+ if (name != GL_EXTENSIONS) return nullptr;
- return (const GLubyte *)c->gl_extensions.c_str();
+ return (const GLubyte*)c->gl_extensions.c_str();
}
-const GLubyte * egl_get_string_for_current_context(GLenum name, GLuint index) {
+const GLubyte* egl_get_string_for_current_context(GLenum name, GLuint index) {
// NOTE: returning NULL here will fall-back to the default
// implementation.
EGLContext context = egl_tls_t::getContext();
- if (context == EGL_NO_CONTEXT)
- return nullptr;
+ if (context == EGL_NO_CONTEXT) return nullptr;
- egl_context_t const * const c = get_context(context);
+ const egl_context_t* const c = get_context(context);
if (c == nullptr) // this should never happen, by construction
return nullptr;
- if (name != GL_EXTENSIONS)
- return nullptr;
+ if (name != GL_EXTENSIONS) return nullptr;
// if index is out of bounds, assume it will be in the default
// implementation too, so we don't have to generate a GL error here
- if (index >= c->tokenized_gl_extensions.size())
- return nullptr;
+ if (index >= c->tokenized_gl_extensions.size()) return nullptr;
- return (const GLubyte *)c->tokenized_gl_extensions[index].c_str();
+ return (const GLubyte*)c->tokenized_gl_extensions[index].c_str();
}
GLint egl_get_num_extensions_for_current_context() {
@@ -152,10 +106,9 @@
// implementation.
EGLContext context = egl_tls_t::getContext();
- if (context == EGL_NO_CONTEXT)
- return -1;
+ if (context == EGL_NO_CONTEXT) return -1;
- egl_context_t const * const c = get_context(context);
+ const egl_context_t* const c = get_context(context);
if (c == nullptr) // this should never happen, by construction
return -1;
@@ -166,7 +119,8 @@
return &gEGLImpl;
}
-// ----------------------------------------------------------------------------
+static pthread_once_t once_control = PTHREAD_ONCE_INIT;
+static int sEarlyInitState = pthread_once(&once_control, &early_egl_init);
static EGLBoolean egl_init_drivers_locked() {
if (sEarlyInitState) {
@@ -194,7 +148,6 @@
return cnx->dso ? EGL_TRUE : EGL_FALSE;
}
-
// this mutex protects driver load logic as a critical section since it accesses to global variable
// like gEGLImpl
static pthread_mutex_t sInitDriverMutex = PTHREAD_MUTEX_INITIALIZER;
@@ -228,13 +181,10 @@
}
}
-void gl_noop() {
-}
+void gl_noop() {}
-// ----------------------------------------------------------------------------
-
-void setGlThreadSpecific(gl_hooks_t const *value) {
- gl_hooks_t const * volatile * tls_hooks = get_tls_hooks();
+void setGlThreadSpecific(gl_hooks_t const* value) {
+ gl_hooks_t const* volatile* tls_hooks = get_tls_hooks();
tls_hooks[TLS_SLOT_OPENGL_API] = value;
}
@@ -270,8 +220,4 @@
#undef GL_ENTRY
#undef EGL_ENTRY
-
-// ----------------------------------------------------------------------------
}; // namespace android
-// ----------------------------------------------------------------------------
-
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index c51a129..502c14f 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -20,7 +20,6 @@
#include <EGL/eglext.h>
#include "../egl_impl.h"
-
#include "egl_layers.h"
#include "egl_platform_entries.h"
#include "egl_tls.h"
diff --git a/opengl/libs/EGL/egl_angle_platform.cpp b/opengl/libs/EGL/egl_angle_platform.cpp
index 4f85eff..d38f2ef 100644
--- a/opengl/libs/EGL/egl_angle_platform.cpp
+++ b/opengl/libs/EGL/egl_angle_platform.cpp
@@ -16,7 +16,6 @@
#if defined(__ANDROID__)
-#include "Loader.h"
#include "egl_angle_platform.h"
#pragma GCC diagnostic push
@@ -32,6 +31,8 @@
#include <time.h>
#include <vndksupport/linker.h>
+#include "Loader.h"
+
namespace angle {
constexpr int kAngleDlFlags = RTLD_LOCAL | RTLD_NOW;
@@ -151,9 +152,8 @@
reinterpret_cast<ResetDisplayPlatformFunc>(dlsym(so, "ANGLEResetDisplayPlatform"));
PlatformMethods* platformMethods = nullptr;
- if (!((angleGetDisplayPlatform)(dpy, g_PlatformMethodNames,
- g_NumPlatformMethods, nullptr,
- &platformMethods))) {
+ if (!((angleGetDisplayPlatform)(dpy, g_PlatformMethodNames, g_NumPlatformMethods, nullptr,
+ &platformMethods))) {
ALOGE("ANGLEGetDisplayPlatform call failed!");
return false;
}
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
index bcf4961..efa67db 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -16,17 +16,14 @@
#include "egl_cache.h"
-#include "../egl_impl.h"
-
-#include "egl_display.h"
-
+#include <log/log.h>
#include <private/EGL/cache.h>
-
#include <unistd.h>
#include <thread>
-#include <log/log.h>
+#include "../egl_impl.h"
+#include "egl_display.h"
// Cache size limits.
static const size_t maxKeySize = 12 * 1024;
@@ -36,9 +33,7 @@
// The time in seconds to wait before saving newly inserted cache entries.
static const unsigned int deferredSaveDelay = 4;
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
#define BC_EXT_STR "EGL_ANDROID_blob_cache"
@@ -50,25 +45,22 @@
//
// Callback functions passed to EGL.
//
-static void setBlob(const void* key, EGLsizeiANDROID keySize,
- const void* value, EGLsizeiANDROID valueSize) {
+static void setBlob(const void* key, EGLsizeiANDROID keySize, const void* value,
+ EGLsizeiANDROID valueSize) {
egl_cache_t::get()->setBlob(key, keySize, value, valueSize);
}
-static EGLsizeiANDROID getBlob(const void* key, EGLsizeiANDROID keySize,
- void* value, EGLsizeiANDROID valueSize) {
+static EGLsizeiANDROID getBlob(const void* key, EGLsizeiANDROID keySize, void* value,
+ EGLsizeiANDROID valueSize) {
return egl_cache_t::get()->getBlob(key, keySize, value, valueSize);
}
//
// egl_cache_t definition
//
-egl_cache_t::egl_cache_t() :
- mInitialized(false) {
-}
+egl_cache_t::egl_cache_t() : mInitialized(false) {}
-egl_cache_t::~egl_cache_t() {
-}
+egl_cache_t::~egl_cache_t() {}
egl_cache_t egl_cache_t::sCache;
@@ -76,7 +68,7 @@
return &sCache;
}
-void egl_cache_t::initialize(egl_display_t *display) {
+void egl_cache_t::initialize(egl_display_t* display) {
std::lock_guard<std::mutex> lock(mMutex);
egl_connection_t* const cnx = &gEGLImpl;
@@ -85,28 +77,26 @@
size_t bcExtLen = strlen(BC_EXT_STR);
size_t extsLen = strlen(exts);
bool equal = !strcmp(BC_EXT_STR, exts);
- bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen+1);
- bool atEnd = (bcExtLen+1) < extsLen &&
- !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen+1));
+ bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen + 1);
+ bool atEnd = (bcExtLen + 1) < extsLen &&
+ !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen + 1));
bool inMiddle = strstr(exts, " " BC_EXT_STR " ") != nullptr;
if (equal || atStart || atEnd || inMiddle) {
PFNEGLSETBLOBCACHEFUNCSANDROIDPROC eglSetBlobCacheFuncsANDROID;
- eglSetBlobCacheFuncsANDROID =
- reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSANDROIDPROC>(
- cnx->egl.eglGetProcAddress(
- "eglSetBlobCacheFuncsANDROID"));
+ eglSetBlobCacheFuncsANDROID = reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSANDROIDPROC>(
+ cnx->egl.eglGetProcAddress("eglSetBlobCacheFuncsANDROID"));
if (eglSetBlobCacheFuncsANDROID == nullptr) {
ALOGE("EGL_ANDROID_blob_cache advertised, "
- "but unable to get eglSetBlobCacheFuncsANDROID");
+ "but unable to get eglSetBlobCacheFuncsANDROID");
return;
}
- eglSetBlobCacheFuncsANDROID(display->disp.dpy,
- android::setBlob, android::getBlob);
+ eglSetBlobCacheFuncsANDROID(display->disp.dpy, android::setBlob, android::getBlob);
EGLint err = cnx->egl.eglGetError();
if (err != EGL_SUCCESS) {
ALOGE("eglSetBlobCacheFuncsANDROID resulted in an error: "
- "%#x", err);
+ "%#x",
+ err);
}
}
}
@@ -122,8 +112,8 @@
mBlobCache = nullptr;
}
-void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize,
- const void* value, EGLsizeiANDROID valueSize) {
+void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize, const void* value,
+ EGLsizeiANDROID valueSize) {
std::lock_guard<std::mutex> lock(mMutex);
if (keySize < 0 || valueSize < 0) {
@@ -150,8 +140,8 @@
}
}
-EGLsizeiANDROID egl_cache_t::getBlob(const void* key, EGLsizeiANDROID keySize,
- void* value, EGLsizeiANDROID valueSize) {
+EGLsizeiANDROID egl_cache_t::getBlob(const void* key, EGLsizeiANDROID keySize, void* value,
+ EGLsizeiANDROID valueSize) {
std::lock_guard<std::mutex> lock(mMutex);
if (keySize < 0 || valueSize < 0) {
@@ -178,6 +168,4 @@
return mBlobCache.get();
}
-// ----------------------------------------------------------------------------
}; // namespace android
-// ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_cache.h b/opengl/libs/EGL/egl_cache.h
index 7382b91..d10a615 100644
--- a/opengl/libs/EGL/egl_cache.h
+++ b/opengl/libs/EGL/egl_cache.h
@@ -20,21 +20,18 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
-#include "FileBlobCache.h"
-
#include <memory>
#include <mutex>
#include <string>
-// ----------------------------------------------------------------------------
+#include "FileBlobCache.h"
+
namespace android {
-// ----------------------------------------------------------------------------
class egl_display_t;
class EGLAPI egl_cache_t {
public:
-
// get returns a pointer to the singleton egl_cache_t object. This
// singleton object will never be destroyed.
static egl_cache_t* get();
@@ -117,8 +114,6 @@
static egl_cache_t sCache;
};
-// ----------------------------------------------------------------------------
}; // namespace android
-// ----------------------------------------------------------------------------
#endif // ANDROID_EGL_CACHE_H
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 3b1cf71..0b755aa 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -14,49 +14,41 @@
** limitations under the License.
*/
-#define __STDC_LIMIT_MACROS 1
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "egl_display.h"
+#include <SurfaceFlingerProperties.h>
+#include <android-base/properties.h>
+#include <android/dlext.h>
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <configstore/Utils.h>
+#include <dlfcn.h>
+#include <graphicsenv/GraphicsEnv.h>
+
#include "../egl_impl.h"
-
-#include <EGL/eglext_angle.h>
-#include <private/EGL/display.h>
-
+#include "EGL/eglext_angle.h"
#include "Loader.h"
#include "egl_angle_platform.h"
#include "egl_cache.h"
#include "egl_object.h"
#include "egl_tls.h"
-
-#include <SurfaceFlingerProperties.h>
-#include <android-base/properties.h>
-#include <android/dlext.h>
-#include <dlfcn.h>
-#include <graphicsenv/GraphicsEnv.h>
-
-#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
-#include <configstore/Utils.h>
+#include "private/EGL/display.h"
using namespace android::hardware::configstore;
using namespace android::hardware::configstore::V1_0;
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
-static char const * const sVendorString = "Android";
-static char const* const sVersionString14 = "1.4 Android META-EGL";
-static char const* const sVersionString15 = "1.5 Android META-EGL";
-static char const * const sClientApiString = "OpenGL_ES";
+static const char* const sVendorString = "Android";
+static const char* const sVersionString14 = "1.4 Android META-EGL";
+static const char* const sVersionString15 = "1.5 Android META-EGL";
+static const char* const sClientApiString = "OpenGL_ES";
-extern char const * const gBuiltinExtensionString;
-extern char const * const gExtensionString;
+extern const char* const gBuiltinExtensionString;
+extern const char* const gExtensionString;
-extern void setGLHooksThreadSpecific(gl_hooks_t const *value);
-
-// ----------------------------------------------------------------------------
+extern void setGLHooksThreadSpecific(gl_hooks_t const* value);
bool findExtension(const char* exts, const char* name, size_t nameLen) {
if (exts) {
@@ -82,11 +74,15 @@
return eglDisplay ? eglDisplay->getRefsCount() : 0;
}
-egl_display_t egl_display_t::sDisplay[NUM_DISPLAYS];
+std::map<EGLDisplay, std::unique_ptr<egl_display_t>> egl_display_t::displayMap;
+std::mutex egl_display_t::displayMapLock;
-egl_display_t::egl_display_t() :
- magic('_dpy'), finishOnSwap(false), traceGpuCompletion(false), refs(0), eglIsInitialized(false) {
-}
+egl_display_t::egl_display_t()
+ : magic('_dpy'),
+ finishOnSwap(false),
+ traceGpuCompletion(false),
+ refs(0),
+ eglIsInitialized(false) {}
egl_display_t::~egl_display_t() {
magic = 0;
@@ -98,11 +94,12 @@
return nullptr;
}
- uintptr_t index = uintptr_t(dpy)-1U;
- if (index >= NUM_DISPLAYS || !sDisplay[index].isValid()) {
+ const std::lock_guard<std::mutex> lock(displayMapLock);
+ auto search = displayMap.find(dpy);
+ if (search == displayMap.end() || !search->second->isValid()) {
return nullptr;
}
- return &sDisplay[index];
+ return search->second.get();
}
void egl_display_t::addObject(egl_object_t* object) {
@@ -128,10 +125,9 @@
EGLDisplay egl_display_t::getFromNativeDisplay(EGLNativeDisplayType disp,
const EGLAttrib* attrib_list) {
- if (uintptr_t(disp) >= NUM_DISPLAYS)
- return nullptr;
+ if (uintptr_t(disp) >= NUM_DISPLAYS) return nullptr;
- return sDisplay[uintptr_t(disp)].getPlatformDisplay(disp, attrib_list);
+ return getPlatformDisplay(disp, attrib_list);
}
static EGLDisplay getPlatformDisplayAngle(EGLNativeDisplayType display, egl_connection_t* const cnx,
@@ -147,6 +143,16 @@
attrs.push_back(attr[1]);
}
}
+ const auto& eglFeatures = GraphicsEnv::getInstance().getAngleEglFeatures();
+ std::vector<const char*> features;
+ if (eglFeatures.size() > 0) {
+ for (const std::string& eglFeature : eglFeatures) {
+ features.push_back(eglFeature.c_str());
+ }
+ features.push_back(0);
+ attrs.push_back(EGL_FEATURE_OVERRIDES_ENABLED_ANGLE);
+ attrs.push_back(reinterpret_cast<EGLAttrib>(features.data()));
+ }
attrs.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE);
attrs.push_back(EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE);
@@ -176,7 +182,6 @@
EGLDisplay egl_display_t::getPlatformDisplay(EGLNativeDisplayType display,
const EGLAttrib* attrib_list) {
- std::lock_guard<std::mutex> _l(lock);
ATRACE_CALL();
// get our driver loader
@@ -204,7 +209,7 @@
// It is possible that eglGetPlatformDisplay does not have a
// working implementation for Android platform; in that case,
// one last fallback to eglGetDisplay
- if(dpy == EGL_NO_DISPLAY) {
+ if (dpy == EGL_NO_DISPLAY) {
if (attrib_list) {
ALOGW("getPlatformDisplay: unexpected attribute list, attributes ignored");
}
@@ -212,17 +217,23 @@
}
}
- disp.dpy = dpy;
if (dpy == EGL_NO_DISPLAY) {
loader.close(cnx);
+ } else {
+ const std::lock_guard<std::mutex> lock(displayMapLock);
+ if (displayMap.find(dpy) == displayMap.end()) {
+ auto d = std::make_unique<egl_display_t>();
+ d->disp.dpy = dpy;
+ displayMap[dpy] = std::move(d);
+ }
+ return dpy;
}
}
- return EGLDisplay(uintptr_t(display) + 1U);
+ return nullptr;
}
-EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) {
-
+EGLBoolean egl_display_t::initialize(EGLint* major, EGLint* minor) {
{ // scope for refLock
std::unique_lock<std::mutex> _l(refLock);
refs++;
@@ -230,7 +241,7 @@
// We don't know what to report until we know what the
// driver supports. Make sure we are initialized before
// returning the version info.
- while(!eglIsInitialized) {
+ while (!eglIsInitialized) {
refCond.wait(_l);
}
egl_connection_t* const cnx = &gEGLImpl;
@@ -243,7 +254,7 @@
if (minor != nullptr) *minor = cnx->minor;
return EGL_TRUE;
}
- while(eglIsInitialized) {
+ while (eglIsInitialized) {
refCond.wait(_l);
}
}
@@ -263,40 +274,31 @@
if (cnx->dso) {
EGLDisplay idpy = disp.dpy;
if (cnx->egl.eglInitialize(idpy, &cnx->major, &cnx->minor)) {
- //ALOGD("initialized dpy=%p, ver=%d.%d, cnx=%p",
+ // ALOGD("initialized dpy=%p, ver=%d.%d, cnx=%p",
// idpy, cnx->major, cnx->minor, cnx);
// display is now initialized
disp.state = egl_display_t::INITIALIZED;
// get the query-strings for this display for each implementation
- disp.queryString.vendor = cnx->egl.eglQueryString(idpy,
- EGL_VENDOR);
- disp.queryString.version = cnx->egl.eglQueryString(idpy,
- EGL_VERSION);
- disp.queryString.extensions = cnx->egl.eglQueryString(idpy,
- EGL_EXTENSIONS);
- disp.queryString.clientApi = cnx->egl.eglQueryString(idpy,
- EGL_CLIENT_APIS);
+ disp.queryString.vendor = cnx->egl.eglQueryString(idpy, EGL_VENDOR);
+ disp.queryString.version = cnx->egl.eglQueryString(idpy, EGL_VERSION);
+ disp.queryString.extensions = cnx->egl.eglQueryString(idpy, EGL_EXTENSIONS);
+ disp.queryString.clientApi = cnx->egl.eglQueryString(idpy, EGL_CLIENT_APIS);
} else {
ALOGW("eglInitialize(%p) failed (%s)", idpy,
- egl_tls_t::egl_strerror(cnx->egl.eglGetError()));
+ egl_tls_t::egl_strerror(cnx->egl.eglGetError()));
}
}
if (cnx->minor == 5) {
// full list in egl_entries.in
- if (!cnx->egl.eglCreateImage ||
- !cnx->egl.eglDestroyImage ||
- !cnx->egl.eglGetPlatformDisplay ||
- !cnx->egl.eglCreatePlatformWindowSurface ||
- !cnx->egl.eglCreatePlatformPixmapSurface ||
- !cnx->egl.eglCreateSync ||
- !cnx->egl.eglDestroySync ||
- !cnx->egl.eglClientWaitSync ||
- !cnx->egl.eglGetSyncAttrib ||
- !cnx->egl.eglWaitSync) {
+ if (!cnx->egl.eglCreateImage || !cnx->egl.eglDestroyImage ||
+ !cnx->egl.eglGetPlatformDisplay || !cnx->egl.eglCreatePlatformWindowSurface ||
+ !cnx->egl.eglCreatePlatformPixmapSurface || !cnx->egl.eglCreateSync ||
+ !cnx->egl.eglDestroySync || !cnx->egl.eglClientWaitSync ||
+ !cnx->egl.eglGetSyncAttrib || !cnx->egl.eglWaitSync) {
ALOGE("Driver indicates EGL 1.5 support, but does not have "
"a critical API");
cnx->minor = 4;
@@ -391,7 +393,6 @@
}
EGLBoolean egl_display_t::terminate() {
-
{ // scope for refLock
std::unique_lock<std::mutex> _rl(refLock);
if (refs == 0) {
@@ -424,7 +425,7 @@
}
if (cnx->egl.eglTerminate(disp.dpy) == EGL_FALSE) {
ALOGW("eglTerminate(%p) failed (%s)", disp.dpy,
- egl_tls_t::egl_strerror(cnx->egl.eglGetError()));
+ egl_tls_t::egl_strerror(cnx->egl.eglGetError()));
}
// REVISIT: it's unclear what to do if eglTerminate() fails
disp.state = egl_display_t::TERMINATED;
@@ -457,8 +458,7 @@
return res;
}
-void egl_display_t::loseCurrent(egl_context_t * cur_c)
-{
+void egl_display_t::loseCurrent(egl_context_t* cur_c) {
if (cur_c) {
egl_display_t* display = cur_c->getDisplay();
if (display) {
@@ -467,8 +467,7 @@
}
}
-void egl_display_t::loseCurrentImpl(egl_context_t * cur_c)
-{
+void egl_display_t::loseCurrentImpl(egl_context_t* cur_c) {
// by construction, these are either 0 or valid (possibly terminated)
// it should be impossible for these to be invalid
ContextRef _cur_c(cur_c);
@@ -478,7 +477,6 @@
{ // scope for the lock
std::lock_guard<std::mutex> _l(lock);
cur_c->onLooseCurrent();
-
}
// This cannot be called with the lock held because it might end-up
@@ -489,10 +487,9 @@
_cur_d.release();
}
-EGLBoolean egl_display_t::makeCurrent(egl_context_t* c, egl_context_t* cur_c,
- EGLSurface draw, EGLSurface read, EGLContext /*ctx*/,
- EGLSurface impl_draw, EGLSurface impl_read, EGLContext impl_ctx)
-{
+EGLBoolean egl_display_t::makeCurrent(egl_context_t* c, egl_context_t* cur_c, EGLSurface draw,
+ EGLSurface read, EGLContext /*ctx*/, EGLSurface impl_draw,
+ EGLSurface impl_read, EGLContext impl_ctx) {
EGLBoolean result;
// by construction, these are either 0 or valid (possibly terminated)
@@ -504,14 +501,12 @@
{ // scope for the lock
std::lock_guard<std::mutex> _l(lock);
if (c) {
- result = c->cnx->egl.eglMakeCurrent(
- disp.dpy, impl_draw, impl_read, impl_ctx);
+ result = c->cnx->egl.eglMakeCurrent(disp.dpy, impl_draw, impl_read, impl_ctx);
if (result == EGL_TRUE) {
c->onMakeCurrent(draw, read);
}
} else {
- result = cur_c->cnx->egl.eglMakeCurrent(
- disp.dpy, impl_draw, impl_read, impl_ctx);
+ result = cur_c->cnx->egl.eglMakeCurrent(disp.dpy, impl_draw, impl_read, impl_ctx);
if (result == EGL_TRUE) {
cur_c->onLooseCurrent();
}
@@ -537,6 +532,23 @@
return findExtension(mExtensionString.c_str(), name, nameLen);
}
-// ----------------------------------------------------------------------------
+egl_display_t* validate_display(EGLDisplay dpy) {
+ egl_display_t* const dp = get_display(dpy);
+ if (!dp) return setError(EGL_BAD_DISPLAY, (egl_display_t*)nullptr);
+ if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, (egl_display_t*)nullptr);
+
+ return dp;
+}
+
+egl_display_t* validate_display_connection(EGLDisplay dpy, egl_connection_t** outCnx) {
+ *outCnx = nullptr;
+ egl_display_t* dp = validate_display(dpy);
+ if (!dp) return dp;
+ *outCnx = &gEGLImpl;
+ if ((*outCnx)->dso == nullptr) {
+ return setError(EGL_BAD_CONFIG, (egl_display_t*)nullptr);
+ }
+ return dp;
+}
+
}; // namespace android
-// ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index e117314..87c2176 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -17,26 +17,22 @@
#ifndef ANDROID_EGL_DISPLAY_H
#define ANDROID_EGL_DISPLAY_H
-
-#include <stdint.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
#include <stddef.h>
+#include <stdint.h>
#include <condition_variable>
+#include <map>
+#include <memory>
#include <mutex>
#include <string>
#include <unordered_set>
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
-#include <cutils/compiler.h>
-
-#include "egldefs.h"
#include "../hooks.h"
+#include "egldefs.h"
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
class egl_object_t;
class egl_context_t;
@@ -45,25 +41,25 @@
bool findExtension(const char* exts, const char* name, size_t nameLen = 0);
bool needsAndroidPEglMitigation();
-// ----------------------------------------------------------------------------
-
class EGLAPI egl_display_t { // marked as EGLAPI for testing purposes
- static egl_display_t sDisplay[NUM_DISPLAYS];
+ static std::map<EGLDisplay, std::unique_ptr<egl_display_t>> displayMap;
+ static std::mutex displayMapLock;
EGLDisplay getDisplay(EGLNativeDisplayType display);
- EGLDisplay getPlatformDisplay(EGLNativeDisplayType display, const EGLAttrib* attrib_list);
- void loseCurrentImpl(egl_context_t * cur_c);
+ static EGLDisplay getPlatformDisplay(EGLNativeDisplayType display,
+ const EGLAttrib* attrib_list);
+ void loseCurrentImpl(egl_context_t* cur_c);
public:
enum {
NOT_INITIALIZED = 0,
- INITIALIZED = 1,
- TERMINATED = 2
+ INITIALIZED = 1,
+ TERMINATED = 2,
};
egl_display_t();
~egl_display_t();
- EGLBoolean initialize(EGLint *major, EGLint *minor);
+ EGLBoolean initialize(EGLint* major, EGLint* minor);
EGLBoolean terminate();
// add object to this display's list
@@ -76,123 +72,69 @@
static egl_display_t* get(EGLDisplay dpy);
static EGLDisplay getFromNativeDisplay(EGLNativeDisplayType disp, const EGLAttrib* attrib_list);
- EGLBoolean makeCurrent(egl_context_t* c, egl_context_t* cur_c,
- EGLSurface draw, EGLSurface read, EGLContext ctx,
- EGLSurface impl_draw, EGLSurface impl_read, EGLContext impl_ctx);
- static void loseCurrent(egl_context_t * cur_c);
+ EGLBoolean makeCurrent(egl_context_t* c, egl_context_t* cur_c, EGLSurface draw, EGLSurface read,
+ EGLContext ctx, EGLSurface impl_draw, EGLSurface impl_read,
+ EGLContext impl_ctx);
+ static void loseCurrent(egl_context_t* cur_c);
inline bool isReady() const { return (refs > 0); }
inline bool isValid() const { return magic == '_dpy'; }
inline bool isAlive() const { return isValid(); }
- char const * getVendorString() const { return mVendorString.c_str(); }
- char const * getVersionString() const { return mVersionString.c_str(); }
- char const * getClientApiString() const { return mClientApiString.c_str(); }
- char const * getExtensionString() const { return mExtensionString.c_str(); }
+ char const* getVendorString() const { return mVendorString.c_str(); }
+ char const* getVersionString() const { return mVersionString.c_str(); }
+ char const* getClientApiString() const { return mClientApiString.c_str(); }
+ char const* getExtensionString() const { return mExtensionString.c_str(); }
bool haveExtension(const char* name, size_t nameLen = 0) const;
inline uint32_t getRefsCount() const { return refs; }
struct strings_t {
- char const * vendor;
- char const * version;
- char const * clientApi;
- char const * extensions;
+ char const* vendor;
+ char const* version;
+ char const* clientApi;
+ char const* extensions;
};
struct DisplayImpl {
- DisplayImpl() : dpy(EGL_NO_DISPLAY), state(NOT_INITIALIZED) { }
- EGLDisplay dpy;
- EGLint state;
- strings_t queryString;
+ DisplayImpl() : dpy(EGL_NO_DISPLAY), state(NOT_INITIALIZED) {}
+ EGLDisplay dpy;
+ EGLint state;
+ strings_t queryString;
};
private:
- uint32_t magic;
+ uint32_t magic;
public:
- DisplayImpl disp;
- bool finishOnSwap; // property: debug.egl.finish
- bool traceGpuCompletion; // property: debug.egl.traceGpuCompletion
- bool hasColorSpaceSupport;
+ DisplayImpl disp;
+ bool finishOnSwap; // property: debug.egl.finish
+ bool traceGpuCompletion; // property: debug.egl.traceGpuCompletion
+ bool hasColorSpaceSupport;
private:
- friend class egl_display_ptr;
-
- uint32_t refs;
- bool eglIsInitialized;
- mutable std::mutex lock;
- mutable std::mutex refLock;
- mutable std::condition_variable refCond;
- std::unordered_set<egl_object_t*> objects;
- std::string mVendorString;
- std::string mVersionString;
- std::string mClientApiString;
- std::string mExtensionString;
+ uint32_t refs;
+ bool eglIsInitialized;
+ mutable std::mutex lock;
+ mutable std::mutex refLock;
+ mutable std::condition_variable refCond;
+ std::unordered_set<egl_object_t*> objects;
+ std::string mVendorString;
+ std::string mVersionString;
+ std::string mClientApiString;
+ std::string mExtensionString;
};
-// ----------------------------------------------------------------------------
-
-// An egl_display_ptr is a kind of smart pointer for egl_display_t objects.
-// It doesn't refcount the egl_display_t, but does ensure that the underlying
-// EGL implementation is "awake" (not hibernating) and ready for use as long
-// as the egl_display_ptr exists.
-class egl_display_ptr {
-public:
- explicit egl_display_ptr(egl_display_t* dpy): mDpy(dpy) {}
-
- // We only really need a C++11 move constructor, not a copy constructor.
- // A move constructor would save an enter()/leave() pair on every EGL API
- // call. But enabling -std=c++0x causes lots of errors elsewhere, so I
- // can't use a move constructor until those are cleaned up.
- //
- // egl_display_ptr(egl_display_ptr&& other) {
- // mDpy = other.mDpy;
- // other.mDpy = NULL;
- // }
- //
- egl_display_ptr(const egl_display_ptr& other): mDpy(other.mDpy) {}
-
- ~egl_display_ptr() {}
-
- const egl_display_t* operator->() const { return mDpy; }
- egl_display_t* operator->() { return mDpy; }
-
- const egl_display_t* get() const { return mDpy; }
- egl_display_t* get() { return mDpy; }
-
- operator bool() const { return mDpy != nullptr; }
-
-private:
- egl_display_t* mDpy;
-
- // non-assignable
- egl_display_ptr& operator=(const egl_display_ptr&);
-};
-
-// ----------------------------------------------------------------------------
-
-inline egl_display_ptr get_display(EGLDisplay dpy) {
- return egl_display_ptr(egl_display_t::get(dpy));
-}
-
-// Does not ensure EGL is unhibernated. Use with caution: calls into the
-// underlying EGL implementation are not safe.
-inline egl_display_t* get_display_nowake(EGLDisplay dpy) {
+inline egl_display_t* get_display(EGLDisplay dpy) {
return egl_display_t::get(dpy);
}
-// ----------------------------------------------------------------------------
-
-egl_display_ptr validate_display(EGLDisplay dpy);
-egl_display_ptr validate_display_connection(EGLDisplay dpy,
- egl_connection_t*& cnx);
+egl_display_t* validate_display(EGLDisplay dpy);
+egl_display_t* validate_display_connection(EGLDisplay dpy, egl_connection_t** outCnx);
EGLBoolean validate_display_context(EGLDisplay dpy, EGLContext ctx);
EGLBoolean validate_display_surface(EGLDisplay dpy, EGLSurface surface);
-// ----------------------------------------------------------------------------
}; // namespace android
-// ----------------------------------------------------------------------------
#endif // ANDROID_EGL_DISPLAY_H
diff --git a/opengl/libs/EGL/egl_entries.in b/opengl/libs/EGL/egl_entries.in
index 2921d51..1c91f1d 100644
--- a/opengl/libs/EGL/egl_entries.in
+++ b/opengl/libs/EGL/egl_entries.in
@@ -104,11 +104,6 @@
EGL_ENTRY(EGLuint64NV, eglGetSystemTimeFrequencyNV, void)
EGL_ENTRY(EGLuint64NV, eglGetSystemTimeNV, void)
-/* IMG extensions */
-
-EGL_ENTRY(EGLBoolean, eglHibernateProcessIMG, void)
-EGL_ENTRY(EGLBoolean, eglAwakenProcessIMG, void)
-
/* Partial update extensions */
EGL_ENTRY(EGLBoolean, eglSwapBuffersWithDamageKHR, EGLDisplay, EGLSurface, EGLint *, EGLint)
diff --git a/opengl/libs/EGL/egl_layers.cpp b/opengl/libs/EGL/egl_layers.cpp
index ea86c9a..9752e38 100644
--- a/opengl/libs/EGL/egl_layers.cpp
+++ b/opengl/libs/EGL/egl_layers.cpp
@@ -85,17 +85,21 @@
// Look up which GPA we should use
int gpaIndex = func_indices["eglGetProcAddress"];
- ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) <- using GPA from this index", name, gpaIndex);
+ ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) <- using GPA from this index", name,
+ gpaIndex);
EGLFuncPointer gpaNext = (*next_layer_funcs)[gpaIndex];
- ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) gpaNext(%llu) <- using GPA at this address", name, gpaIndex, (unsigned long long)gpaNext);
-
+ ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) gpaNext(%llu) <- using GPA at this "
+ "address",
+ name, gpaIndex, (unsigned long long)gpaNext);
// Call it for the requested function
typedef void* (*PFNEGLGETPROCADDRESSPROC)(const char*);
PFNEGLGETPROCADDRESSPROC next = reinterpret_cast<PFNEGLGETPROCADDRESSPROC>(gpaNext);
val = reinterpret_cast<EGLFuncPointer>(next(name));
- ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) gpaNext(%llu) Got back (%llu) from GPA", name, gpaIndex, (unsigned long long)gpaNext, (unsigned long long)val);
+ ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) gpaNext(%llu) Got back (%llu) from "
+ "GPA",
+ name, gpaIndex, (unsigned long long)gpaNext, (unsigned long long)val);
// We should store it now, but to do that, we need to move func_idx to the class so we can
// increment it separately
@@ -105,7 +109,9 @@
int index = func_indices[name];
val = (*next_layer_funcs)[index];
- ALOGV("getNextLayerProcAddress - name(%s) index(%i) entry(%llu) - Got a hit, returning known entry", name, index, (unsigned long long)val);
+ ALOGV("getNextLayerProcAddress - name(%s) index(%i) entry(%llu) - Got a hit, returning known "
+ "entry",
+ name, index, (unsigned long long)val);
return reinterpret_cast<void*>(val);
}
@@ -117,20 +123,26 @@
// Some names overlap, only fill with initial entry
// This does mean that some indices will not be used
if (func_indices.find(name) == func_indices.end()) {
- ALOGV("SetupFuncMaps - name(%s), func_idx(%i), No entry for func_indices, assigning now", name, func_idx);
+ ALOGV("SetupFuncMaps - name(%s), func_idx(%i), No entry for func_indices, assigning "
+ "now",
+ name, func_idx);
func_names[func_idx] = name;
func_indices[name] = func_idx;
} else {
- ALOGV("SetupFuncMaps - name(%s), func_idx(%i), Found entry for func_indices", name, func_idx);
+ ALOGV("SetupFuncMaps - name(%s), func_idx(%i), Found entry for func_indices", name,
+ func_idx);
}
// Populate layer_functions once with initial value
// These values will arrive in priority order, starting with platform entries
if (functions[func_idx] == nullptr) {
- ALOGV("SetupFuncMaps - name(%s), func_idx(%i), No entry for functions, assigning (%llu)", name, func_idx, (unsigned long long) *curr);
+ ALOGV("SetupFuncMaps - name(%s), func_idx(%i), No entry for functions, assigning "
+ "(%llu)",
+ name, func_idx, (unsigned long long)*curr);
functions[func_idx] = *curr;
} else {
- ALOGV("SetupFuncMaps - name(%s), func_idx(%i), Found entry for functions (%llu)", name, func_idx, (unsigned long long) functions[func_idx]);
+ ALOGV("SetupFuncMaps - name(%s), func_idx(%i), Found entry for functions (%llu)", name,
+ func_idx, (unsigned long long)functions[func_idx]);
}
entries++;
@@ -380,8 +392,8 @@
auto app_namespace = android::GraphicsEnv::getInstance().getAppNamespace();
if (app_namespace && !android::base::StartsWith(layer, kSystemLayerLibraryDir)) {
char* error_message = nullptr;
- dlhandle_ = OpenNativeLibraryInNamespace(
- app_namespace, layer.c_str(), &native_bridge_, &error_message);
+ dlhandle_ = OpenNativeLibraryInNamespace(app_namespace, layer.c_str(),
+ &native_bridge_, &error_message);
if (!dlhandle_) {
ALOGE("Failed to load layer %s with error: %s", layer.c_str(),
error_message);
diff --git a/opengl/libs/EGL/egl_layers.h b/opengl/libs/EGL/egl_layers.h
index 1e2783f..705525d 100644
--- a/opengl/libs/EGL/egl_layers.h
+++ b/opengl/libs/EGL/egl_layers.h
@@ -17,19 +17,18 @@
#ifndef ANDROID_EGL_LAYERS_H
#define ANDROID_EGL_LAYERS_H
+#include <EGL/egldefs.h>
+#include <android/dlext.h>
+#include <dlfcn.h>
+#include <nativebridge/native_bridge.h>
+#include <nativeloader/native_loader.h>
+
#include <string>
#include <unordered_map>
#include <vector>
-#include <android/dlext.h>
-#include <dlfcn.h>
-
-#include <EGL/egldefs.h>
#include "egl_platform_entries.h"
-#include <nativebridge/native_bridge.h>
-#include <nativeloader/native_loader.h>
-
typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer;
namespace android {
@@ -46,8 +45,8 @@
void LoadLayers();
void InitLayers(egl_connection_t*);
- void LayerPlatformEntries(layer_setup_func layer_setup, EGLFuncPointer*, char const* const*);
- void LayerDriverEntries(layer_setup_func layer_setup, EGLFuncPointer*, char const* const*);
+ void LayerPlatformEntries(layer_setup_func layer_setup, EGLFuncPointer*, const char* const*);
+ void LayerDriverEntries(layer_setup_func layer_setup, EGLFuncPointer*, const char* const*);
bool Initialized();
std::string GetDebugLayers();
@@ -59,18 +58,23 @@
std::vector<layer_setup_func> layer_setup_;
private:
- LayerLoader() : layers_loaded_(false), initialized_(false), current_layer_(0), dlhandle_(nullptr), native_bridge_(false){};
+ LayerLoader()
+ : layers_loaded_(false),
+ initialized_(false),
+ current_layer_(0),
+ dlhandle_(nullptr),
+ native_bridge_(false){};
bool layers_loaded_;
bool initialized_;
unsigned current_layer_;
void* dlhandle_;
bool native_bridge_;
- template<typename Func = void*>
+ template <typename Func = void*>
Func GetTrampoline(const char* name) const {
if (native_bridge_) {
- return reinterpret_cast<Func>(android::NativeBridgeGetTrampoline(
- dlhandle_, name, nullptr, 0));
+ return reinterpret_cast<Func>(
+ android::NativeBridgeGetTrampoline(dlhandle_, name, nullptr, 0));
}
return reinterpret_cast<Func>(dlsym(dlhandle_, name));
}
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index ff4fe2d..847b351 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -18,19 +18,14 @@
#include <sstream>
-
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
-egl_object_t::egl_object_t(egl_display_t* disp) :
- display(disp), count(1) {
+egl_object_t::egl_object_t(egl_display_t* disp) : display(disp), count(1) {
// NOTE: this does an implicit incRef
display->addObject(this);
}
-egl_object_t::~egl_object_t() {
-}
+egl_object_t::~egl_object_t() {}
void egl_object_t::terminate() {
// this marks the object as "terminated"
@@ -53,8 +48,6 @@
return display->getObject(object);
}
-// ----------------------------------------------------------------------------
-
egl_surface_t::egl_surface_t(egl_display_t* dpy, EGLConfig config, EGLNativeWindowType win,
EGLSurface surface, EGLint colorSpace, egl_connection_t const* cnx)
: egl_object_t(dpy),
@@ -66,10 +59,10 @@
colorSpace(colorSpace),
egl_smpte2086_dirty(false),
egl_cta861_3_dirty(false) {
- egl_smpte2086_metadata.displayPrimaryRed = { EGL_DONT_CARE, EGL_DONT_CARE };
- egl_smpte2086_metadata.displayPrimaryGreen = { EGL_DONT_CARE, EGL_DONT_CARE };
- egl_smpte2086_metadata.displayPrimaryBlue = { EGL_DONT_CARE, EGL_DONT_CARE };
- egl_smpte2086_metadata.whitePoint = { EGL_DONT_CARE, EGL_DONT_CARE };
+ egl_smpte2086_metadata.displayPrimaryRed = {EGL_DONT_CARE, EGL_DONT_CARE};
+ egl_smpte2086_metadata.displayPrimaryGreen = {EGL_DONT_CARE, EGL_DONT_CARE};
+ egl_smpte2086_metadata.displayPrimaryBlue = {EGL_DONT_CARE, EGL_DONT_CARE};
+ egl_smpte2086_metadata.whitePoint = {EGL_DONT_CARE, EGL_DONT_CARE};
egl_smpte2086_metadata.maxLuminance = EGL_DONT_CARE;
egl_smpte2086_metadata.minLuminance = EGL_DONT_CARE;
egl_cta861_3_metadata.maxFrameAverageLightLevel = EGL_DONT_CARE;
@@ -173,16 +166,30 @@
return EGL_FALSE;
}
- metadata.displayPrimaryRed.x = static_cast<float>(egl_smpte2086_metadata.displayPrimaryRed.x) / EGL_METADATA_SCALING_EXT;
- metadata.displayPrimaryRed.y = static_cast<float>(egl_smpte2086_metadata.displayPrimaryRed.y) / EGL_METADATA_SCALING_EXT;
- metadata.displayPrimaryGreen.x = static_cast<float>(egl_smpte2086_metadata.displayPrimaryGreen.x) / EGL_METADATA_SCALING_EXT;
- metadata.displayPrimaryGreen.y = static_cast<float>(egl_smpte2086_metadata.displayPrimaryGreen.y) / EGL_METADATA_SCALING_EXT;
- metadata.displayPrimaryBlue.x = static_cast<float>(egl_smpte2086_metadata.displayPrimaryBlue.x) / EGL_METADATA_SCALING_EXT;
- metadata.displayPrimaryBlue.y = static_cast<float>(egl_smpte2086_metadata.displayPrimaryBlue.y) / EGL_METADATA_SCALING_EXT;
- metadata.whitePoint.x = static_cast<float>(egl_smpte2086_metadata.whitePoint.x) / EGL_METADATA_SCALING_EXT;
- metadata.whitePoint.y = static_cast<float>(egl_smpte2086_metadata.whitePoint.y) / EGL_METADATA_SCALING_EXT;
- metadata.maxLuminance = static_cast<float>(egl_smpte2086_metadata.maxLuminance) / EGL_METADATA_SCALING_EXT;
- metadata.minLuminance = static_cast<float>(egl_smpte2086_metadata.minLuminance) / EGL_METADATA_SCALING_EXT;
+ metadata.displayPrimaryRed.x = static_cast<float>(egl_smpte2086_metadata.displayPrimaryRed.x) /
+ EGL_METADATA_SCALING_EXT;
+ metadata.displayPrimaryRed.y = static_cast<float>(egl_smpte2086_metadata.displayPrimaryRed.y) /
+ EGL_METADATA_SCALING_EXT;
+ metadata.displayPrimaryGreen.x =
+ static_cast<float>(egl_smpte2086_metadata.displayPrimaryGreen.x) /
+ EGL_METADATA_SCALING_EXT;
+ metadata.displayPrimaryGreen.y =
+ static_cast<float>(egl_smpte2086_metadata.displayPrimaryGreen.y) /
+ EGL_METADATA_SCALING_EXT;
+ metadata.displayPrimaryBlue.x =
+ static_cast<float>(egl_smpte2086_metadata.displayPrimaryBlue.x) /
+ EGL_METADATA_SCALING_EXT;
+ metadata.displayPrimaryBlue.y =
+ static_cast<float>(egl_smpte2086_metadata.displayPrimaryBlue.y) /
+ EGL_METADATA_SCALING_EXT;
+ metadata.whitePoint.x =
+ static_cast<float>(egl_smpte2086_metadata.whitePoint.x) / EGL_METADATA_SCALING_EXT;
+ metadata.whitePoint.y =
+ static_cast<float>(egl_smpte2086_metadata.whitePoint.y) / EGL_METADATA_SCALING_EXT;
+ metadata.maxLuminance =
+ static_cast<float>(egl_smpte2086_metadata.maxLuminance) / EGL_METADATA_SCALING_EXT;
+ metadata.minLuminance =
+ static_cast<float>(egl_smpte2086_metadata.minLuminance) / EGL_METADATA_SCALING_EXT;
return EGL_TRUE;
}
@@ -196,13 +203,15 @@
return EGL_FALSE;
}
- metadata.maxContentLightLevel = static_cast<float>(egl_cta861_3_metadata.maxContentLightLevel) / EGL_METADATA_SCALING_EXT;
- metadata.maxFrameAverageLightLevel = static_cast<float>(egl_cta861_3_metadata.maxFrameAverageLightLevel) / EGL_METADATA_SCALING_EXT;
+ metadata.maxContentLightLevel = static_cast<float>(egl_cta861_3_metadata.maxContentLightLevel) /
+ EGL_METADATA_SCALING_EXT;
+ metadata.maxFrameAverageLightLevel =
+ static_cast<float>(egl_cta861_3_metadata.maxFrameAverageLightLevel) /
+ EGL_METADATA_SCALING_EXT;
return EGL_TRUE;
}
-
EGLBoolean egl_surface_t::getColorSpaceAttribute(EGLint attribute, EGLint* value) const {
if (attribute == EGL_GL_COLORSPACE_KHR) {
*value = colorSpace;
@@ -211,7 +220,7 @@
return EGL_FALSE;
}
-EGLBoolean egl_surface_t::getSmpte2086Attribute(EGLint attribute, EGLint *value) const {
+EGLBoolean egl_surface_t::getSmpte2086Attribute(EGLint attribute, EGLint* value) const {
switch (attribute) {
case EGL_SMPTE2086_DISPLAY_PRIMARY_RX_EXT:
*value = egl_smpte2086_metadata.displayPrimaryRed.x;
@@ -257,7 +266,7 @@
return EGL_FALSE;
}
-EGLBoolean egl_surface_t::getCta8613Attribute(EGLint attribute, EGLint *value) const {
+EGLBoolean egl_surface_t::getCta8613Attribute(EGLint attribute, EGLint* value) const {
switch (attribute) {
case EGL_CTA861_3_MAX_CONTENT_LIGHT_LEVEL_EXT:
*value = egl_cta861_3_metadata.maxContentLightLevel;
@@ -276,13 +285,16 @@
egl_object_t::terminate();
}
-// ----------------------------------------------------------------------------
-
egl_context_t::egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config,
- egl_connection_t const* cnx, int version) :
- egl_object_t(get_display_nowake(dpy)), dpy(dpy), context(context),
- config(config), read(nullptr), draw(nullptr), cnx(cnx), version(version) {
-}
+ egl_connection_t const* cnx, int version)
+ : egl_object_t(get_display(dpy)),
+ dpy(dpy),
+ context(context),
+ config(config),
+ read(nullptr),
+ draw(nullptr),
+ cnx(cnx),
+ version(version) {}
void egl_context_t::onLooseCurrent() {
read = nullptr;
@@ -297,31 +309,39 @@
* Here we cache the GL_EXTENSIONS string for this context and we
* add the extensions always handled by the wrapper
*/
+ if (!gl_extensions.empty()) return;
- if (gl_extensions.empty()) {
- // call the implementation's glGetString(GL_EXTENSIONS)
- const char* exts = (const char *)gEGLImpl.hooks[version]->gl.glGetString(GL_EXTENSIONS);
+ // call the implementation's glGetString(GL_EXTENSIONS)
+ const char* exts = (const char*)gEGLImpl.hooks[version]->gl.glGetString(GL_EXTENSIONS);
+ if (!exts) return;
- // If this context is sharing with another context, and the other context was reset
- // e.g. due to robustness failure, this context might also be reset and glGetString can
- // return NULL.
- if (exts) {
- gl_extensions = exts;
- if (gl_extensions.find("GL_EXT_debug_marker") == std::string::npos) {
- gl_extensions.insert(0, "GL_EXT_debug_marker ");
- }
+ // If this context is sharing with another context, and the other context was reset
+ // e.g. due to robustness failure, this context might also be reset and glGetString can
+ // return NULL.
+ gl_extensions = exts;
+ if (gl_extensions.find("GL_EXT_debug_marker") == std::string::npos) {
+ gl_extensions.insert(0, "GL_EXT_debug_marker ");
+ // eglGetProcAddress could return function pointers to these
+ // functions while they actually don't work. Fix them now.
+ __eglMustCastToProperFunctionPointerType* f;
+ f = (__eglMustCastToProperFunctionPointerType*)&gEGLImpl.hooks[version]
+ ->gl.glInsertEventMarkerEXT;
+ if (*f != gl_noop) *f = gl_noop;
+ f = (__eglMustCastToProperFunctionPointerType*)&gEGLImpl.hooks[version]
+ ->gl.glPushGroupMarkerEXT;
+ if (*f != gl_noop) *f = gl_noop;
+ f = (__eglMustCastToProperFunctionPointerType*)&gEGLImpl.hooks[version]
+ ->gl.glPopGroupMarkerEXT;
+ if (*f != gl_noop) *f = gl_noop;
+ }
- // tokenize the supported extensions for the glGetStringi() wrapper
- std::stringstream ss;
- std::string str;
- ss << gl_extensions;
- while (ss >> str) {
- tokenized_gl_extensions.push_back(str);
- }
- }
+ // tokenize the supported extensions for the glGetStringi() wrapper
+ std::stringstream ss;
+ std::string str;
+ ss << gl_extensions;
+ while (ss >> str) {
+ tokenized_gl_extensions.push_back(str);
}
}
-// ----------------------------------------------------------------------------
}; // namespace android
-// ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h
index fb2bdf4..e593b1c 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -17,30 +17,25 @@
#ifndef ANDROID_EGL_OBJECT_H
#define ANDROID_EGL_OBJECT_H
-#include <atomic>
-#include <stdint.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <log/log.h>
#include <stddef.h>
+#include <stdint.h>
+#include <system/window.h>
+#include <atomic>
#include <string>
#include <vector>
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
-#include <system/window.h>
-
-#include <log/log.h>
-
#include "egl_display.h"
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
class egl_display_t;
class egl_object_t {
- egl_display_t *display;
+ egl_display_t* display;
mutable std::atomic_size_t count;
protected:
@@ -64,6 +59,7 @@
egl_object_t* ref;
LocalRef() = delete;
LocalRef(const LocalRef* rhs) = delete;
+
public:
~LocalRef();
explicit LocalRef(egl_object_t* rhs);
@@ -73,9 +69,7 @@
ref = native;
}
}
- inline N* get() {
- return static_cast<N*>(ref);
- }
+ inline N* get() { return static_cast<N*>(ref); }
void acquire() const;
void release() const;
void terminate();
@@ -84,7 +78,7 @@
friend class LocalRef;
};
-template<typename N, typename T>
+template <typename N, typename T>
egl_object_t::LocalRef<N, T>::LocalRef(egl_object_t* rhs) : ref(rhs) {
if (ref) {
ref->incRef();
@@ -92,21 +86,21 @@
}
template <typename N, typename T>
-egl_object_t::LocalRef<N,T>::~LocalRef() {
+egl_object_t::LocalRef<N, T>::~LocalRef() {
if (ref) {
ref->destroy();
}
}
template <typename N, typename T>
-void egl_object_t::LocalRef<N,T>::acquire() const {
+void egl_object_t::LocalRef<N, T>::acquire() const {
if (ref) {
ref->incRef();
}
}
template <typename N, typename T>
-void egl_object_t::LocalRef<N,T>::release() const {
+void egl_object_t::LocalRef<N, T>::release() const {
if (ref) {
if (ref->decRef() == 1) {
// shouldn't happen because this is called from LocalRef
@@ -116,7 +110,7 @@
}
template <typename N, typename T>
-void egl_object_t::LocalRef<N,T>::terminate() {
+void egl_object_t::LocalRef<N, T>::terminate() {
if (ref) {
ref->terminate();
}
@@ -128,6 +122,7 @@
protected:
~egl_surface_t();
void terminate() override;
+
public:
typedef egl_object_t::LocalRef<egl_surface_t, EGLSurface> Ref;
@@ -151,10 +146,13 @@
// it's not hard to imagine native games accessing them.
EGLSurface surface;
EGLConfig config;
+
private:
ANativeWindow* win;
+
public:
egl_connection_t const* cnx;
+
private:
bool connected;
void disconnect();
@@ -186,14 +184,15 @@
egl_cta861_3_metadata egl_cta861_3_metadata;
};
-class egl_context_t: public egl_object_t {
+class egl_context_t : public egl_object_t {
protected:
~egl_context_t() {}
+
public:
typedef egl_object_t::LocalRef<egl_context_t, EGLContext> Ref;
- egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config,
- egl_connection_t const* cnx, int version);
+ egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config, egl_connection_t const* cnx,
+ int version);
void onLooseCurrent();
void onMakeCurrent(EGLSurface draw, EGLSurface read);
@@ -209,30 +208,22 @@
std::vector<std::string> tokenized_gl_extensions;
};
-// ----------------------------------------------------------------------------
+typedef egl_surface_t::Ref SurfaceRef;
+typedef egl_context_t::Ref ContextRef;
-typedef egl_surface_t::Ref SurfaceRef;
-typedef egl_context_t::Ref ContextRef;
-
-// ----------------------------------------------------------------------------
-
-template<typename NATIVE, typename EGL>
+template <typename NATIVE, typename EGL>
static inline NATIVE* egl_to_native_cast(EGL arg) {
return reinterpret_cast<NATIVE*>(arg);
}
-static inline
-egl_surface_t* get_surface(EGLSurface surface) {
+static inline egl_surface_t* get_surface(EGLSurface surface) {
return egl_to_native_cast<egl_surface_t>(surface);
}
-static inline
-egl_context_t* get_context(EGLContext context) {
+static inline egl_context_t* get_context(EGLContext context) {
return egl_to_native_cast<egl_context_t>(context);
}
-// ----------------------------------------------------------------------------
}; // namespace android
-// ----------------------------------------------------------------------------
#endif // ANDROID_EGL_OBJECT_H
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index aa24e8e..f576660 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -18,36 +18,32 @@
#include "egl_platform_entries.h"
-#include <ctype.h>
-#include <dlfcn.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <EGL/eglext_angle.h>
-
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android/hardware_buffer.h>
-#include <graphicsenv/GraphicsEnv.h>
-#include <private/android/AHardwareBufferHelpers.h>
-
+#include <ctype.h>
#include <cutils/compiler.h>
+#include <dlfcn.h>
+#include <graphicsenv/GraphicsEnv.h>
#include <log/log.h>
+#include <private/android/AHardwareBufferHelpers.h>
+#include <stdlib.h>
+#include <string.h>
#include <condition_variable>
#include <deque>
#include <mutex>
-#include <unordered_map>
#include <string>
#include <thread>
+#include <unordered_map>
#include "../egl_impl.h"
-
+#include "EGL/egl.h"
+#include "EGL/eglext.h"
+#include "EGL/eglext_angle.h"
#include "egl_display.h"
-#include "egl_object.h"
#include "egl_layers.h"
+#include "egl_object.h"
#include "egl_tls.h"
#include "egl_trace.h"
@@ -80,71 +76,71 @@
* NOTE: Both strings MUST have a single space as the last character.
*/
-extern char const * const gBuiltinExtensionString;
-extern char const * const gExtensionString;
+extern const char* const gBuiltinExtensionString;
+extern const char* const gExtensionString;
// clang-format off
// Extensions implemented by the EGL wrapper.
-char const * const gBuiltinExtensionString =
- "EGL_KHR_get_all_proc_addresses "
- "EGL_ANDROID_presentation_time "
- "EGL_KHR_swap_buffers_with_damage "
- "EGL_ANDROID_get_native_client_buffer "
+const char* const gBuiltinExtensionString =
"EGL_ANDROID_front_buffer_auto_refresh "
"EGL_ANDROID_get_frame_timestamps "
- "EGL_EXT_surface_SMPTE2086_metadata "
+ "EGL_ANDROID_get_native_client_buffer "
+ "EGL_ANDROID_presentation_time "
"EGL_EXT_surface_CTA861_3_metadata "
+ "EGL_EXT_surface_SMPTE2086_metadata "
+ "EGL_KHR_get_all_proc_addresses "
+ "EGL_KHR_swap_buffers_with_damage "
;
// Allowed list of extensions exposed to applications if implemented in the vendor driver.
-char const * const gExtensionString =
- "EGL_KHR_image " // mandatory
- "EGL_KHR_image_base " // mandatory
+const char* const gExtensionString =
+ "EGL_ANDROID_image_native_buffer " // mandatory
+ "EGL_ANDROID_native_fence_sync " // strongly recommended
+ "EGL_ANDROID_recordable " // mandatory
+ "EGL_EXT_buffer_age " // strongly recommended with partial_update
+ "EGL_EXT_create_context_robustness "
"EGL_EXT_image_gl_colorspace "
- "EGL_KHR_image_pixmap "
- "EGL_KHR_lock_surface "
+ "EGL_EXT_pixel_format_float "
+ "EGL_EXT_protected_content "
+ "EGL_EXT_yuv_surface "
+ "EGL_IMG_context_priority "
+ "EGL_KHR_config_attribs "
+ "EGL_KHR_create_context "
+ "EGL_KHR_create_context_no_error "
+ "EGL_KHR_fence_sync "
"EGL_KHR_gl_colorspace "
+ "EGL_KHR_gl_renderbuffer_image "
"EGL_KHR_gl_texture_2D_image "
"EGL_KHR_gl_texture_3D_image "
"EGL_KHR_gl_texture_cubemap_image "
- "EGL_KHR_gl_renderbuffer_image "
+ "EGL_KHR_image " // mandatory
+ "EGL_KHR_image_base " // mandatory
+ "EGL_KHR_image_pixmap "
+ "EGL_KHR_lock_surface "
+ "EGL_KHR_mutable_render_buffer "
+ "EGL_KHR_no_config_context "
+ "EGL_KHR_partial_update " // strongly recommended
"EGL_KHR_reusable_sync "
- "EGL_KHR_fence_sync "
- "EGL_KHR_create_context "
- "EGL_KHR_config_attribs "
- "EGL_KHR_surfaceless_context "
"EGL_KHR_stream "
- "EGL_KHR_stream_fifo "
- "EGL_KHR_stream_producer_eglsurface "
"EGL_KHR_stream_consumer_gltexture "
"EGL_KHR_stream_cross_process_fd "
- "EGL_EXT_create_context_robustness "
- "EGL_NV_system_time "
- "EGL_ANDROID_image_native_buffer " // mandatory
+ "EGL_KHR_stream_fifo "
+ "EGL_KHR_stream_producer_eglsurface "
+ "EGL_KHR_surfaceless_context "
"EGL_KHR_wait_sync " // strongly recommended
- "EGL_ANDROID_recordable " // mandatory
- "EGL_KHR_partial_update " // strongly recommended
- "EGL_EXT_pixel_format_float "
- "EGL_EXT_buffer_age " // strongly recommended with partial_update
- "EGL_KHR_create_context_no_error "
- "EGL_KHR_mutable_render_buffer "
- "EGL_EXT_yuv_surface "
- "EGL_EXT_protected_content "
- "EGL_IMG_context_priority "
- "EGL_KHR_no_config_context "
+ "EGL_NV_context_priority_realtime "
+ "EGL_NV_system_time "
;
-char const * const gClientExtensionString =
+const char* const gClientExtensionString =
+ "EGL_ANDROID_GLES_layers "
+ "EGL_ANGLE_platform_angle "
"EGL_EXT_client_extensions "
"EGL_KHR_platform_android "
- "EGL_ANGLE_platform_angle "
- "EGL_ANDROID_GLES_layers";
-// clang-format on
+ ;
// extensions not exposed to applications but used by the ANDROID system
// "EGL_ANDROID_blob_cache " // strongly recommended
-// "EGL_IMG_hibernate_process " // optional
-// "EGL_ANDROID_native_fence_sync " // strongly recommended
// "EGL_ANDROID_framebuffer_target " // mandatory for HWC 1.1
/*
@@ -154,105 +150,69 @@
*/
static const extension_map_t sExtensionMap[] = {
// EGL_KHR_lock_surface
- { "eglLockSurfaceKHR",
- (__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR },
- { "eglUnlockSurfaceKHR",
- (__eglMustCastToProperFunctionPointerType)&eglUnlockSurfaceKHR },
+ { "eglLockSurfaceKHR", (__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR },
+ { "eglUnlockSurfaceKHR", (__eglMustCastToProperFunctionPointerType)&eglUnlockSurfaceKHR },
// EGL_KHR_image, EGL_KHR_image_base
- { "eglCreateImageKHR",
- (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR },
- { "eglDestroyImageKHR",
- (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR },
+ { "eglCreateImageKHR", (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR },
+ { "eglDestroyImageKHR", (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR },
// EGL_KHR_reusable_sync, EGL_KHR_fence_sync
- { "eglCreateSyncKHR",
- (__eglMustCastToProperFunctionPointerType)&eglCreateSyncKHR },
- { "eglDestroySyncKHR",
- (__eglMustCastToProperFunctionPointerType)&eglDestroySyncKHR },
- { "eglClientWaitSyncKHR",
- (__eglMustCastToProperFunctionPointerType)&eglClientWaitSyncKHR },
- { "eglSignalSyncKHR",
- (__eglMustCastToProperFunctionPointerType)&eglSignalSyncKHR },
- { "eglGetSyncAttribKHR",
- (__eglMustCastToProperFunctionPointerType)&eglGetSyncAttribKHR },
+ { "eglCreateSyncKHR", (__eglMustCastToProperFunctionPointerType)&eglCreateSyncKHR },
+ { "eglDestroySyncKHR", (__eglMustCastToProperFunctionPointerType)&eglDestroySyncKHR },
+ { "eglClientWaitSyncKHR", (__eglMustCastToProperFunctionPointerType)&eglClientWaitSyncKHR },
+ { "eglSignalSyncKHR", (__eglMustCastToProperFunctionPointerType)&eglSignalSyncKHR },
+ { "eglGetSyncAttribKHR", (__eglMustCastToProperFunctionPointerType)&eglGetSyncAttribKHR },
// EGL_NV_system_time
- { "eglGetSystemTimeFrequencyNV",
- (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeFrequencyNV },
- { "eglGetSystemTimeNV",
- (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeNV },
+ { "eglGetSystemTimeFrequencyNV", (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeFrequencyNV },
+ { "eglGetSystemTimeNV", (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeNV },
// EGL_KHR_wait_sync
- { "eglWaitSyncKHR",
- (__eglMustCastToProperFunctionPointerType)&eglWaitSyncKHR },
+ { "eglWaitSyncKHR", (__eglMustCastToProperFunctionPointerType)&eglWaitSyncKHR },
// EGL_ANDROID_presentation_time
- { "eglPresentationTimeANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglPresentationTimeANDROID },
+ { "eglPresentationTimeANDROID", (__eglMustCastToProperFunctionPointerType)&eglPresentationTimeANDROID },
// EGL_KHR_swap_buffers_with_damage
- { "eglSwapBuffersWithDamageKHR",
- (__eglMustCastToProperFunctionPointerType)&eglSwapBuffersWithDamageKHR },
+ { "eglSwapBuffersWithDamageKHR", (__eglMustCastToProperFunctionPointerType)&eglSwapBuffersWithDamageKHR },
// EGL_ANDROID_get_native_client_buffer
- { "eglGetNativeClientBufferANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetNativeClientBufferANDROID },
+ { "eglGetNativeClientBufferANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetNativeClientBufferANDROID },
// EGL_KHR_partial_update
- { "eglSetDamageRegionKHR",
- (__eglMustCastToProperFunctionPointerType)&eglSetDamageRegionKHR },
+ { "eglSetDamageRegionKHR", (__eglMustCastToProperFunctionPointerType)&eglSetDamageRegionKHR },
- { "eglCreateStreamKHR",
- (__eglMustCastToProperFunctionPointerType)&eglCreateStreamKHR },
- { "eglDestroyStreamKHR",
- (__eglMustCastToProperFunctionPointerType)&eglDestroyStreamKHR },
- { "eglStreamAttribKHR",
- (__eglMustCastToProperFunctionPointerType)&eglStreamAttribKHR },
- { "eglQueryStreamKHR",
- (__eglMustCastToProperFunctionPointerType)&eglQueryStreamKHR },
- { "eglQueryStreamu64KHR",
- (__eglMustCastToProperFunctionPointerType)&eglQueryStreamu64KHR },
- { "eglQueryStreamTimeKHR",
- (__eglMustCastToProperFunctionPointerType)&eglQueryStreamTimeKHR },
- { "eglCreateStreamProducerSurfaceKHR",
- (__eglMustCastToProperFunctionPointerType)&eglCreateStreamProducerSurfaceKHR },
- { "eglStreamConsumerGLTextureExternalKHR",
- (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerGLTextureExternalKHR },
- { "eglStreamConsumerAcquireKHR",
- (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerAcquireKHR },
- { "eglStreamConsumerReleaseKHR",
- (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerReleaseKHR },
- { "eglGetStreamFileDescriptorKHR",
- (__eglMustCastToProperFunctionPointerType)&eglGetStreamFileDescriptorKHR },
- { "eglCreateStreamFromFileDescriptorKHR",
- (__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR },
+ { "eglCreateStreamKHR", (__eglMustCastToProperFunctionPointerType)&eglCreateStreamKHR },
+ { "eglDestroyStreamKHR", (__eglMustCastToProperFunctionPointerType)&eglDestroyStreamKHR },
+ { "eglStreamAttribKHR", (__eglMustCastToProperFunctionPointerType)&eglStreamAttribKHR },
+ { "eglQueryStreamKHR", (__eglMustCastToProperFunctionPointerType)&eglQueryStreamKHR },
+ { "eglQueryStreamu64KHR", (__eglMustCastToProperFunctionPointerType)&eglQueryStreamu64KHR },
+ { "eglQueryStreamTimeKHR", (__eglMustCastToProperFunctionPointerType)&eglQueryStreamTimeKHR },
+ { "eglCreateStreamProducerSurfaceKHR", (__eglMustCastToProperFunctionPointerType)&eglCreateStreamProducerSurfaceKHR },
+ { "eglStreamConsumerGLTextureExternalKHR", (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerGLTextureExternalKHR },
+ { "eglStreamConsumerAcquireKHR", (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerAcquireKHR },
+ { "eglStreamConsumerReleaseKHR", (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerReleaseKHR },
+ { "eglGetStreamFileDescriptorKHR", (__eglMustCastToProperFunctionPointerType)&eglGetStreamFileDescriptorKHR },
+ { "eglCreateStreamFromFileDescriptorKHR", (__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR },
// EGL_ANDROID_get_frame_timestamps
- { "eglGetNextFrameIdANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetNextFrameIdANDROID },
- { "eglGetCompositorTimingANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingANDROID },
- { "eglGetCompositorTimingSupportedANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingSupportedANDROID },
- { "eglGetFrameTimestampsANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID },
- { "eglGetFrameTimestampSupportedANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampSupportedANDROID },
+ { "eglGetNextFrameIdANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetNextFrameIdANDROID },
+ { "eglGetCompositorTimingANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingANDROID },
+ { "eglGetCompositorTimingSupportedANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingSupportedANDROID },
+ { "eglGetFrameTimestampsANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID },
+ { "eglGetFrameTimestampSupportedANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampSupportedANDROID },
// EGL_ANDROID_native_fence_sync
- { "eglDupNativeFenceFDANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglDupNativeFenceFDANDROID },
+ { "eglDupNativeFenceFDANDROID", (__eglMustCastToProperFunctionPointerType)&eglDupNativeFenceFDANDROID },
};
+// clang-format on
/*
* These extensions entry-points should not be exposed to applications.
* They're used internally by the Android EGL layer.
*/
-#define FILTER_EXTENSIONS(procname) \
- (!strcmp((procname), "eglSetBlobCacheFuncsANDROID") || \
- !strcmp((procname), "eglHibernateProcessIMG") || \
- !strcmp((procname), "eglAwakenProcessIMG"))
+#define FILTER_EXTENSIONS(procname) (!strcmp((procname), "eglSetBlobCacheFuncsANDROID"))
// accesses protected by sExtensionMapMutex
static std::unordered_map<std::string, __eglMustCastToProperFunctionPointerType> sGLExtensionMap;
@@ -261,9 +221,8 @@
static int sGLExtensionSlot = 0;
static pthread_mutex_t sExtensionMapMutex = PTHREAD_MUTEX_INITIALIZER;
-static void(*findProcAddress(const char* name,
- const extension_map_t* map, size_t n))() {
- for (uint32_t i=0 ; i<n ; i++) {
+static void (*findProcAddress(const char* name, const extension_map_t* map, size_t n))() {
+ for (uint32_t i = 0; i < n; i++) {
if (!strcmp(name, map[i].name)) {
return map[i].address;
}
@@ -273,14 +232,17 @@
// ----------------------------------------------------------------------------
-extern void setGLHooksThreadSpecific(gl_hooks_t const *value);
+extern void setGLHooksThreadSpecific(gl_hooks_t const* value);
extern EGLBoolean egl_init_drivers();
-extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS];
+extern const __eglMustCastToProperFunctionPointerType
+ gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS];
extern gl_hooks_t gHooksTrace;
// ----------------------------------------------------------------------------
-static inline EGLContext getContext() { return egl_tls_t::getContext(); }
+static inline EGLContext getContext() {
+ return egl_tls_t::getContext();
+}
// ----------------------------------------------------------------------------
@@ -313,9 +275,8 @@
// Initialization
// ----------------------------------------------------------------------------
-EGLBoolean eglInitializeImpl(EGLDisplay dpy, EGLint *major, EGLint *minor)
-{
- egl_display_ptr dp = get_display(dpy);
+EGLBoolean eglInitializeImpl(EGLDisplay dpy, EGLint* major, EGLint* minor) {
+ egl_display_t* dp = get_display(dpy);
if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
EGLBoolean res = dp->initialize(major, minor);
@@ -323,13 +284,12 @@
return res;
}
-EGLBoolean eglTerminateImpl(EGLDisplay dpy)
-{
+EGLBoolean eglTerminateImpl(EGLDisplay dpy) {
// NOTE: don't unload the drivers b/c some APIs can be called
// after eglTerminate() has been called. eglTerminate() only
// terminates an EGLDisplay, not a EGL itself.
- egl_display_ptr dp = get_display(dpy);
+ egl_display_t* dp = get_display(dpy);
if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
EGLBoolean res = dp->terminate();
@@ -341,14 +301,12 @@
// configuration
// ----------------------------------------------------------------------------
-EGLBoolean eglGetConfigsImpl(EGLDisplay dpy,
- EGLConfig *configs,
- EGLint config_size, EGLint *num_config)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglGetConfigsImpl(EGLDisplay dpy, EGLConfig* configs, EGLint config_size,
+ EGLint* num_config) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- if (num_config==nullptr) {
+ if (num_config == nullptr) {
return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
}
@@ -357,96 +315,88 @@
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso) {
- res = cnx->egl.eglGetConfigs(
- dp->disp.dpy, configs, config_size, num_config);
+ res = cnx->egl.eglGetConfigs(dp->disp.dpy, configs, config_size, num_config);
}
return res;
}
-EGLBoolean eglChooseConfigImpl( EGLDisplay dpy, const EGLint *attrib_list,
- EGLConfig *configs, EGLint config_size,
- EGLint *num_config)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglChooseConfigImpl(EGLDisplay dpy, const EGLint* attrib_list, EGLConfig* configs,
+ EGLint config_size, EGLint* num_config) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- if (num_config==nullptr) {
+ if (num_config == nullptr) {
return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
}
- EGLBoolean res = EGL_FALSE;
*num_config = 0;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso) {
- if (attrib_list) {
- if (base::GetBoolProperty("debug.egl.force_msaa", false)) {
- size_t attribCount = 0;
- EGLint attrib = attrib_list[0];
+ if (!cnx->dso) return EGL_FALSE;
- // Only enable MSAA if the context is OpenGL ES 2.0 and
- // if no caveat is requested
- const EGLint *attribRendererable = nullptr;
- const EGLint *attribCaveat = nullptr;
+ if (!attrib_list || !base::GetBoolProperty("debug.egl.force_msaa", false))
+ return cnx->egl.eglChooseConfig(dp->disp.dpy, attrib_list, configs, config_size,
+ num_config);
- // Count the number of attributes and look for
- // EGL_RENDERABLE_TYPE and EGL_CONFIG_CAVEAT
- while (attrib != EGL_NONE) {
- attrib = attrib_list[attribCount];
- switch (attrib) {
- case EGL_RENDERABLE_TYPE:
- attribRendererable = &attrib_list[attribCount];
- break;
- case EGL_CONFIG_CAVEAT:
- attribCaveat = &attrib_list[attribCount];
- break;
- default:
- break;
- }
- attribCount++;
- }
+ // Force 4x MSAA
+ size_t attribCount = 0;
+ EGLint attrib = attrib_list[0];
- if (attribRendererable && attribRendererable[1] == EGL_OPENGL_ES2_BIT &&
- (!attribCaveat || attribCaveat[1] != EGL_NONE)) {
+ // Only enable MSAA if the context is OpenGL ES 2.0 and
+ // if no caveat is requested
+ const EGLint* attribRendererable = nullptr;
+ const EGLint* attribCaveat = nullptr;
- // Insert 2 extra attributes to force-enable MSAA 4x
- EGLint aaAttribs[attribCount + 4];
- aaAttribs[0] = EGL_SAMPLE_BUFFERS;
- aaAttribs[1] = 1;
- aaAttribs[2] = EGL_SAMPLES;
- aaAttribs[3] = 4;
-
- memcpy(&aaAttribs[4], attrib_list, attribCount * sizeof(EGLint));
-
- EGLint numConfigAA;
- EGLBoolean resAA = cnx->egl.eglChooseConfig(
- dp->disp.dpy, aaAttribs, configs, config_size, &numConfigAA);
-
- if (resAA == EGL_TRUE && numConfigAA > 0) {
- ALOGD("Enabling MSAA 4x");
- *num_config = numConfigAA;
- return resAA;
- }
- }
- }
+ // Count the number of attributes and look for
+ // EGL_RENDERABLE_TYPE and EGL_CONFIG_CAVEAT
+ while (attrib != EGL_NONE) {
+ attrib = attrib_list[attribCount];
+ switch (attrib) {
+ case EGL_RENDERABLE_TYPE:
+ attribRendererable = &attrib_list[attribCount];
+ break;
+ case EGL_CONFIG_CAVEAT:
+ attribCaveat = &attrib_list[attribCount];
+ break;
+ default:
+ break;
}
-
- res = cnx->egl.eglChooseConfig(
- dp->disp.dpy, attrib_list, configs, config_size, num_config);
+ attribCount++;
}
- return res;
+
+ if (attribRendererable && attribRendererable[1] == EGL_OPENGL_ES2_BIT &&
+ (!attribCaveat || attribCaveat[1] != EGL_NONE)) {
+ // Insert 2 extra attributes to force-enable MSAA 4x
+ EGLint aaAttribs[attribCount + 4];
+ aaAttribs[0] = EGL_SAMPLE_BUFFERS;
+ aaAttribs[1] = 1;
+ aaAttribs[2] = EGL_SAMPLES;
+ aaAttribs[3] = 4;
+
+ memcpy(&aaAttribs[4], attrib_list, attribCount * sizeof(EGLint));
+
+ EGLint numConfigAA;
+ EGLBoolean resAA = cnx->egl.eglChooseConfig(dp->disp.dpy, aaAttribs, configs, config_size,
+ &numConfigAA);
+
+ if (resAA == EGL_TRUE && numConfigAA > 0) {
+ ALOGD("Enabling MSAA 4x");
+ *num_config = numConfigAA;
+ return resAA;
+ }
+ }
+
+ return cnx->egl.eglChooseConfig(dp->disp.dpy, attrib_list, configs, config_size, num_config);
}
-EGLBoolean eglGetConfigAttribImpl(EGLDisplay dpy, EGLConfig config,
- EGLint attribute, EGLint *value)
-{
+EGLBoolean eglGetConfigAttribImpl(EGLDisplay dpy, EGLConfig config, EGLint attribute,
+ EGLint* value) {
egl_connection_t* cnx = nullptr;
- const egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ const egl_display_t* dp = validate_display_connection(dpy, &cnx);
if (!dp) return EGL_FALSE;
- return cnx->egl.eglGetConfigAttrib(
- dp->disp.dpy, config, attribute, value);
+ return cnx->egl.eglGetConfigAttrib(dp->disp.dpy, config, attribute, value);
}
// ----------------------------------------------------------------------------
@@ -484,7 +434,7 @@
}
// Returns a list of color spaces understood by the vendor EGL driver.
-static std::vector<EGLint> getDriverColorSpaces(egl_display_ptr dp) {
+static std::vector<EGLint> getDriverColorSpaces(egl_display_t* dp) {
std::vector<EGLint> colorSpaces;
// sRGB and linear are always supported when color space support is present.
@@ -509,7 +459,8 @@
if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_display_p3_linear")) {
colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT);
}
- if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_display_p3_passthrough")) {
+ if (findExtension(dp->disp.queryString.extensions,
+ "EGL_EXT_gl_colorspace_display_p3_passthrough")) {
colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT);
}
return colorSpaces;
@@ -519,7 +470,7 @@
// If there is no color space attribute in attrib_list, colorSpace is left
// unmodified.
template <typename AttrType>
-static EGLBoolean processAttributes(egl_display_ptr dp, ANativeWindow* window,
+static EGLBoolean processAttributes(egl_display_t* dp, ANativeWindow* window,
const AttrType* attrib_list, EGLint* colorSpace,
std::vector<AttrType>* strippedAttribList) {
for (const AttrType* attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) {
@@ -699,7 +650,7 @@
}
template <typename AttrType, typename CreateFuncType>
-EGLSurface eglCreateWindowSurfaceTmpl(egl_display_ptr dp, egl_connection_t* cnx, EGLConfig config,
+EGLSurface eglCreateWindowSurfaceTmpl(egl_display_t* dp, egl_connection_t* cnx, EGLConfig config,
ANativeWindow* window, const AttrType* attrib_list,
CreateFuncType createWindowSurfaceFunc) {
const AttrType* origAttribList = attrib_list;
@@ -768,7 +719,7 @@
EGLSurface surface = createWindowSurfaceFunc(iDpy, config, window, attrib_list);
if (surface != EGL_NO_SURFACE) {
- egl_surface_t* s = new egl_surface_t(dp.get(), config, window, surface,
+ egl_surface_t* s = new egl_surface_t(dp, config, window, surface,
getReportedColorSpace(colorSpace), cnx);
return s;
}
@@ -789,8 +740,8 @@
EGLSurface eglCreateWindowSurfaceImpl(EGLDisplay dpy, EGLConfig config, NativeWindowType window,
const EGLint* attrib_list) {
- egl_connection_t* cnx = NULL;
- egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ egl_connection_t* cnx = nullptr;
+ egl_display_t* dp = validate_display_connection(dpy, &cnx);
if (dp) {
return eglCreateWindowSurfaceTmpl<
EGLint, PFNEGLCREATEWINDOWSURFACEPROC>(dp, cnx, config, window, attrib_list,
@@ -801,8 +752,8 @@
EGLSurface eglCreatePlatformWindowSurfaceImpl(EGLDisplay dpy, EGLConfig config, void* native_window,
const EGLAttrib* attrib_list) {
- egl_connection_t* cnx = NULL;
- egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ egl_connection_t* cnx = nullptr;
+ egl_display_t* dp = validate_display_connection(dpy, &cnx);
if (dp) {
if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
if (cnx->egl.eglCreatePlatformWindowSurface) {
@@ -842,8 +793,8 @@
// belongs to the Android platform. Any such call fails and generates
// an EGL_BAD_PARAMETER error.
- egl_connection_t* cnx = NULL;
- egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ egl_connection_t* cnx = nullptr;
+ const egl_display_t* dp = validate_display_connection(dpy, &cnx);
if (dp) {
return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
}
@@ -853,7 +804,7 @@
EGLSurface eglCreatePixmapSurfaceImpl(EGLDisplay dpy, EGLConfig /*config*/,
NativePixmapType /*pixmap*/, const EGLint* /*attrib_list*/) {
egl_connection_t* cnx = nullptr;
- egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ const egl_display_t* dp = validate_display_connection(dpy, &cnx);
if (dp) {
return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
}
@@ -863,36 +814,33 @@
EGLSurface eglCreatePbufferSurfaceImpl(EGLDisplay dpy, EGLConfig config,
const EGLint* attrib_list) {
egl_connection_t* cnx = nullptr;
- egl_display_ptr dp = validate_display_connection(dpy, cnx);
- if (dp) {
- EGLDisplay iDpy = dp->disp.dpy;
- android_pixel_format format;
- getNativePixelFormat(iDpy, cnx, config, &format);
+ egl_display_t* dp = validate_display_connection(dpy, &cnx);
+ if (!dp) return EGL_NO_SURFACE;
- // Select correct colorspace based on user's attribute list
- EGLint colorSpace = EGL_UNKNOWN;
- std::vector<EGLint> strippedAttribList;
- if (!processAttributes(dp, nullptr, attrib_list, &colorSpace, &strippedAttribList)) {
- ALOGE("error invalid colorspace: %d", colorSpace);
- return EGL_NO_SURFACE;
- }
- attrib_list = strippedAttribList.data();
+ EGLDisplay iDpy = dp->disp.dpy;
+ android_pixel_format format;
+ getNativePixelFormat(iDpy, cnx, config, &format);
- EGLSurface surface = cnx->egl.eglCreatePbufferSurface(dp->disp.dpy, config, attrib_list);
- if (surface != EGL_NO_SURFACE) {
- egl_surface_t* s = new egl_surface_t(dp.get(), config, nullptr, surface,
- getReportedColorSpace(colorSpace), cnx);
- return s;
- }
+ // Select correct colorspace based on user's attribute list
+ EGLint colorSpace = EGL_UNKNOWN;
+ std::vector<EGLint> strippedAttribList;
+ if (!processAttributes(dp, nullptr, attrib_list, &colorSpace, &strippedAttribList)) {
+ ALOGE("error invalid colorspace: %d", colorSpace);
+ return EGL_NO_SURFACE;
}
- return EGL_NO_SURFACE;
+ attrib_list = strippedAttribList.data();
+
+ EGLSurface surface = cnx->egl.eglCreatePbufferSurface(iDpy, config, attrib_list);
+ if (surface == EGL_NO_SURFACE) return surface;
+
+ return new egl_surface_t(dp, config, nullptr, surface, getReportedColorSpace(colorSpace), cnx);
}
EGLBoolean eglDestroySurfaceImpl(EGLDisplay dpy, EGLSurface surface) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp.get(), surface);
+ SurfaceRef _s(dp, surface);
if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
egl_surface_t* const s = get_surface(surface);
@@ -905,10 +853,10 @@
EGLBoolean eglQuerySurfaceImpl(EGLDisplay dpy, EGLSurface surface, EGLint attribute,
EGLint* value) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp.get(), surface);
+ SurfaceRef _s(dp, surface);
if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
egl_surface_t const* const s = get_surface(surface);
@@ -923,12 +871,12 @@
}
void EGLAPI eglBeginFrameImpl(EGLDisplay dpy, EGLSurface surface) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) {
return;
}
- SurfaceRef _s(dp.get(), surface);
+ SurfaceRef _s(dp, surface);
if (!_s.get()) {
setError(EGL_BAD_SURFACE, EGL_FALSE);
}
@@ -938,14 +886,13 @@
// Contexts
// ----------------------------------------------------------------------------
-EGLContext eglCreateContextImpl(EGLDisplay dpy, EGLConfig config,
- EGLContext share_list, const EGLint *attrib_list)
-{
+EGLContext eglCreateContextImpl(EGLDisplay dpy, EGLConfig config, EGLContext share_list,
+ const EGLint* attrib_list) {
egl_connection_t* cnx = nullptr;
- const egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ const egl_display_t* dp = validate_display_connection(dpy, &cnx);
if (dp) {
if (share_list != EGL_NO_CONTEXT) {
- if (!ContextRef(dp.get(), share_list).get()) {
+ if (!ContextRef(dp, share_list).get()) {
return setError(EGL_BAD_CONTEXT, EGL_NO_CONTEXT);
}
egl_context_t* const c = get_context(share_list);
@@ -967,8 +914,8 @@
}
};
}
- EGLContext context = cnx->egl.eglCreateContext(
- dp->disp.dpy, config, share_list, attrib_list);
+ EGLContext context =
+ cnx->egl.eglCreateContext(dp->disp.dpy, config, share_list, attrib_list);
if (context != EGL_NO_CONTEXT) {
// figure out if it's a GLESv1 or GLESv2
int version = egl_connection_t::GLESv1_INDEX;
@@ -992,17 +939,14 @@
return EGL_NO_CONTEXT;
}
-EGLBoolean eglDestroyContextImpl(EGLDisplay dpy, EGLContext ctx)
-{
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp)
- return EGL_FALSE;
+EGLBoolean eglDestroyContextImpl(EGLDisplay dpy, EGLContext ctx) {
+ const egl_display_t* dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
- ContextRef _c(dp.get(), ctx);
- if (!_c.get())
- return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+ ContextRef _c(dp, ctx);
+ if (!_c.get()) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
- egl_context_t * const c = get_context(ctx);
+ egl_context_t* const c = get_context(ctx);
EGLBoolean result = c->cnx->egl.eglDestroyContext(dp->disp.dpy, c->context);
if (result == EGL_TRUE) {
_c.terminate();
@@ -1010,24 +954,21 @@
return result;
}
-EGLBoolean eglMakeCurrentImpl( EGLDisplay dpy, EGLSurface draw,
- EGLSurface read, EGLContext ctx)
-{
- egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglMakeCurrentImpl(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) {
+ egl_display_t* dp = validate_display(dpy);
if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
// If ctx is not EGL_NO_CONTEXT, read is not EGL_NO_SURFACE, or draw is not
// EGL_NO_SURFACE, then an EGL_NOT_INITIALIZED error is generated if dpy is
// a valid but uninitialized display.
- if ( (ctx != EGL_NO_CONTEXT) || (read != EGL_NO_SURFACE) ||
- (draw != EGL_NO_SURFACE) ) {
+ if ((ctx != EGL_NO_CONTEXT) || (read != EGL_NO_SURFACE) || (draw != EGL_NO_SURFACE)) {
if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
}
// get a reference to the object passed in
- ContextRef _c(dp.get(), ctx);
- SurfaceRef _d(dp.get(), draw);
- SurfaceRef _r(dp.get(), read);
+ ContextRef _c(dp, ctx);
+ SurfaceRef _d(dp, draw);
+ SurfaceRef _r(dp, read);
// validate the context (if not EGL_NO_CONTEXT)
if ((ctx != EGL_NO_CONTEXT) && !_c.get()) {
@@ -1036,17 +977,17 @@
}
// these are the underlying implementation's object
- EGLContext impl_ctx = EGL_NO_CONTEXT;
+ EGLContext impl_ctx = EGL_NO_CONTEXT;
EGLSurface impl_draw = EGL_NO_SURFACE;
EGLSurface impl_read = EGL_NO_SURFACE;
// these are our objects structs passed in
- egl_context_t * c = nullptr;
- egl_surface_t const * d = nullptr;
- egl_surface_t const * r = nullptr;
+ egl_context_t* c = nullptr;
+ egl_surface_t const* d = nullptr;
+ egl_surface_t const* r = nullptr;
// these are the current objects structs
- egl_context_t * cur_c = get_context(getContext());
+ egl_context_t* cur_c = get_context(getContext());
if (ctx != EGL_NO_CONTEXT) {
c = get_context(ctx);
@@ -1078,10 +1019,7 @@
impl_read = r->surface;
}
-
- EGLBoolean result = dp->makeCurrent(c, cur_c,
- draw, read, ctx,
- impl_draw, impl_read, impl_ctx);
+ EGLBoolean result = dp->makeCurrent(c, cur_c, draw, read, ctx, impl_draw, impl_read, impl_ctx);
if (result == EGL_TRUE) {
if (c) {
@@ -1102,81 +1040,72 @@
return result;
}
-EGLBoolean eglQueryContextImpl( EGLDisplay dpy, EGLContext ctx,
- EGLint attribute, EGLint *value)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglQueryContextImpl(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint* value) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- ContextRef _c(dp.get(), ctx);
+ ContextRef _c(dp, ctx);
if (!_c.get()) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
- egl_context_t * const c = get_context(ctx);
- return c->cnx->egl.eglQueryContext(
- dp->disp.dpy, c->context, attribute, value);
-
+ egl_context_t* const c = get_context(ctx);
+ return c->cnx->egl.eglQueryContext(dp->disp.dpy, c->context, attribute, value);
}
-EGLContext eglGetCurrentContextImpl(void)
-{
+EGLContext eglGetCurrentContextImpl(void) {
// could be called before eglInitialize(), but we wouldn't have a context
// then, and this function would correctly return EGL_NO_CONTEXT.
EGLContext ctx = getContext();
return ctx;
}
-EGLSurface eglGetCurrentSurfaceImpl(EGLint readdraw)
-{
+EGLSurface eglGetCurrentSurfaceImpl(EGLint readdraw) {
// could be called before eglInitialize(), but we wouldn't have a context
// then, and this function would correctly return EGL_NO_SURFACE.
EGLContext ctx = getContext();
if (ctx) {
- egl_context_t const * const c = get_context(ctx);
+ egl_context_t const* const c = get_context(ctx);
if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE);
switch (readdraw) {
- case EGL_READ: return c->read;
- case EGL_DRAW: return c->draw;
- default: return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
+ case EGL_READ:
+ return c->read;
+ case EGL_DRAW:
+ return c->draw;
+ default:
+ return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
}
}
return EGL_NO_SURFACE;
}
-EGLDisplay eglGetCurrentDisplayImpl(void)
-{
+EGLDisplay eglGetCurrentDisplayImpl(void) {
// could be called before eglInitialize(), but we wouldn't have a context
// then, and this function would correctly return EGL_NO_DISPLAY.
EGLContext ctx = getContext();
if (ctx) {
- egl_context_t const * const c = get_context(ctx);
+ egl_context_t const* const c = get_context(ctx);
if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE);
return c->dpy;
}
return EGL_NO_DISPLAY;
}
-EGLBoolean eglWaitGLImpl(void)
-{
+EGLBoolean eglWaitGLImpl(void) {
egl_connection_t* const cnx = &gEGLImpl;
- if (!cnx->dso)
- return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+ if (!cnx->dso) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
return cnx->egl.eglWaitGL();
}
-EGLBoolean eglWaitNativeImpl(EGLint engine)
-{
+EGLBoolean eglWaitNativeImpl(EGLint engine) {
egl_connection_t* const cnx = &gEGLImpl;
- if (!cnx->dso)
- return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+ if (!cnx->dso) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
return cnx->egl.eglWaitNative(engine);
}
-EGLint eglGetErrorImpl(void)
-{
+EGLint eglGetErrorImpl(void) {
EGLint err = EGL_SUCCESS;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso) {
@@ -1188,8 +1117,7 @@
return err;
}
-static __eglMustCastToProperFunctionPointerType findBuiltinWrapper(
- const char* procname) {
+static __eglMustCastToProperFunctionPointerType findBuiltinWrapper(const char* procname) {
const egl_connection_t* cnx = &gEGLImpl;
void* proc = nullptr;
@@ -1205,8 +1133,7 @@
return nullptr;
}
-__eglMustCastToProperFunctionPointerType eglGetProcAddressImpl(const char *procname)
-{
+__eglMustCastToProperFunctionPointerType eglGetProcAddressImpl(const char* procname) {
if (FILTER_EXTENSIONS(procname)) {
return nullptr;
}
@@ -1253,13 +1180,10 @@
// Ensure we have room to track it
const int slot = sGLExtensionSlot;
if (slot < MAX_NUMBER_OF_GL_EXTENSIONS) {
-
if (cnx->dso && cnx->egl.eglGetProcAddress) {
-
// Extensions are independent of the bound context
addr = cnx->egl.eglGetProcAddress(procname);
if (addr) {
-
// purposefully track the bottom of the stack in extensionMap
extensionMap[name] = addr;
@@ -1268,7 +1192,7 @@
// Track the top most entry point return the extension forwarder
cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] =
- cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr;
+ cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr;
addr = gExtensionForwarders[slot];
// Remember the slot for this extension
@@ -1300,7 +1224,7 @@
// Track the top most entry point and return the extension forwarder
cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[ext_slot] =
- cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[ext_slot] = addr;
+ cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[ext_slot] = addr;
addr = gExtensionForwarders[ext_slot];
}
@@ -1310,7 +1234,6 @@
class FrameCompletionThread {
public:
-
static void queueSync(EGLSyncKHR sync) {
static FrameCompletionThread thread;
@@ -1327,7 +1250,6 @@
}
private:
-
FrameCompletionThread() : mFramesQueued(0), mFramesCompleted(0) {
std::thread thread(&FrameCompletionThread::loop, this);
thread.detach();
@@ -1382,15 +1304,13 @@
std::mutex mMutex;
};
-EGLBoolean eglSwapBuffersWithDamageKHRImpl(EGLDisplay dpy, EGLSurface draw,
- EGLint *rects, EGLint n_rects)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglSwapBuffersWithDamageKHRImpl(EGLDisplay dpy, EGLSurface draw, EGLint* rects,
+ EGLint n_rects) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp.get(), draw);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ SurfaceRef _s(dp, draw);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
if (n_rects < 0 || (n_rects > 0 && rects == NULL))
return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
@@ -1406,11 +1326,11 @@
if (CC_UNLIKELY(dp->finishOnSwap)) {
uint32_t pixel;
- egl_context_t * const c = get_context( egl_tls_t::getContext() );
+ egl_context_t* const c = get_context(egl_tls_t::getContext());
if (c) {
// glReadPixels() ensures that the frame is complete
- s->cnx->hooks[c->version]->gl.glReadPixels(0,0,1,1,
- GL_RGBA,GL_UNSIGNED_BYTE,&pixel);
+ s->cnx->hooks[c->version]->gl.glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE,
+ &pixel);
}
}
@@ -1445,41 +1365,35 @@
}
if (s->cnx->egl.eglSwapBuffersWithDamageKHR) {
- return s->cnx->egl.eglSwapBuffersWithDamageKHR(dp->disp.dpy, s->surface,
- rects, n_rects);
- } else {
- return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
+ return s->cnx->egl.eglSwapBuffersWithDamageKHR(dp->disp.dpy, s->surface, rects, n_rects);
}
+
+ return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
}
-EGLBoolean eglSwapBuffersImpl(EGLDisplay dpy, EGLSurface surface)
-{
+EGLBoolean eglSwapBuffersImpl(EGLDisplay dpy, EGLSurface surface) {
return eglSwapBuffersWithDamageKHRImpl(dpy, surface, nullptr, 0);
}
-EGLBoolean eglCopyBuffersImpl( EGLDisplay dpy, EGLSurface surface,
- NativePixmapType target)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglCopyBuffersImpl(EGLDisplay dpy, EGLSurface surface, NativePixmapType target) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ SurfaceRef _s(dp, surface);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
return s->cnx->egl.eglCopyBuffers(dp->disp.dpy, s->surface, target);
}
-const char* eglQueryStringImpl(EGLDisplay dpy, EGLint name)
-{
+const char* eglQueryStringImpl(EGLDisplay dpy, EGLint name) {
if (dpy == EGL_NO_DISPLAY && name == EGL_EXTENSIONS) {
// Return list of client extensions
return gClientExtensionString;
}
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return (const char *) nullptr;
+ const egl_display_t* dp = validate_display(dpy);
+ if (!dp) return (const char*)nullptr;
switch (name) {
case EGL_VENDOR:
@@ -1493,13 +1407,12 @@
default:
break;
}
- return setError(EGL_BAD_PARAMETER, (const char *)nullptr);
+ return setError(EGL_BAD_PARAMETER, (const char*)nullptr);
}
-EGLAPI const char* eglQueryStringImplementationANDROIDImpl(EGLDisplay dpy, EGLint name)
-{
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return (const char *) nullptr;
+EGLAPI const char* eglQueryStringImplementationANDROIDImpl(EGLDisplay dpy, EGLint name) {
+ const egl_display_t* dp = validate_display(dpy);
+ if (!dp) return (const char*)nullptr;
switch (name) {
case EGL_VENDOR:
@@ -1513,24 +1426,22 @@
default:
break;
}
- return setError(EGL_BAD_PARAMETER, (const char *)nullptr);
+ return setError(EGL_BAD_PARAMETER, (const char*)nullptr);
}
// ----------------------------------------------------------------------------
// EGL 1.1
// ----------------------------------------------------------------------------
-EGLBoolean eglSurfaceAttribImpl(
- EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglSurfaceAttribImpl(EGLDisplay dpy, EGLSurface surface, EGLint attribute,
+ EGLint value) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ SurfaceRef _s(dp, surface);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- egl_surface_t * const s = get_surface(surface);
+ egl_surface_t* const s = get_surface(surface);
if (attribute == EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID) {
if (!s->getNativeWindow()) {
@@ -1553,51 +1464,41 @@
} else if (s->setCta8613Attribute(attribute, value)) {
return EGL_TRUE;
} else if (s->cnx->egl.eglSurfaceAttrib) {
- return s->cnx->egl.eglSurfaceAttrib(
- dp->disp.dpy, s->surface, attribute, value);
+ return s->cnx->egl.eglSurfaceAttrib(dp->disp.dpy, s->surface, attribute, value);
}
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
}
-EGLBoolean eglBindTexImageImpl(
- EGLDisplay dpy, EGLSurface surface, EGLint buffer)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglBindTexImageImpl(EGLDisplay dpy, EGLSurface surface, EGLint buffer) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ SurfaceRef _s(dp, surface);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
if (s->cnx->egl.eglBindTexImage) {
- return s->cnx->egl.eglBindTexImage(
- dp->disp.dpy, s->surface, buffer);
+ return s->cnx->egl.eglBindTexImage(dp->disp.dpy, s->surface, buffer);
}
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
}
-EGLBoolean eglReleaseTexImageImpl(
- EGLDisplay dpy, EGLSurface surface, EGLint buffer)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglReleaseTexImageImpl(EGLDisplay dpy, EGLSurface surface, EGLint buffer) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ SurfaceRef _s(dp, surface);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
if (s->cnx->egl.eglReleaseTexImage) {
- return s->cnx->egl.eglReleaseTexImage(
- dp->disp.dpy, s->surface, buffer);
+ return s->cnx->egl.eglReleaseTexImage(dp->disp.dpy, s->surface, buffer);
}
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
}
-EGLBoolean eglSwapIntervalImpl(EGLDisplay dpy, EGLint interval)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglSwapIntervalImpl(EGLDisplay dpy, EGLint interval) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean res = EGL_TRUE;
@@ -1609,16 +1510,13 @@
return res;
}
-
// ----------------------------------------------------------------------------
// EGL 1.2
// ----------------------------------------------------------------------------
-EGLBoolean eglWaitClientImpl(void)
-{
+EGLBoolean eglWaitClientImpl(void) {
egl_connection_t* const cnx = &gEGLImpl;
- if (!cnx->dso)
- return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+ if (!cnx->dso) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
EGLBoolean res;
if (cnx->egl.eglWaitClient) {
@@ -1629,8 +1527,7 @@
return res;
}
-EGLBoolean eglBindAPIImpl(EGLenum api)
-{
+EGLBoolean eglBindAPIImpl(EGLenum api) {
// bind this API on all EGLs
EGLBoolean res = EGL_TRUE;
egl_connection_t* const cnx = &gEGLImpl;
@@ -1640,8 +1537,7 @@
return res;
}
-EGLenum eglQueryAPIImpl(void)
-{
+EGLenum eglQueryAPIImpl(void) {
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglQueryAPI) {
return cnx->egl.eglQueryAPI();
@@ -1651,8 +1547,7 @@
return EGL_OPENGL_ES_API;
}
-EGLBoolean eglReleaseThreadImpl(void)
-{
+EGLBoolean eglReleaseThreadImpl(void) {
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglReleaseThread) {
cnx->egl.eglReleaseThread();
@@ -1665,16 +1560,15 @@
return EGL_TRUE;
}
-EGLSurface eglCreatePbufferFromClientBufferImpl(
- EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer,
- EGLConfig config, const EGLint *attrib_list)
-{
+EGLSurface eglCreatePbufferFromClientBufferImpl(EGLDisplay dpy, EGLenum buftype,
+ EGLClientBuffer buffer, EGLConfig config,
+ const EGLint* attrib_list) {
egl_connection_t* cnx = nullptr;
- const egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ const egl_display_t* dp = validate_display_connection(dpy, &cnx);
if (!dp) return EGL_FALSE;
if (cnx->egl.eglCreatePbufferFromClientBuffer) {
- return cnx->egl.eglCreatePbufferFromClientBuffer(
- dp->disp.dpy, buftype, buffer, config, attrib_list);
+ return cnx->egl.eglCreatePbufferFromClientBuffer(dp->disp.dpy, buftype, buffer, config,
+ attrib_list);
}
return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE);
}
@@ -1683,34 +1577,28 @@
// EGL_EGLEXT_VERSION 3
// ----------------------------------------------------------------------------
-EGLBoolean eglLockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface,
- const EGLint *attrib_list)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglLockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface, const EGLint* attrib_list) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ SurfaceRef _s(dp, surface);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
if (s->cnx->egl.eglLockSurfaceKHR) {
- return s->cnx->egl.eglLockSurfaceKHR(
- dp->disp.dpy, s->surface, attrib_list);
+ return s->cnx->egl.eglLockSurfaceKHR(dp->disp.dpy, s->surface, attrib_list);
}
return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
}
-EGLBoolean eglUnlockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglUnlockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ SurfaceRef _s(dp, surface);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
if (s->cnx->egl.eglUnlockSurfaceKHR) {
return s->cnx->egl.eglUnlockSurfaceKHR(dp->disp.dpy, s->surface);
}
@@ -1723,7 +1611,7 @@
EGLImageKHR eglCreateImageTmpl(EGLDisplay dpy, EGLContext ctx, EGLenum target,
EGLClientBuffer buffer, const AttrType* attrib_list,
FuncType eglCreateImageFunc) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_NO_IMAGE_KHR;
std::vector<AttrType> strippedAttribs;
@@ -1732,7 +1620,7 @@
// EGL_GL_COLORSPACE_LINEAR_KHR, EGL_GL_COLORSPACE_SRGB_KHR and
// EGL_GL_COLORSPACE_DEFAULT_EXT if EGL_EXT_image_gl_colorspace is supported,
// but some drivers don't like the DEFAULT value and generate an error.
- for (const AttrType *attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) {
+ for (const AttrType* attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) {
if (attr[0] == EGL_GL_COLORSPACE_KHR &&
dp->haveExtension("EGL_EXT_image_gl_colorspace")) {
if (attr[1] != EGL_GL_COLORSPACE_LINEAR_KHR &&
@@ -1746,14 +1634,15 @@
strippedAttribs.push_back(EGL_NONE);
}
- ContextRef _c(dp.get(), ctx);
+ ContextRef _c(dp, ctx);
egl_context_t* const c = _c.get();
EGLImageKHR result = EGL_NO_IMAGE_KHR;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && eglCreateImageFunc) {
result = eglCreateImageFunc(dp->disp.dpy, c ? c->context : EGL_NO_CONTEXT, target, buffer,
- needsAndroidPEglMitigation() ? strippedAttribs.data() : attrib_list);
+ needsAndroidPEglMitigation() ? strippedAttribs.data()
+ : attrib_list);
}
return result;
}
@@ -1792,7 +1681,7 @@
EGLBoolean eglDestroyImageTmpl(EGLDisplay dpy, EGLImageKHR img,
PFNEGLDESTROYIMAGEKHRPROC destroyImageFunc) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
@@ -1829,7 +1718,7 @@
template <typename AttrType, typename FuncType>
EGLSyncKHR eglCreateSyncTmpl(EGLDisplay dpy, EGLenum type, const AttrType* attrib_list,
FuncType eglCreateSyncFunc) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_NO_SYNC_KHR;
egl_connection_t* const cnx = &gEGLImpl;
@@ -1868,7 +1757,7 @@
EGLBoolean eglDestroySyncTmpl(EGLDisplay dpy, EGLSyncKHR sync,
PFNEGLDESTROYSYNCKHRPROC eglDestroySyncFunc) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
@@ -1897,7 +1786,7 @@
}
EGLBoolean eglSignalSyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLenum mode) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
@@ -1910,7 +1799,7 @@
EGLint eglClientWaitSyncTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout,
PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncFunc) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLint result = EGL_FALSE;
@@ -1942,7 +1831,7 @@
template <typename AttrType, typename FuncType>
EGLBoolean eglGetSyncAttribTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, AttrType* value,
FuncType eglGetSyncAttribFunc) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
@@ -1987,106 +1876,93 @@
.eglGetSyncAttribKHR);
}
-EGLStreamKHR eglCreateStreamKHRImpl(EGLDisplay dpy, const EGLint *attrib_list)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLStreamKHR eglCreateStreamKHRImpl(EGLDisplay dpy, const EGLint* attrib_list) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_NO_STREAM_KHR;
EGLStreamKHR result = EGL_NO_STREAM_KHR;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglCreateStreamKHR) {
- result = cnx->egl.eglCreateStreamKHR(
- dp->disp.dpy, attrib_list);
+ result = cnx->egl.eglCreateStreamKHR(dp->disp.dpy, attrib_list);
}
return result;
}
-EGLBoolean eglDestroyStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglDestroyStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglDestroyStreamKHR) {
- result = cnx->egl.eglDestroyStreamKHR(
- dp->disp.dpy, stream);
+ result = cnx->egl.eglDestroyStreamKHR(dp->disp.dpy, stream);
}
return result;
}
-EGLBoolean eglStreamAttribKHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
- EGLenum attribute, EGLint value)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglStreamAttribKHRImpl(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+ EGLint value) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglStreamAttribKHR) {
- result = cnx->egl.eglStreamAttribKHR(
- dp->disp.dpy, stream, attribute, value);
+ result = cnx->egl.eglStreamAttribKHR(dp->disp.dpy, stream, attribute, value);
}
return result;
}
-EGLBoolean eglQueryStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
- EGLenum attribute, EGLint *value)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglQueryStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+ EGLint* value) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglQueryStreamKHR) {
- result = cnx->egl.eglQueryStreamKHR(
- dp->disp.dpy, stream, attribute, value);
+ result = cnx->egl.eglQueryStreamKHR(dp->disp.dpy, stream, attribute, value);
}
return result;
}
-EGLBoolean eglQueryStreamu64KHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
- EGLenum attribute, EGLuint64KHR *value)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglQueryStreamu64KHRImpl(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+ EGLuint64KHR* value) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglQueryStreamu64KHR) {
- result = cnx->egl.eglQueryStreamu64KHR(
- dp->disp.dpy, stream, attribute, value);
+ result = cnx->egl.eglQueryStreamu64KHR(dp->disp.dpy, stream, attribute, value);
}
return result;
}
-EGLBoolean eglQueryStreamTimeKHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
- EGLenum attribute, EGLTimeKHR *value)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglQueryStreamTimeKHRImpl(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+ EGLTimeKHR* value) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglQueryStreamTimeKHR) {
- result = cnx->egl.eglQueryStreamTimeKHR(
- dp->disp.dpy, stream, attribute, value);
+ result = cnx->egl.eglQueryStreamTimeKHR(dp->disp.dpy, stream, attribute, value);
}
return result;
}
EGLSurface eglCreateStreamProducerSurfaceKHRImpl(EGLDisplay dpy, EGLConfig config,
- EGLStreamKHR stream, const EGLint *attrib_list)
-{
- egl_display_ptr dp = validate_display(dpy);
+ EGLStreamKHR stream, const EGLint* attrib_list) {
+ egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_NO_SURFACE;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglCreateStreamProducerSurfaceKHR) {
- EGLSurface surface = cnx->egl.eglCreateStreamProducerSurfaceKHR(
- dp->disp.dpy, config, stream, attrib_list);
+ EGLSurface surface = cnx->egl.eglCreateStreamProducerSurfaceKHR(dp->disp.dpy, config,
+ stream, attrib_list);
if (surface != EGL_NO_SURFACE) {
- egl_surface_t* s = new egl_surface_t(dp.get(), config, nullptr, surface,
+ egl_surface_t* s = new egl_surface_t(dp, config, nullptr, surface,
EGL_GL_COLORSPACE_LINEAR_KHR, cnx);
return s;
}
@@ -2094,77 +1970,63 @@
return EGL_NO_SURFACE;
}
-EGLBoolean eglStreamConsumerGLTextureExternalKHRImpl(EGLDisplay dpy,
- EGLStreamKHR stream)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglStreamConsumerGLTextureExternalKHRImpl(EGLDisplay dpy, EGLStreamKHR stream) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglStreamConsumerGLTextureExternalKHR) {
- result = cnx->egl.eglStreamConsumerGLTextureExternalKHR(
- dp->disp.dpy, stream);
+ result = cnx->egl.eglStreamConsumerGLTextureExternalKHR(dp->disp.dpy, stream);
}
return result;
}
-EGLBoolean eglStreamConsumerAcquireKHRImpl(EGLDisplay dpy,
- EGLStreamKHR stream)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglStreamConsumerAcquireKHRImpl(EGLDisplay dpy, EGLStreamKHR stream) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglStreamConsumerAcquireKHR) {
- result = cnx->egl.eglStreamConsumerAcquireKHR(
- dp->disp.dpy, stream);
+ result = cnx->egl.eglStreamConsumerAcquireKHR(dp->disp.dpy, stream);
}
return result;
}
-EGLBoolean eglStreamConsumerReleaseKHRImpl(EGLDisplay dpy,
- EGLStreamKHR stream)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglStreamConsumerReleaseKHRImpl(EGLDisplay dpy, EGLStreamKHR stream) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglStreamConsumerReleaseKHR) {
- result = cnx->egl.eglStreamConsumerReleaseKHR(
- dp->disp.dpy, stream);
+ result = cnx->egl.eglStreamConsumerReleaseKHR(dp->disp.dpy, stream);
}
return result;
}
-EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHRImpl(
- EGLDisplay dpy, EGLStreamKHR stream)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHRImpl(EGLDisplay dpy, EGLStreamKHR stream) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_NO_FILE_DESCRIPTOR_KHR;
EGLNativeFileDescriptorKHR result = EGL_NO_FILE_DESCRIPTOR_KHR;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglGetStreamFileDescriptorKHR) {
- result = cnx->egl.eglGetStreamFileDescriptorKHR(
- dp->disp.dpy, stream);
+ result = cnx->egl.eglGetStreamFileDescriptorKHR(dpy, stream);
}
return result;
}
-EGLStreamKHR eglCreateStreamFromFileDescriptorKHRImpl(
- EGLDisplay dpy, EGLNativeFileDescriptorKHR file_descriptor)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLStreamKHR eglCreateStreamFromFileDescriptorKHRImpl(EGLDisplay dpy,
+ EGLNativeFileDescriptorKHR file_descriptor) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_NO_STREAM_KHR;
EGLStreamKHR result = EGL_NO_STREAM_KHR;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglCreateStreamFromFileDescriptorKHR) {
- result = cnx->egl.eglCreateStreamFromFileDescriptorKHR(
- dp->disp.dpy, file_descriptor);
+ result = cnx->egl.eglCreateStreamFromFileDescriptorKHR(dp->disp.dpy, file_descriptor);
}
return result;
}
@@ -2177,7 +2039,7 @@
template <typename ReturnType, typename FuncType>
ReturnType eglWaitSyncTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags,
FuncType eglWaitSyncFunc) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
ReturnType result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
@@ -2214,9 +2076,8 @@
// ANDROID extensions
// ----------------------------------------------------------------------------
-EGLint eglDupNativeFenceFDANDROIDImpl(EGLDisplay dpy, EGLSyncKHR sync)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLint eglDupNativeFenceFDANDROIDImpl(EGLDisplay dpy, EGLSyncKHR sync) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_NO_NATIVE_FENCE_FD_ANDROID;
EGLint result = EGL_NO_NATIVE_FENCE_FD_ANDROID;
@@ -2228,42 +2089,33 @@
}
EGLBoolean eglPresentationTimeANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
- EGLnsecsANDROID time)
-{
- const egl_display_ptr dp = validate_display(dpy);
+ EGLnsecsANDROID time) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) {
return EGL_FALSE;
}
- SurfaceRef _s(dp.get(), surface);
+ SurfaceRef _s(dp, surface);
if (!_s.get()) {
setError(EGL_BAD_SURFACE, EGL_FALSE);
return EGL_FALSE;
}
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
native_window_set_buffers_timestamp(s->getNativeWindow(), time);
return EGL_TRUE;
}
-EGLClientBuffer eglGetNativeClientBufferANDROIDImpl(const AHardwareBuffer *buffer) {
- // AHardwareBuffer_to_ANativeWindowBuffer is a platform-only symbol and thus
- // this function cannot be implemented when this libEGL is built for
- // vendors.
-#ifndef __ANDROID_VNDK__
+EGLClientBuffer eglGetNativeClientBufferANDROIDImpl(const AHardwareBuffer* buffer) {
if (!buffer) return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr);
- return const_cast<ANativeWindowBuffer *>(AHardwareBuffer_to_ANativeWindowBuffer(buffer));
-#else
- return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr);
-#endif
+ return const_cast<ANativeWindowBuffer*>(AHardwareBuffer_to_ANativeWindowBuffer(buffer));
}
// ----------------------------------------------------------------------------
// NVIDIA extensions
// ----------------------------------------------------------------------------
-EGLuint64NV eglGetSystemTimeFrequencyNVImpl()
-{
+EGLuint64NV eglGetSystemTimeFrequencyNVImpl() {
EGLuint64NV ret = 0;
egl_connection_t* const cnx = &gEGLImpl;
@@ -2274,8 +2126,7 @@
return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0);
}
-EGLuint64NV eglGetSystemTimeNVImpl()
-{
+EGLuint64NV eglGetSystemTimeNVImpl() {
EGLuint64NV ret = 0;
egl_connection_t* const cnx = &gEGLImpl;
@@ -2289,43 +2140,40 @@
// ----------------------------------------------------------------------------
// Partial update extension
// ----------------------------------------------------------------------------
-EGLBoolean eglSetDamageRegionKHRImpl(EGLDisplay dpy, EGLSurface surface,
- EGLint *rects, EGLint n_rects)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglSetDamageRegionKHRImpl(EGLDisplay dpy, EGLSurface surface, EGLint* rects,
+ EGLint n_rects) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) {
setError(EGL_BAD_DISPLAY, EGL_FALSE);
return EGL_FALSE;
}
- SurfaceRef _s(dp.get(), surface);
+ SurfaceRef _s(dp, surface);
if (!_s.get()) {
setError(EGL_BAD_SURFACE, EGL_FALSE);
return EGL_FALSE;
}
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
if (s->cnx->egl.eglSetDamageRegionKHR) {
- return s->cnx->egl.eglSetDamageRegionKHR(dp->disp.dpy, s->surface,
- rects, n_rects);
+ return s->cnx->egl.eglSetDamageRegionKHR(dp->disp.dpy, s->surface, rects, n_rects);
}
return EGL_FALSE;
}
-EGLBoolean eglGetNextFrameIdANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
- EGLuint64KHR *frameId) {
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglGetNextFrameIdANDROIDImpl(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR* frameId) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) {
return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
}
- SurfaceRef _s(dp.get(), surface);
+ SurfaceRef _s(dp, surface);
if (!_s.get()) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
}
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
if (!s->getNativeWindow()) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
@@ -2346,19 +2194,19 @@
}
EGLBoolean eglGetCompositorTimingANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
- EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values)
-{
- const egl_display_ptr dp = validate_display(dpy);
+ EGLint numTimestamps, const EGLint* names,
+ EGLnsecsANDROID* values) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) {
return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
}
- SurfaceRef _s(dp.get(), surface);
+ SurfaceRef _s(dp, surface);
if (!_s.get()) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
}
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
if (!s->getNativeWindow()) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
@@ -2384,36 +2232,35 @@
}
}
- int ret = native_window_get_compositor_timing(s->getNativeWindow(),
- compositeDeadline, compositeInterval, compositeToPresentLatency);
+ int ret = native_window_get_compositor_timing(s->getNativeWindow(), compositeDeadline,
+ compositeInterval, compositeToPresentLatency);
switch (ret) {
- case 0:
- return EGL_TRUE;
- case -ENOSYS:
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- default:
- // This should not happen. Return an error that is not in the spec
- // so it's obvious something is very wrong.
- ALOGE("eglGetCompositorTiming: Unexpected error.");
- return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
+ case 0:
+ return EGL_TRUE;
+ case -ENOSYS:
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ default:
+ // This should not happen. Return an error that is not in the spec
+ // so it's obvious something is very wrong.
+ ALOGE("eglGetCompositorTiming: Unexpected error.");
+ return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
}
}
-EGLBoolean eglGetCompositorTimingSupportedANDROIDImpl(
- EGLDisplay dpy, EGLSurface surface, EGLint name)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglGetCompositorTimingSupportedANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
+ EGLint name) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) {
return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
}
- SurfaceRef _s(dp.get(), surface);
+ SurfaceRef _s(dp, surface);
if (!_s.get()) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
}
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
ANativeWindow* window = s->getNativeWindow();
if (!window) {
@@ -2431,20 +2278,19 @@
}
EGLBoolean eglGetFrameTimestampsANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
- EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps,
- EGLnsecsANDROID *values)
-{
- const egl_display_ptr dp = validate_display(dpy);
+ EGLuint64KHR frameId, EGLint numTimestamps,
+ const EGLint* timestamps, EGLnsecsANDROID* values) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) {
return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
}
- SurfaceRef _s(dp.get(), surface);
+ SurfaceRef _s(dp, surface);
if (!_s.get()) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
}
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
if (!s->getNativeWindow()) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
@@ -2494,10 +2340,11 @@
}
}
- int ret = native_window_get_frame_timestamps(s->getNativeWindow(), frameId,
- requestedPresentTime, acquireTime, latchTime, firstRefreshStartTime,
- lastRefreshStartTime, gpuCompositionDoneTime, displayPresentTime,
- dequeueReadyTime, releaseTime);
+ int ret =
+ native_window_get_frame_timestamps(s->getNativeWindow(), frameId, requestedPresentTime,
+ acquireTime, latchTime, firstRefreshStartTime,
+ lastRefreshStartTime, gpuCompositionDoneTime,
+ displayPresentTime, dequeueReadyTime, releaseTime);
switch (ret) {
case 0:
@@ -2516,20 +2363,19 @@
}
}
-EGLBoolean eglGetFrameTimestampSupportedANDROIDImpl(
- EGLDisplay dpy, EGLSurface surface, EGLint timestamp)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglGetFrameTimestampSupportedANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
+ EGLint timestamp) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) {
return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
}
- SurfaceRef _s(dp.get(), surface);
+ SurfaceRef _s(dp, surface);
if (!_s.get()) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
}
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
ANativeWindow* window = s->getNativeWindow();
if (!window) {
@@ -2551,8 +2397,7 @@
return EGL_TRUE;
case EGL_DISPLAY_PRESENT_TIME_ANDROID: {
int value = 0;
- window->query(window,
- NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &value);
+ window->query(window, NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &value);
return value == 0 ? EGL_FALSE : EGL_TRUE;
}
default:
@@ -2560,25 +2405,25 @@
}
}
-const GLubyte * glGetStringImpl(GLenum name) {
- const GLubyte * ret = egl_get_string_for_current_context(name);
+const GLubyte* glGetStringImpl(GLenum name) {
+ const GLubyte* ret = egl_get_string_for_current_context(name);
if (ret == NULL) {
- gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
- if(_c) ret = _c->glGetString(name);
+ gl_hooks_t::gl_t const* const _c = &getGlThreadSpecific()->gl;
+ if (_c) ret = _c->glGetString(name);
}
return ret;
}
-const GLubyte * glGetStringiImpl(GLenum name, GLuint index) {
- const GLubyte * ret = egl_get_string_for_current_context(name, index);
+const GLubyte* glGetStringiImpl(GLenum name, GLuint index) {
+ const GLubyte* ret = egl_get_string_for_current_context(name, index);
if (ret == NULL) {
- gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
- if(_c) ret = _c->glGetStringi(name, index);
+ gl_hooks_t::gl_t const* const _c = &getGlThreadSpecific()->gl;
+ if (_c) ret = _c->glGetStringi(name, index);
}
return ret;
}
-void glGetBooleanvImpl(GLenum pname, GLboolean * data) {
+void glGetBooleanvImpl(GLenum pname, GLboolean* data) {
if (pname == GL_NUM_EXTENSIONS) {
int num_exts = egl_get_num_extensions_for_current_context();
if (num_exts >= 0) {
@@ -2587,11 +2432,11 @@
}
}
- gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+ gl_hooks_t::gl_t const* const _c = &getGlThreadSpecific()->gl;
if (_c) _c->glGetBooleanv(pname, data);
}
-void glGetFloatvImpl(GLenum pname, GLfloat * data) {
+void glGetFloatvImpl(GLenum pname, GLfloat* data) {
if (pname == GL_NUM_EXTENSIONS) {
int num_exts = egl_get_num_extensions_for_current_context();
if (num_exts >= 0) {
@@ -2600,11 +2445,11 @@
}
}
- gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+ gl_hooks_t::gl_t const* const _c = &getGlThreadSpecific()->gl;
if (_c) _c->glGetFloatv(pname, data);
}
-void glGetIntegervImpl(GLenum pname, GLint * data) {
+void glGetIntegervImpl(GLenum pname, GLint* data) {
if (pname == GL_NUM_EXTENSIONS) {
int num_exts = egl_get_num_extensions_for_current_context();
if (num_exts >= 0) {
@@ -2613,11 +2458,11 @@
}
}
- gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+ gl_hooks_t::gl_t const* const _c = &getGlThreadSpecific()->gl;
if (_c) _c->glGetIntegerv(pname, data);
}
-void glGetInteger64vImpl(GLenum pname, GLint64 * data) {
+void glGetInteger64vImpl(GLenum pname, GLint64* data) {
if (pname == GL_NUM_EXTENSIONS) {
int num_exts = egl_get_num_extensions_for_current_context();
if (num_exts >= 0) {
@@ -2626,7 +2471,7 @@
}
}
- gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+ gl_hooks_t::gl_t const* const _c = &getGlThreadSpecific()->gl;
if (_c) _c->glGetInteger64v(pname, data);
}
@@ -2635,8 +2480,8 @@
EGLFuncPointer address;
};
+// clang-format off
static const implementation_map_t sPlatformImplMap[] = {
- // clang-format off
{ "eglGetDisplay", (EGLFuncPointer)&eglGetDisplayImpl },
{ "eglGetPlatformDisplay", (EGLFuncPointer)&eglGetPlatformDisplayImpl },
{ "eglInitialize", (EGLFuncPointer)&eglInitializeImpl },
@@ -2723,11 +2568,10 @@
{ "glGetFloatv", (EGLFuncPointer)&glGetFloatvImpl },
{ "glGetIntegerv", (EGLFuncPointer)&glGetIntegervImpl },
{ "glGetInteger64v", (EGLFuncPointer)&glGetInteger64vImpl },
- // clang-format on
};
+// clang-format on
-EGLFuncPointer FindPlatformImplAddr(const char* name)
-{
+EGLFuncPointer FindPlatformImplAddr(const char* name) {
static const bool DEBUG = false;
if (name == nullptr) {
@@ -2741,7 +2585,8 @@
return nullptr;
}
if (!strcmp(name, sPlatformImplMap[i].name)) {
- ALOGV("FindPlatformImplAddr found %llu for sPlatformImplMap[%i].address (%s)", (unsigned long long)sPlatformImplMap[i].address, i, name);
+ ALOGV("FindPlatformImplAddr found %llu for sPlatformImplMap[%i].address (%s)",
+ (unsigned long long)sPlatformImplMap[i].address, i, name);
return sPlatformImplMap[i].address;
}
}
diff --git a/opengl/libs/EGL/egl_tls.cpp b/opengl/libs/EGL/egl_tls.cpp
index 8d118e0..dd1dcc2 100644
--- a/opengl/libs/EGL/egl_tls.cpp
+++ b/opengl/libs/EGL/egl_tls.cpp
@@ -16,10 +16,10 @@
#include "egl_tls.h"
-#include <stdlib.h>
-
#include <android-base/properties.h>
#include <log/log.h>
+#include <stdlib.h>
+
#include "CallStack.h"
#include "egl_platform_entries.h"
@@ -28,33 +28,46 @@
pthread_key_t egl_tls_t::sKey = TLS_KEY_NOT_INITIALIZED;
pthread_once_t egl_tls_t::sOnceKey = PTHREAD_ONCE_INIT;
-egl_tls_t::egl_tls_t()
- : error(EGL_SUCCESS), ctx(nullptr), logCallWithNoContext(true) {
-}
+egl_tls_t::egl_tls_t() : error(EGL_SUCCESS), ctx(nullptr), logCallWithNoContext(true) {}
-const char *egl_tls_t::egl_strerror(EGLint err) {
+const char* egl_tls_t::egl_strerror(EGLint err) {
switch (err) {
- case EGL_SUCCESS: return "EGL_SUCCESS";
- case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED";
- case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS";
- case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC";
- case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE";
- case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG";
- case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT";
- case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE";
- case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY";
- case EGL_BAD_MATCH: return "EGL_BAD_MATCH";
- case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP";
- case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW";
- case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER";
- case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE";
- case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST";
- default: return "UNKNOWN";
+ case EGL_SUCCESS:
+ return "EGL_SUCCESS";
+ case EGL_NOT_INITIALIZED:
+ return "EGL_NOT_INITIALIZED";
+ case EGL_BAD_ACCESS:
+ return "EGL_BAD_ACCESS";
+ case EGL_BAD_ALLOC:
+ return "EGL_BAD_ALLOC";
+ case EGL_BAD_ATTRIBUTE:
+ return "EGL_BAD_ATTRIBUTE";
+ case EGL_BAD_CONFIG:
+ return "EGL_BAD_CONFIG";
+ case EGL_BAD_CONTEXT:
+ return "EGL_BAD_CONTEXT";
+ case EGL_BAD_CURRENT_SURFACE:
+ return "EGL_BAD_CURRENT_SURFACE";
+ case EGL_BAD_DISPLAY:
+ return "EGL_BAD_DISPLAY";
+ case EGL_BAD_MATCH:
+ return "EGL_BAD_MATCH";
+ case EGL_BAD_NATIVE_PIXMAP:
+ return "EGL_BAD_NATIVE_PIXMAP";
+ case EGL_BAD_NATIVE_WINDOW:
+ return "EGL_BAD_NATIVE_WINDOW";
+ case EGL_BAD_PARAMETER:
+ return "EGL_BAD_PARAMETER";
+ case EGL_BAD_SURFACE:
+ return "EGL_BAD_SURFACE";
+ case EGL_CONTEXT_LOST:
+ return "EGL_CONTEXT_LOST";
+ default:
+ return "UNKNOWN";
}
}
-void egl_tls_t::validateTLSKey()
-{
+void egl_tls_t::validateTLSKey() {
struct TlsKeyInitializer {
static void create() { pthread_key_create(&sKey, destructTLSData); }
};
@@ -88,14 +101,12 @@
"EGL TLS data still exists after eglReleaseThread");
}
-void egl_tls_t::setErrorEtcImpl(
- const char* caller, int line, EGLint error, bool quiet) {
+void egl_tls_t::setErrorEtcImpl(const char* caller, int line, EGLint error, bool quiet) {
validateTLSKey();
egl_tls_t* tls = getTLS();
if (tls->error != error) {
if (!quiet) {
- ALOGE("%s:%d error %x (%s)",
- caller, line, error, egl_strerror(error));
+ ALOGE("%s:%d error %x (%s)", caller, line, error, egl_strerror(error));
if (base::GetBoolProperty("debug.egl.callstack", false)) {
CallStack::log(LOG_TAG);
}
@@ -111,7 +122,6 @@
return true;
}
return false;
-
}
egl_tls_t* egl_tls_t::getTLS() {
@@ -161,10 +171,9 @@
if (sKey == TLS_KEY_NOT_INITIALIZED) {
return EGL_NO_CONTEXT;
}
- egl_tls_t* tls = (egl_tls_t *)pthread_getspecific(sKey);
+ egl_tls_t* tls = (egl_tls_t*)pthread_getspecific(sKey);
if (!tls) return EGL_NO_CONTEXT;
return tls->ctx;
}
-
} // namespace android
diff --git a/opengl/libs/EGL/egl_tls.h b/opengl/libs/EGL/egl_tls.h
index 86a375c..b5fcc1a 100644
--- a/opengl/libs/EGL/egl_tls.h
+++ b/opengl/libs/EGL/egl_tls.h
@@ -18,12 +18,9 @@
#define ANDROID_EGL_TLS_H
#include <EGL/egl.h>
-
#include <pthread.h>
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
class DbgContext;
@@ -32,15 +29,14 @@
static pthread_key_t sKey;
static pthread_once_t sOnceKey;
- EGLint error;
- EGLContext ctx;
- bool logCallWithNoContext;
+ EGLint error;
+ EGLContext ctx;
+ bool logCallWithNoContext;
egl_tls_t();
static void validateTLSKey();
static void destructTLSData(void* data);
- static void setErrorEtcImpl(
- const char* caller, int line, EGLint error, bool quiet);
+ static void setErrorEtcImpl(const char* caller, int line, EGLint error, bool quiet);
public:
static egl_tls_t* getTLS();
@@ -50,24 +46,20 @@
static void setContext(EGLContext ctx);
static EGLContext getContext();
static bool logNoContextCall();
- static const char *egl_strerror(EGLint err);
+ static const char* egl_strerror(EGLint err);
- template<typename T>
- static T setErrorEtc(const char* caller,
- int line, EGLint error, T returnValue, bool quiet = false) {
+ template <typename T>
+ static T setErrorEtc(const char* caller, int line, EGLint error, T returnValue,
+ bool quiet = false) {
setErrorEtcImpl(caller, line, error, quiet);
return returnValue;
}
};
-#define setError(_e, _r) \
- egl_tls_t::setErrorEtc(__FUNCTION__, __LINE__, _e, _r)
+#define setError(_e, _r) egl_tls_t::setErrorEtc(__FUNCTION__, __LINE__, _e, _r)
-#define setErrorQuiet(_e, _r) \
- egl_tls_t::setErrorEtc(__FUNCTION__, __LINE__, _e, _r, true)
+#define setErrorQuiet(_e, _r) egl_tls_t::setErrorEtc(__FUNCTION__, __LINE__, _e, _r, true)
-// ----------------------------------------------------------------------------
}; // namespace android
-// ----------------------------------------------------------------------------
#endif // ANDROID_EGL_TLS_H
diff --git a/opengl/libs/EGL/egl_trace.h b/opengl/libs/EGL/egl_trace.h
index 7664de2..ffdf676 100644
--- a/opengl/libs/EGL/egl_trace.h
+++ b/opengl/libs/EGL/egl_trace.h
@@ -18,16 +18,14 @@
#if defined(__ANDROID__)
-#include <stdint.h>
-
#include <cutils/trace.h>
+#include <stdint.h>
// See <cutils/trace.h> for more ATRACE_* macros.
// ATRACE_NAME traces from its location until the end of its enclosing scope.
-#define _PASTE(x, y) x ## y
-#define PASTE(x, y) _PASTE(x,y)
-#define ATRACE_NAME(name) android::EglScopedTrace PASTE(___tracer, __LINE__) (ATRACE_TAG, name)
+#define PASTE(x, y) x##y
+#define ATRACE_NAME(name) android::EglScopedTrace PASTE(___tracer, __LINE__)(ATRACE_TAG, name)
// ATRACE_CALL is an ATRACE_NAME that uses the current function name.
#define ATRACE_CALL() ATRACE_NAME(__FUNCTION__)
@@ -36,13 +34,9 @@
class EglScopedTrace {
public:
- inline EglScopedTrace(uint64_t tag, const char* name) : mTag(tag) {
- atrace_begin(mTag, name);
- }
+ inline EglScopedTrace(uint64_t tag, const char* name) : mTag(tag) { atrace_begin(mTag, name); }
- inline ~EglScopedTrace() {
- atrace_end(mTag);
- }
+ inline ~EglScopedTrace() { atrace_end(mTag); }
private:
uint64_t mTag;
diff --git a/opengl/libs/EGL/egldefs.h b/opengl/libs/EGL/egldefs.h
index 5fbffbd..fcc11f1 100644
--- a/opengl/libs/EGL/egldefs.h
+++ b/opengl/libs/EGL/egldefs.h
@@ -17,40 +17,32 @@
#ifndef ANDROID_EGLDEFS_H
#define ANDROID_EGLDEFS_H
+#include <log/log.h>
+
#include "../hooks.h"
#include "egl_platform_entries.h"
-#include <log/log.h>
-
#define VERSION_MAJOR 1
#define VERSION_MINOR 4
#define EGL_MAKE_VERSION(major, minor, patch) (((major) << 22) | ((minor) << 12) | (patch))
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
-// EGLDisplay are global, not attached to a given thread
+// EGLDisplay are global, not attached to a given thread
const unsigned int NUM_DISPLAYS = 1;
-// ----------------------------------------------------------------------------
+extern const char* const platform_names[];
-extern char const * const platform_names[];
-
-// clang-format off
struct egl_connection_t {
- enum {
- GLESv1_INDEX = 0,
- GLESv2_INDEX = 1
- };
+ enum { GLESv1_INDEX = 0, GLESv2_INDEX = 1 };
- inline egl_connection_t() : dso(nullptr),
- libEgl(nullptr),
- libGles1(nullptr),
- libGles2(nullptr),
- systemDriverUnloaded(false) {
-
- char const* const* entries = platform_names;
+ inline egl_connection_t()
+ : dso(nullptr),
+ libEgl(nullptr),
+ libGles1(nullptr),
+ libGles2(nullptr),
+ systemDriverUnloaded(false) {
+ const char* const* entries = platform_names;
EGLFuncPointer* curr = reinterpret_cast<EGLFuncPointer*>(&platform);
while (*entries) {
const char* name = *entries;
@@ -66,41 +58,34 @@
}
}
- void * dso;
- gl_hooks_t * hooks[2];
- EGLint major;
- EGLint minor;
- EGLint driverVersion;
- egl_t egl;
+ void* dso;
+ gl_hooks_t* hooks[2];
+ EGLint major;
+ EGLint minor;
+ EGLint driverVersion;
+ egl_t egl;
// Functions implemented or redirected by platform libraries
- platform_impl_t platform;
+ platform_impl_t platform;
- void* libEgl;
- void* libGles1;
- void* libGles2;
+ void* libEgl;
+ void* libGles1;
+ void* libGles2;
- bool systemDriverUnloaded;
- bool useAngle; // Was ANGLE successfully loaded
+ bool systemDriverUnloaded;
+ bool useAngle; // Was ANGLE successfully loaded
};
-// clang-format on
-
-// ----------------------------------------------------------------------------
extern gl_hooks_t gHooks[2];
extern gl_hooks_t gHooksNoContext;
extern pthread_key_t gGLWrapperKey;
extern "C" void gl_unimplemented();
extern "C" void gl_noop();
-
-extern char const * const gl_names[];
-extern char const * const gl_names_1[];
-extern char const * const egl_names[];
-
+extern const char* const gl_names[];
+extern const char* const gl_names_1[];
+extern const char* const egl_names[];
extern egl_connection_t gEGLImpl;
-// ----------------------------------------------------------------------------
}; // namespace android
-// ----------------------------------------------------------------------------
#endif /* ANDROID_EGLDEFS_H */
diff --git a/opengl/libs/EGL/getProcAddress.cpp b/opengl/libs/EGL/getProcAddress.cpp
index fedc789..b3d6f74 100644
--- a/opengl/libs/EGL/getProcAddress.cpp
+++ b/opengl/libs/EGL/getProcAddress.cpp
@@ -16,15 +16,12 @@
#include <ctype.h>
#include <errno.h>
-#include <stdlib.h>
-
#include <log/log.h>
+#include <stdlib.h>
#include "egldefs.h"
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
#undef API_ENTRY
#undef CALL_GL_EXTENSION_API
@@ -34,6 +31,7 @@
#undef GL_EXTENSION_LIST
#undef GET_TLS
+// clang-format off
#if defined(__arm__)
#define GET_TLS(reg) "mrc p15, 0, " #reg ", c13, c0, 3 \n"
@@ -239,13 +237,13 @@
name(248) name(249) name(250) name(251) name(252) name(253) name(254) name(255)
-GL_EXTENSION_LIST( GL_EXTENSION )
+GL_EXTENSION_LIST(GL_EXTENSION)
-#define GL_EXTENSION_ARRAY(_n) GL_EXTENSION_NAME(_n),
+#define GL_EXTENSION_ARRAY(_n) GL_EXTENSION_NAME(_n),
+// clang-format on
-extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS] = {
- GL_EXTENSION_LIST( GL_EXTENSION_ARRAY )
- };
+extern const __eglMustCastToProperFunctionPointerType
+ gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS] = {GL_EXTENSION_LIST(GL_EXTENSION_ARRAY)};
#undef GET_TLS
#undef GL_EXTENSION_LIST
@@ -255,7 +253,4 @@
#undef API_ENTRY
#undef CALL_GL_EXTENSION_API
-// ----------------------------------------------------------------------------
}; // namespace android
-// ----------------------------------------------------------------------------
-
diff --git a/opengl/tests/configdump/Android.bp b/opengl/tests/configdump/Android.bp
index ffb0c1f..1bb5983 100644
--- a/opengl/tests/configdump/Android.bp
+++ b/opengl/tests/configdump/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/opengl/tests/filter/Android.bp b/opengl/tests/filter/Android.bp
index 3b92b37..b93576f 100644
--- a/opengl/tests/filter/Android.bp
+++ b/opengl/tests/filter/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/opengl/tests/gl_basic/Android.bp b/opengl/tests/gl_basic/Android.bp
index f777401..f55cd0d 100644
--- a/opengl/tests/gl_basic/Android.bp
+++ b/opengl/tests/gl_basic/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/opengl/tests/lib/WindowSurface.cpp b/opengl/tests/lib/WindowSurface.cpp
index dfb9c92..fd4522e 100644
--- a/opengl/tests/lib/WindowSurface.cpp
+++ b/opengl/tests/lib/WindowSurface.cpp
@@ -21,7 +21,7 @@
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
-#include <ui/DisplayConfig.h>
+#include <ui/DisplayMode.h>
#include <ui/DisplayState.h>
using namespace android;
@@ -42,10 +42,10 @@
return;
}
- DisplayConfig displayConfig;
- err = SurfaceComposerClient::getActiveDisplayConfig(displayToken, &displayConfig);
+ ui::DisplayMode displayMode;
+ err = SurfaceComposerClient::getActiveDisplayMode(displayToken, &displayMode);
if (err != NO_ERROR) {
- fprintf(stderr, "ERROR: unable to get active display config\n");
+ fprintf(stderr, "ERROR: unable to get active display mode\n");
return;
}
@@ -56,7 +56,7 @@
return;
}
- const ui::Size& resolution = displayConfig.resolution;
+ const ui::Size& resolution = displayMode.resolution;
auto width = resolution.getWidth();
auto height = resolution.getHeight();
diff --git a/opengl/tests/tritex/Android.bp b/opengl/tests/tritex/Android.bp
index 759582c..87da93f 100644
--- a/opengl/tests/tritex/Android.bp
+++ b/opengl/tests/tritex/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/services/audiomanager/IAudioManager.cpp b/services/audiomanager/IAudioManager.cpp
index 6235f06..ae1bb1a 100644
--- a/services/audiomanager/IAudioManager.cpp
+++ b/services/audiomanager/IAudioManager.cpp
@@ -36,7 +36,7 @@
}
virtual audio_unique_id_t trackPlayer(player_type_t playerType, audio_usage_t usage,
- audio_content_type_t content, const sp<IBinder>& player) {
+ audio_content_type_t content, const sp<IBinder>& player, audio_session_t sessionId) {
Parcel data, reply;
data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor());
data.writeInt32(1); // non-null PlayerIdCard parcelable
@@ -54,6 +54,8 @@
data.writeInt32(-1977 /*ATTR_PARCEL_IS_NULL_BUNDLE*/); // no bundle
// write IPlayer
data.writeStrongBinder(player);
+ // write session Id
+ data.writeInt32((int32_t)sessionId);
// get new PIId in reply
const status_t res = remote()->transact(TRACK_PLAYER, data, &reply, 0);
if (res != OK || reply.readExceptionCode() != 0) {
@@ -84,11 +86,13 @@
return remote()->transact(PLAYER_ATTRIBUTES, data, &reply, IBinder::FLAG_ONEWAY);
}
- virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event) {
+ virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event,
+ audio_port_handle_t deviceId) {
Parcel data, reply;
data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor());
data.writeInt32((int32_t) piid);
data.writeInt32((int32_t) event);
+ data.writeInt32((int32_t) deviceId);
return remote()->transact(PLAYER_EVENT, data, &reply, IBinder::FLAG_ONEWAY);
}
@@ -129,6 +133,14 @@
data.writeInt32((int32_t) riid);
return remote()->transact(RELEASE_RECORDER, data, &reply, IBinder::FLAG_ONEWAY);
}
+
+ virtual status_t playerSessionId(audio_unique_id_t piid, audio_session_t sessionId) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor());
+ data.writeInt32((int32_t) piid);
+ data.writeInt32((int32_t) sessionId);
+ return remote()->transact(PLAYER_SESSION_ID, data, &reply, IBinder::FLAG_ONEWAY);
+ }
};
IMPLEMENT_META_INTERFACE(AudioManager, "android.media.IAudioService");
diff --git a/services/automotive/display/AutomotiveDisplayProxyService.cpp b/services/automotive/display/AutomotiveDisplayProxyService.cpp
index 4767406..d6fc695 100644
--- a/services/automotive/display/AutomotiveDisplayProxyService.cpp
+++ b/services/automotive/display/AutomotiveDisplayProxyService.cpp
@@ -34,17 +34,17 @@
sp<IBinder> displayToken = nullptr;
sp<SurfaceControl> surfaceControl = nullptr;
if (it == mDisplays.end()) {
- displayToken = SurfaceComposerClient::getPhysicalDisplayToken(id);
+ displayToken = SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(id));
if (displayToken == nullptr) {
ALOGE("Given display id, 0x%lX, is invalid.", (unsigned long)id);
return nullptr;
}
// Get the resolution from stored display state.
- DisplayConfig displayConfig = {};
- auto err = SurfaceComposerClient::getActiveDisplayConfig(displayToken, &displayConfig);
+ ui::DisplayMode displayMode = {};
+ auto err = SurfaceComposerClient::getActiveDisplayMode(displayToken, &displayMode);
if (err != NO_ERROR) {
- ALOGE("Failed to get display configuration of %lX. "
+ ALOGE("Failed to get display mode of %lX. "
"This display will be ignored.", (unsigned long)id);
return nullptr;
}
@@ -57,8 +57,8 @@
return nullptr;
}
- auto displayWidth = displayConfig.resolution.getWidth();
- auto displayHeight = displayConfig.resolution.getHeight();
+ auto displayWidth = displayMode.resolution.getWidth();
+ auto displayHeight = displayMode.resolution.getHeight();
if ((displayState.orientation != ui::ROTATION_0) &&
(displayState.orientation != ui::ROTATION_180)) {
std::swap(displayWidth, displayHeight);
@@ -145,7 +145,7 @@
auto displayIds = SurfaceComposerClient::getPhysicalDisplayIds();
ids.resize(displayIds.size());
for (auto i = 0; i < displayIds.size(); ++i) {
- ids[i] = displayIds[i];
+ ids[i] = displayIds[i].value;
}
_cb(ids);
@@ -157,14 +157,14 @@
HwDisplayConfig activeConfig;
HwDisplayState activeState;
- auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(id);
+ auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(id));
if (displayToken == nullptr) {
ALOGE("Given display id, 0x%lX, is invalid.", (unsigned long)id);
} else {
- DisplayConfig displayConfig = {};
- auto err = SurfaceComposerClient::getActiveDisplayConfig(displayToken, &displayConfig);
+ ui::DisplayMode displayMode = {};
+ auto err = SurfaceComposerClient::getActiveDisplayMode(displayToken, &displayMode);
if (err != NO_ERROR) {
- ALOGW("Failed to get display configuration of %lX. "
+ ALOGW("Failed to get display mode of %lX. "
"This display will be ignored.", (unsigned long)id);
}
@@ -175,7 +175,7 @@
"This display will be ignored.", (unsigned long)id);
}
- activeConfig.setToExternal((uint8_t*)&displayConfig, sizeof(DisplayConfig));
+ activeConfig.setToExternal((uint8_t*)&displayMode, sizeof(ui::DisplayMode));
activeState.setToExternal((uint8_t*)&displayState, sizeof(DisplayState));
}
diff --git a/services/automotive/display/include/AutomotiveDisplayProxyService.h b/services/automotive/display/include/AutomotiveDisplayProxyService.h
index e2fc0d2..4482b9b 100644
--- a/services/automotive/display/include/AutomotiveDisplayProxyService.h
+++ b/services/automotive/display/include/AutomotiveDisplayProxyService.h
@@ -20,7 +20,7 @@
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
-#include <ui/DisplayConfig.h>
+#include <ui/DisplayMode.h>
#include <ui/DisplayState.h>
#include <tuple>
#include <vector>
diff --git a/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp
index 231d068..220952d 100644
--- a/services/gpuservice/gpustats/GpuStats.cpp
+++ b/services/gpuservice/gpustats/GpuStats.cpp
@@ -291,24 +291,27 @@
if (data) {
for (const auto& ele : mAppStats) {
- AStatsEvent* event = AStatsEventList_addStatsEvent(data);
- AStatsEvent_setAtomId(event, android::util::GPU_STATS_APP_INFO);
- AStatsEvent_writeString(event, ele.second.appPackageName.c_str());
- AStatsEvent_writeInt64(event, ele.second.driverVersionCode);
+ std::string glDriverBytes = int64VectorToProtoByteString(
+ ele.second.glDriverLoadingTime);
+ std::string vkDriverBytes = int64VectorToProtoByteString(
+ ele.second.vkDriverLoadingTime);
+ std::string angleDriverBytes = int64VectorToProtoByteString(
+ ele.second.angleDriverLoadingTime);
- std::string bytes = int64VectorToProtoByteString(ele.second.glDriverLoadingTime);
- AStatsEvent_writeByteArray(event, (const uint8_t*)bytes.c_str(), bytes.length());
-
- bytes = int64VectorToProtoByteString(ele.second.vkDriverLoadingTime);
- AStatsEvent_writeByteArray(event, (const uint8_t*)bytes.c_str(), bytes.length());
-
- bytes = int64VectorToProtoByteString(ele.second.angleDriverLoadingTime);
- AStatsEvent_writeByteArray(event, (const uint8_t*)bytes.c_str(), bytes.length());
-
- AStatsEvent_writeBool(event, ele.second.cpuVulkanInUse);
- AStatsEvent_writeBool(event, ele.second.falsePrerotation);
- AStatsEvent_writeBool(event, ele.second.gles1InUse);
- AStatsEvent_build(event);
+ android::util::addAStatsEvent(
+ data,
+ android::util::GPU_STATS_APP_INFO,
+ ele.second.appPackageName.c_str(),
+ ele.second.driverVersionCode,
+ android::util::BytesField(glDriverBytes.c_str(),
+ glDriverBytes.length()),
+ android::util::BytesField(vkDriverBytes.c_str(),
+ vkDriverBytes.length()),
+ android::util::BytesField(angleDriverBytes.c_str(),
+ angleDriverBytes.length()),
+ ele.second.cpuVulkanInUse,
+ ele.second.falsePrerotation,
+ ele.second.gles1InUse);
}
}
@@ -326,22 +329,22 @@
if (data) {
for (const auto& ele : mGlobalStats) {
- AStatsEvent* event = AStatsEventList_addStatsEvent(data);
- AStatsEvent_setAtomId(event, android::util::GPU_STATS_GLOBAL_INFO);
- AStatsEvent_writeString(event, ele.second.driverPackageName.c_str());
- AStatsEvent_writeString(event, ele.second.driverVersionName.c_str());
- AStatsEvent_writeInt64(event, ele.second.driverVersionCode);
- AStatsEvent_writeInt64(event, ele.second.driverBuildTime);
- AStatsEvent_writeInt64(event, ele.second.glLoadingCount);
- AStatsEvent_writeInt64(event, ele.second.glLoadingFailureCount);
- AStatsEvent_writeInt64(event, ele.second.vkLoadingCount);
- AStatsEvent_writeInt64(event, ele.second.vkLoadingFailureCount);
- AStatsEvent_writeInt32(event, ele.second.vulkanVersion);
- AStatsEvent_writeInt32(event, ele.second.cpuVulkanVersion);
- AStatsEvent_writeInt32(event, ele.second.glesVersion);
- AStatsEvent_writeInt64(event, ele.second.angleLoadingCount);
- AStatsEvent_writeInt64(event, ele.second.angleLoadingFailureCount);
- AStatsEvent_build(event);
+ android::util::addAStatsEvent(
+ data,
+ android::util::GPU_STATS_GLOBAL_INFO,
+ ele.second.driverPackageName.c_str(),
+ ele.second.driverVersionName.c_str(),
+ ele.second.driverVersionCode,
+ ele.second.driverBuildTime,
+ ele.second.glLoadingCount,
+ ele.second.glLoadingFailureCount,
+ ele.second.vkLoadingCount,
+ ele.second.vkLoadingFailureCount,
+ ele.second.vulkanVersion,
+ ele.second.cpuVulkanVersion,
+ ele.second.glesVersion,
+ ele.second.angleLoadingCount,
+ ele.second.angleLoadingFailureCount);
}
}
diff --git a/services/gpuservice/tracing/GpuMemTracer.cpp b/services/gpuservice/tracing/GpuMemTracer.cpp
index 44a30ea..6975151 100644
--- a/services/gpuservice/tracing/GpuMemTracer.cpp
+++ b/services/gpuservice/tracing/GpuMemTracer.cpp
@@ -110,6 +110,7 @@
mGpuMem->traverseGpuMemTotals([](int64_t ts, uint32_t gpuId, uint32_t pid, uint64_t size) {
GpuMemDataSource::Trace([&](GpuMemDataSource::TraceContext ctx) {
auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
packet->set_timestamp(ts);
auto* event = packet->set_gpu_mem_total_event();
event->set_gpu_id(gpuId);
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 8610737..9b98a17 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -30,7 +30,13 @@
"-Werror",
"-Wno-unused-parameter",
"-Wthread-safety",
+ "-Wshadow",
+ "-Wshadow-field-in-constructor-modified",
+ "-Wshadow-uncaptured-local",
],
+ sanitize: {
+ misc_undefined: ["bounds"],
+ },
}
/////////////////////////////////////////////////
@@ -61,6 +67,10 @@
"libstatslog",
"libutils",
"libui",
+ "lib-platform-compat-native-api",
+ ],
+ static_libs: [
+ "libattestation",
],
}
@@ -108,6 +118,7 @@
"InputListener.cpp",
"InputReaderBase.cpp",
"InputThread.cpp",
+ "VibrationElement.cpp"
],
}
@@ -119,6 +130,7 @@
"libcutils",
"libinput",
"liblog",
+ "libui",
"libutils",
],
header_libs: [
diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp
index 77a0716..a9cbd5a 100644
--- a/services/inputflinger/InputClassifier.cpp
+++ b/services/inputflinger/InputClassifier.cpp
@@ -391,6 +391,16 @@
mListener->notifyMotion(&newArgs);
}
+void InputClassifier::notifySensor(const NotifySensorArgs* args) {
+ // pass through
+ mListener->notifySensor(args);
+}
+
+void InputClassifier::notifyVibratorState(const NotifyVibratorStateArgs* args) {
+ // pass through
+ mListener->notifyVibratorState(args);
+}
+
void InputClassifier::notifySwitch(const NotifySwitchArgs* args) {
// pass through
mListener->notifySwitch(args);
@@ -405,6 +415,11 @@
mListener->notifyDeviceReset(args);
}
+void InputClassifier::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
+ // pass through
+ mListener->notifyPointerCaptureChanged(args);
+}
+
void InputClassifier::setMotionClassifier(
std::unique_ptr<MotionClassifierInterface> motionClassifier) {
std::scoped_lock lock(mLock);
diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h
index 03510a6..1eef020 100644
--- a/services/inputflinger/InputClassifier.h
+++ b/services/inputflinger/InputClassifier.h
@@ -229,7 +229,10 @@
virtual void notifyKey(const NotifyKeyArgs* args) override;
virtual void notifyMotion(const NotifyMotionArgs* args) override;
virtual void notifySwitch(const NotifySwitchArgs* args) override;
+ virtual void notifySensor(const NotifySensorArgs* args) override;
+ virtual void notifyVibratorState(const NotifyVibratorStateArgs* args) override;
virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
+ void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
virtual void dump(std::string& dump) override;
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index 84838ec..33b3e1e 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -48,12 +48,12 @@
listener->notifyConfigurationChanged(this);
}
-
// --- NotifyKeyArgs ---
-NotifyKeyArgs::NotifyKeyArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
- int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags,
- int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime)
+NotifyKeyArgs::NotifyKeyArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId,
+ uint32_t source, int32_t displayId, uint32_t policyFlags,
+ int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
+ int32_t metaState, nsecs_t downTime)
: NotifyArgs(id, eventTime),
deviceId(deviceId),
source(source),
@@ -64,7 +64,8 @@
keyCode(keyCode),
scanCode(scanCode),
metaState(metaState),
- downTime(downTime) {}
+ downTime(downTime),
+ readTime(readTime) {}
NotifyKeyArgs::NotifyKeyArgs(const NotifyKeyArgs& other)
: NotifyArgs(other.id, other.eventTime),
@@ -77,32 +78,31 @@
keyCode(other.keyCode),
scanCode(other.scanCode),
metaState(other.metaState),
- downTime(other.downTime) {}
+ downTime(other.downTime),
+ readTime(other.readTime) {}
bool NotifyKeyArgs::operator==(const NotifyKeyArgs& rhs) const {
- return id == rhs.id && eventTime == rhs.eventTime && deviceId == rhs.deviceId &&
- source == rhs.source && displayId == rhs.displayId && policyFlags == rhs.policyFlags &&
- action == rhs.action && flags == rhs.flags && keyCode == rhs.keyCode &&
- scanCode == rhs.scanCode && metaState == rhs.metaState && downTime == rhs.downTime;
+ return id == rhs.id && eventTime == rhs.eventTime && readTime == rhs.readTime &&
+ deviceId == rhs.deviceId && source == rhs.source && displayId == rhs.displayId &&
+ policyFlags == rhs.policyFlags && action == rhs.action && flags == rhs.flags &&
+ keyCode == rhs.keyCode && scanCode == rhs.scanCode && metaState == rhs.metaState &&
+ downTime == rhs.downTime;
}
void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {
listener->notifyKey(this);
}
-
// --- NotifyMotionArgs ---
-NotifyMotionArgs::NotifyMotionArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
- int32_t displayId, uint32_t policyFlags, int32_t action,
- int32_t actionButton, int32_t flags, int32_t metaState,
- int32_t buttonState, MotionClassification classification,
- int32_t edgeFlags, uint32_t pointerCount,
- const PointerProperties* pointerProperties,
- const PointerCoords* pointerCoords, float xPrecision,
- float yPrecision, float xCursorPosition, float yCursorPosition,
- nsecs_t downTime,
- const std::vector<TouchVideoFrame>& videoFrames)
+NotifyMotionArgs::NotifyMotionArgs(
+ int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId, uint32_t source,
+ int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton,
+ int32_t flags, int32_t metaState, int32_t buttonState, MotionClassification classification,
+ int32_t edgeFlags, uint32_t pointerCount, const PointerProperties* pointerProperties,
+ const PointerCoords* pointerCoords, float xPrecision, float yPrecision,
+ float xCursorPosition, float yCursorPosition, nsecs_t downTime,
+ const std::vector<TouchVideoFrame>& videoFrames)
: NotifyArgs(id, eventTime),
deviceId(deviceId),
source(source),
@@ -121,6 +121,7 @@
xCursorPosition(xCursorPosition),
yCursorPosition(yCursorPosition),
downTime(downTime),
+ readTime(readTime),
videoFrames(videoFrames) {
for (uint32_t i = 0; i < pointerCount; i++) {
this->pointerProperties[i].copyFrom(pointerProperties[i]);
@@ -147,6 +148,7 @@
xCursorPosition(other.xCursorPosition),
yCursorPosition(other.yCursorPosition),
downTime(other.downTime),
+ readTime(other.readTime),
videoFrames(other.videoFrames) {
for (uint32_t i = 0; i < pointerCount; i++) {
pointerProperties[i].copyFrom(other.pointerProperties[i]);
@@ -159,11 +161,12 @@
}
bool NotifyMotionArgs::operator==(const NotifyMotionArgs& rhs) const {
- bool equal = id == rhs.id && eventTime == rhs.eventTime && deviceId == rhs.deviceId &&
- source == rhs.source && displayId == rhs.displayId && policyFlags == rhs.policyFlags &&
- action == rhs.action && actionButton == rhs.actionButton && flags == rhs.flags &&
- metaState == rhs.metaState && buttonState == rhs.buttonState &&
- classification == rhs.classification && edgeFlags == rhs.edgeFlags &&
+ bool equal = id == rhs.id && eventTime == rhs.eventTime && readTime == rhs.readTime &&
+ deviceId == rhs.deviceId && source == rhs.source && displayId == rhs.displayId &&
+ policyFlags == rhs.policyFlags && action == rhs.action &&
+ actionButton == rhs.actionButton && flags == rhs.flags && metaState == rhs.metaState &&
+ buttonState == rhs.buttonState && classification == rhs.classification &&
+ edgeFlags == rhs.edgeFlags &&
pointerCount == rhs.pointerCount
// PointerProperties and PointerCoords are compared separately below
&& xPrecision == rhs.xPrecision && yPrecision == rhs.yPrecision &&
@@ -189,7 +192,6 @@
listener->notifyMotion(this);
}
-
// --- NotifySwitchArgs ---
NotifySwitchArgs::NotifySwitchArgs(int32_t id, nsecs_t eventTime, uint32_t policyFlags,
@@ -214,6 +216,58 @@
listener->notifySwitch(this);
}
+// --- NotifySensorArgs ---
+
+NotifySensorArgs::NotifySensorArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
+ InputDeviceSensorType sensorType,
+ InputDeviceSensorAccuracy accuracy, bool accuracyChanged,
+ nsecs_t hwTimestamp, std::vector<float> values)
+ : NotifyArgs(id, eventTime),
+ deviceId(deviceId),
+ source(source),
+ sensorType(sensorType),
+ accuracy(accuracy),
+ accuracyChanged(accuracyChanged),
+ hwTimestamp(hwTimestamp),
+ values(std::move(values)) {}
+
+NotifySensorArgs::NotifySensorArgs(const NotifySensorArgs& other)
+ : NotifyArgs(other.id, other.eventTime),
+ deviceId(other.deviceId),
+ source(other.source),
+ sensorType(other.sensorType),
+ accuracy(other.accuracy),
+ accuracyChanged(other.accuracyChanged),
+ hwTimestamp(other.hwTimestamp),
+ values(other.values) {}
+
+bool NotifySensorArgs::operator==(const NotifySensorArgs rhs) const {
+ return id == rhs.id && eventTime == rhs.eventTime && sensorType == rhs.sensorType &&
+ accuracy == rhs.accuracy && accuracyChanged == rhs.accuracyChanged &&
+ hwTimestamp == rhs.hwTimestamp && values == rhs.values;
+}
+
+void NotifySensorArgs::notify(const sp<InputListenerInterface>& listener) const {
+ listener->notifySensor(this);
+}
+
+// --- NotifyVibratorStateArgs ---
+
+NotifyVibratorStateArgs::NotifyVibratorStateArgs(int32_t id, nsecs_t eventTime, int32_t deviceId,
+ bool isOn)
+ : NotifyArgs(id, eventTime), deviceId(deviceId), isOn(isOn) {}
+
+NotifyVibratorStateArgs::NotifyVibratorStateArgs(const NotifyVibratorStateArgs& other)
+ : NotifyArgs(other.id, other.eventTime), deviceId(other.deviceId), isOn(other.isOn) {}
+
+bool NotifyVibratorStateArgs::operator==(const NotifyVibratorStateArgs rhs) const {
+ return id == rhs.id && eventTime == rhs.eventTime && deviceId == rhs.deviceId &&
+ isOn == rhs.isOn;
+}
+
+void NotifyVibratorStateArgs::notify(const sp<InputListenerInterface>& listener) const {
+ listener->notifyVibratorState(this);
+}
// --- NotifyDeviceResetArgs ---
@@ -231,6 +285,23 @@
listener->notifyDeviceReset(this);
}
+// --- NotifyPointerCaptureChangedArgs ---
+
+NotifyPointerCaptureChangedArgs::NotifyPointerCaptureChangedArgs(int32_t id, nsecs_t eventTime,
+ bool enabled)
+ : NotifyArgs(id, eventTime), enabled(enabled) {}
+
+NotifyPointerCaptureChangedArgs::NotifyPointerCaptureChangedArgs(
+ const NotifyPointerCaptureChangedArgs& other)
+ : NotifyArgs(other.id, other.eventTime), enabled(other.enabled) {}
+
+bool NotifyPointerCaptureChangedArgs::operator==(const NotifyPointerCaptureChangedArgs& rhs) const {
+ return id == rhs.id && eventTime == rhs.eventTime && enabled == rhs.enabled;
+}
+
+void NotifyPointerCaptureChangedArgs::notify(const sp<InputListenerInterface>& listener) const {
+ listener->notifyPointerCaptureChanged(this);
+}
// --- QueuedInputListener ---
@@ -273,11 +344,26 @@
mArgsQueue.push_back(new NotifySwitchArgs(*args));
}
+void QueuedInputListener::notifySensor(const NotifySensorArgs* args) {
+ traceEvent(__func__, args->id);
+ mArgsQueue.push_back(new NotifySensorArgs(*args));
+}
+
+void QueuedInputListener::notifyVibratorState(const NotifyVibratorStateArgs* args) {
+ traceEvent(__func__, args->id);
+ mArgsQueue.push_back(new NotifyVibratorStateArgs(*args));
+}
+
void QueuedInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
traceEvent(__func__, args->id);
mArgsQueue.push_back(new NotifyDeviceResetArgs(*args));
}
+void QueuedInputListener::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
+ traceEvent(__func__, args->id);
+ mArgsQueue.push_back(new NotifyPointerCaptureChangedArgs(*args));
+}
+
void QueuedInputListener::flush() {
size_t count = mArgsQueue.size();
for (size_t i = 0; i < count; i++) {
@@ -288,5 +374,4 @@
mArgsQueue.clear();
}
-
} // namespace android
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index e68946d..a50e5c7 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -31,6 +31,25 @@
namespace android {
+static int32_t exceptionCodeFromStatusT(status_t status) {
+ switch (status) {
+ case OK:
+ return binder::Status::EX_NONE;
+ case INVALID_OPERATION:
+ return binder::Status::EX_UNSUPPORTED_OPERATION;
+ case BAD_VALUE:
+ case BAD_TYPE:
+ case NAME_NOT_FOUND:
+ return binder::Status::EX_ILLEGAL_ARGUMENT;
+ case NO_INIT:
+ return binder::Status::EX_ILLEGAL_STATE;
+ case PERMISSION_DENIED:
+ return binder::Status::EX_SECURITY;
+ default:
+ return binder::Status::EX_TRANSACTION_FAILED;
+ }
+}
+
InputManager::InputManager(
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
@@ -93,16 +112,15 @@
class BinderWindowHandle : public InputWindowHandle {
public:
- BinderWindowHandle(const InputWindowInfo& info) {
- mInfo = info;
- }
+ BinderWindowHandle(const InputWindowInfo& info) { mInfo = info; }
bool updateInfo() override {
return true;
}
};
-void InputManager::setInputWindows(const std::vector<InputWindowInfo>& infos,
+binder::Status InputManager::setInputWindows(
+ const std::vector<InputWindowInfo>& infos,
const sp<ISetInputWindowsListener>& setInputWindowsListener) {
std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> handlesPerDisplay;
@@ -116,26 +134,45 @@
if (setInputWindowsListener) {
setInputWindowsListener->onSetInputWindowsFinished();
}
+ return binder::Status::ok();
}
// Used by tests only.
-void InputManager::registerInputChannel(const sp<InputChannel>& channel) {
+binder::Status InputManager::createInputChannel(const std::string& name, InputChannel* outChannel) {
IPCThreadState* ipc = IPCThreadState::self();
const int uid = ipc->getCallingUid();
if (uid != AID_SHELL && uid != AID_ROOT) {
ALOGE("Invalid attempt to register input channel over IPC"
"from non shell/root entity (PID: %d)", ipc->getCallingPid());
- return;
+ return binder::Status::ok();
}
- mDispatcher->registerInputChannel(channel);
+
+ base::Result<std::unique_ptr<InputChannel>> channel = mDispatcher->createInputChannel(name);
+ if (!channel.ok()) {
+ return binder::Status::fromExceptionCode(exceptionCodeFromStatusT(channel.error().code()),
+ channel.error().message().c_str());
+ }
+ (*channel)->copyTo(*outChannel);
+ return binder::Status::ok();
}
-void InputManager::unregisterInputChannel(const sp<InputChannel>& channel) {
- mDispatcher->unregisterInputChannel(channel);
+binder::Status InputManager::removeInputChannel(const sp<IBinder>& connectionToken) {
+ mDispatcher->removeInputChannel(connectionToken);
+ return binder::Status::ok();
}
-void InputManager::setMotionClassifierEnabled(bool enabled) {
- mClassifier->setMotionClassifierEnabled(enabled);
+status_t InputManager::dump(int fd, const Vector<String16>& args) {
+ std::string dump;
+
+ dump += " InputFlinger dump\n";
+
+ ::write(fd, dump.c_str(), dump.size());
+ return NO_ERROR;
+}
+
+binder::Status InputManager::setFocusedWindow(const FocusRequest& request) {
+ mDispatcher->setFocusedWindow(request);
+ return binder::Status::ok();
}
} // namespace android
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 0158441..49bea13 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -26,15 +26,19 @@
#include <InputDispatcherInterface.h>
#include <InputDispatcherPolicyInterface.h>
-#include <input/ISetInputWindowsListener.h>
+#include <android/os/ISetInputWindowsListener.h>
#include <input/Input.h>
#include <input/InputTransport.h>
-#include <input/IInputFlinger.h>
+#include <android/os/BnInputFlinger.h>
+#include <android/os/IInputFlinger.h>
#include <utils/Errors.h>
-#include <utils/Vector.h>
-#include <utils/Timers.h>
#include <utils/RefBase.h>
+#include <utils/Timers.h>
+#include <utils/Vector.h>
+
+using android::os::BnInputFlinger;
+using android::os::ISetInputWindowsListener;
namespace android {
class InputChannel;
@@ -43,17 +47,19 @@
/*
* The input manager is the core of the system event processing.
*
- * The input manager has two components.
+ * The input manager has three components.
*
* 1. The InputReader class starts a thread that reads and preprocesses raw input events, applies
- * policy, and posts messages to a queue managed by the InputDispatcherThread.
- * 2. The InputDispatcher class starts a thread that waits for new events on the
- * queue and asynchronously dispatches them to applications.
+ * policy, and posts messages to a queue managed by the InputClassifier.
+ * 2. The InputClassifier class starts a thread to communicate with the device-specific
+ * classifiers. It then waits on the queue of events from InputReader, applies a classification
+ * to them, and queues them for the InputDispatcher.
+ * 3. The InputDispatcher class starts a thread that waits for new events on the
+ * previous queue and asynchronously dispatches them to applications.
*
- * By design, the InputReader class and InputDispatcher class do not share any
- * internal state. Moreover, all communication is done one way from the InputReader
- * into the InputDispatcherThread and never the reverse. Both classes may interact with the
- * InputDispatchPolicy, however.
+ * By design, none of these classes share any internal state. Moreover, all communication is
+ * done one way from the InputReader to the InputDispatcher and never the reverse. All
+ * classes may interact with the InputDispatchPolicy, however.
*
* The InputManager class never makes any calls into Java itself. Instead, the
* InputDispatchPolicy is responsible for performing all external interactions with the
@@ -74,33 +80,37 @@
/* Gets the input reader. */
virtual sp<InputReaderInterface> getReader() = 0;
+ /* Gets the input classifier */
+ virtual sp<InputClassifierInterface> getClassifier() = 0;
+
/* Gets the input dispatcher. */
virtual sp<InputDispatcherInterface> getDispatcher() = 0;
};
class InputManager : public InputManagerInterface, public BnInputFlinger {
protected:
- virtual ~InputManager();
+ ~InputManager() override;
public:
InputManager(
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy);
- virtual status_t start();
- virtual status_t stop();
+ status_t start() override;
+ status_t stop() override;
- virtual sp<InputReaderInterface> getReader();
- virtual sp<InputClassifierInterface> getClassifier();
- virtual sp<InputDispatcherInterface> getDispatcher();
+ sp<InputReaderInterface> getReader() override;
+ sp<InputClassifierInterface> getClassifier() override;
+ sp<InputDispatcherInterface> getDispatcher() override;
- virtual void setInputWindows(const std::vector<InputWindowInfo>& handles,
- const sp<ISetInputWindowsListener>& setInputWindowsListener);
+ status_t dump(int fd, const Vector<String16>& args) override;
+ binder::Status setInputWindows(
+ const std::vector<InputWindowInfo>& handles,
+ const sp<ISetInputWindowsListener>& setInputWindowsListener) override;
- virtual void registerInputChannel(const sp<InputChannel>& channel);
- virtual void unregisterInputChannel(const sp<InputChannel>& channel);
-
- void setMotionClassifierEnabled(bool enabled);
+ binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override;
+ binder::Status removeInputChannel(const sp<IBinder>& connectionToken) override;
+ binder::Status setFocusedWindow(const FocusRequest&) override;
private:
sp<InputReaderInterface> mReader;
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
index b2dadf8..9cc777d 100644
--- a/services/inputflinger/InputReaderBase.cpp
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -19,6 +19,9 @@
//#define LOG_NDEBUG 0
#include "InputReaderBase.h"
+#include "input/DisplayViewport.h"
+#include "input/Input.h"
+#include "input/NamedEnum.h"
#include <android/log.h>
#include <android-base/stringprintf.h>
@@ -99,17 +102,19 @@
size_t count = 0;
std::optional<DisplayViewport> result = std::nullopt;
for (const DisplayViewport& currentViewport : mDisplays) {
- // Return the first match
+ // Return the first match, or the default display if we're looking for the internal viewport
if (currentViewport.type == type) {
- if (!result) {
+ if (!result ||
+ (type == ViewportType::INTERNAL &&
+ currentViewport.displayId == ADISPLAY_ID_DEFAULT)) {
result = std::make_optional(currentViewport);
}
count++;
}
}
if (count > 1) {
- ALOGE("Found %zu viewports with type %s, but expected 1 at most",
- count, viewportTypeToString(type));
+ ALOGW("Found %zu viewports with type %s, but expected 1 at most", count,
+ NamedEnum::string(type).c_str());
}
return result;
}
diff --git a/services/inputflinger/OWNERS b/services/inputflinger/OWNERS
index 0313a40..82c6ee1 100644
--- a/services/inputflinger/OWNERS
+++ b/services/inputflinger/OWNERS
@@ -1,2 +1,3 @@
+lzye@google.com
michaelwr@google.com
svv@google.com
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
new file mode 100644
index 0000000..8073a93
--- /dev/null
+++ b/services/inputflinger/TEST_MAPPING
@@ -0,0 +1,43 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsWindowManagerDeviceTestCases",
+ "options": [
+ {
+ "include-filter": "android.server.wm.WindowInputTests"
+ }
+ ]
+ },
+ {
+ "name": "libinput_tests"
+ },
+ {
+ "name": "inputflinger_tests"
+ },
+ {
+ "name": "InputTests"
+ },
+ {
+ "name": "libinputservice_test"
+ },
+ {
+ "name": "CtsInputTestCases"
+ },
+ {
+ "name": "CtsViewTestCases",
+ "options": [
+ {
+ "include-filter": "android.view.cts.MotionEventTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsSecurityTestCases",
+ "options": [
+ {
+ "include-filter": "android.security.cts.MotionEventTest"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/inputflinger/VibrationElement.cpp b/services/inputflinger/VibrationElement.cpp
new file mode 100644
index 0000000..17e1ad4
--- /dev/null
+++ b/services/inputflinger/VibrationElement.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2020 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 "VibrationElement.h"
+
+#include <android-base/stringprintf.h>
+
+#include <algorithm>
+#include <cinttypes>
+
+using android::base::StringPrintf;
+
+namespace android {
+// VibrationElement implementations
+VibrationElement::VibrationElement(size_t channelNum) {
+ channels.reserve(channelNum);
+}
+
+VibrationElement::VibrationElement(const VibrationElement& other) {
+ duration = other.duration;
+ channels.resize(other.channels.size());
+ for (size_t i = 0; i < other.channels.size(); i++) {
+ channels[i].first = other.channels[i].first;
+ channels[i].second = other.channels[i].second;
+ }
+}
+
+const std::string VibrationElement::toString() const {
+ std::string dump;
+ dump += StringPrintf("[duration=%lldms, channels=[", duration.count());
+
+ for (auto it = channels.begin(); it != channels.end(); ++it) {
+ dump += std::to_string(it->first);
+ dump += " : ";
+ dump += std::to_string(it->second);
+ if (std::next(it) != channels.end()) {
+ dump += ", ";
+ }
+ }
+
+ dump += "]]";
+ return dump;
+}
+
+uint16_t VibrationElement::getMagnitude(int32_t vibratorId) const {
+ auto it =
+ std::find_if(channels.begin(), channels.end(),
+ [vibratorId](const std::pair<int32_t /*vibratorId*/, uint8_t /*amplitude*/>
+ pair) { return pair.first == vibratorId; });
+ if (it == channels.end()) {
+ return 0;
+ }
+ // convert range [0,255] to [0,65535] (android framework to linux ff ranges)
+ return static_cast<uint16_t>(it->second) << 8;
+}
+
+bool VibrationElement::isOn() const {
+ return std::any_of(channels.begin(), channels.end(),
+ [](const auto& channel) { return channel.second != 0; });
+}
+
+void VibrationElement::addChannel(int32_t vibratorId, uint8_t amplitude) {
+ channels.push_back(std::make_pair(vibratorId, amplitude));
+}
+
+bool VibrationElement::operator==(const VibrationElement& other) const {
+ if (duration != other.duration || channels.size() != other.channels.size()) {
+ return false;
+ }
+ for (size_t i = 0; i < CHANNEL_SIZE; i++) {
+ if (channels[i] != other.channels[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool VibrationElement::operator!=(const VibrationElement& other) const {
+ return !(*this == other);
+}
+
+// VibrationSequence implementations
+VibrationSequence::VibrationSequence(size_t length) {
+ pattern.reserve(length);
+}
+
+void VibrationSequence::operator=(const VibrationSequence& other) {
+ pattern = other.pattern;
+}
+
+bool VibrationSequence::operator==(const VibrationSequence& other) const {
+ if (pattern.size() != other.pattern.size()) {
+ return false;
+ }
+ for (size_t i = 0; i < pattern.size(); i++) {
+ if (pattern[i] != other.pattern[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void VibrationSequence::addElement(VibrationElement element) {
+ pattern.push_back(element);
+}
+
+const std::string VibrationSequence::toString() const {
+ std::string dump;
+ dump += "[";
+
+ for (const auto& element : pattern) {
+ dump += element.toString();
+ dump += " ";
+ }
+
+ dump += "]";
+ return dump;
+}
+
+} // namespace android
diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp
index 67d85d6..ea37f4d 100644
--- a/services/inputflinger/benchmarks/Android.bp
+++ b/services/inputflinger/benchmarks/Android.bp
@@ -25,8 +25,10 @@
"libstatslog",
"libui",
"libutils",
+ "lib-platform-compat-native-api",
],
static_libs: [
+ "libattestation",
"libinputdispatcher",
],
}
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 5a14133..7bd0c6b 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -19,6 +19,9 @@
#include <binder/Binder.h>
#include "../dispatcher/InputDispatcher.h"
+using android::os::InputEventInjectionResult;
+using android::os::InputEventInjectionSync;
+
namespace android::inputdispatcher {
// An arbitrary device id.
@@ -45,49 +48,72 @@
virtual ~FakeInputDispatcherPolicy() {}
private:
- virtual void notifyConfigurationChanged(nsecs_t) override {}
+ void notifyConfigurationChanged(nsecs_t) override {}
- virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>&, const sp<IBinder>&,
- const std::string& name) override {
- ALOGE("The window is not responding : %s", name.c_str());
- return 0;
+ void notifyNoFocusedWindowAnr(
+ const std::shared_ptr<InputApplicationHandle>& applicationHandle) override {
+ ALOGE("There is no focused window for %s", applicationHandle->getName().c_str());
}
- virtual void notifyInputChannelBroken(const sp<IBinder>&) override {}
+ void notifyWindowUnresponsive(const sp<IBinder>& connectionToken,
+ const std::string& reason) override {
+ ALOGE("Window is not responding: %s", reason.c_str());
+ }
- virtual void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
+ void notifyWindowResponsive(const sp<IBinder>& connectionToken) override {}
- virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override {
+ void notifyMonitorUnresponsive(int32_t pid, const std::string& reason) override {
+ ALOGE("Monitor is not responding: %s", reason.c_str());
+ }
+
+ void notifyMonitorResponsive(int32_t pid) override {}
+
+ void notifyInputChannelBroken(const sp<IBinder>&) override {}
+
+ void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
+
+ void notifySensorEvent(int32_t deviceId, InputDeviceSensorType sensorType,
+ InputDeviceSensorAccuracy accuracy, nsecs_t timestamp,
+ const std::vector<float>& values) override {}
+
+ void notifySensorAccuracy(int32_t deviceId, InputDeviceSensorType sensorType,
+ InputDeviceSensorAccuracy accuracy) override {}
+
+ void notifyVibratorState(int32_t deviceId, bool isOn) override {}
+
+ void notifyUntrustedTouch(const std::string& obscuringPackage) override {}
+
+ void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override {
*outConfig = mConfig;
}
- virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override {
+ bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override {
return true;
}
- virtual void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) override {}
+ void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) override {}
- virtual void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
+ void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
- virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent*,
- uint32_t) override {
+ nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent*, uint32_t) override {
return 0;
}
- virtual bool dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent*, uint32_t,
- KeyEvent*) override {
+ bool dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent*, uint32_t, KeyEvent*) override {
return false;
}
- virtual void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) override {}
+ void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) override {}
- virtual void pokeUserActivity(nsecs_t, int32_t) override {}
+ void pokeUserActivity(nsecs_t, int32_t, int32_t) override {}
- virtual bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override {
- return false;
- }
+ bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override { return false; }
- virtual void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {}
+ void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {}
+
+ void setPointerCapture(bool enabled) override {}
+
+ void notifyDropWindow(const sp<IBinder>&, float x, float y) override {}
InputDispatcherConfiguration mConfig;
};
@@ -98,7 +124,8 @@
virtual ~FakeApplicationHandle() {}
virtual bool updateInfo() {
- mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count();
+ mInfo.dispatchingTimeoutMillis =
+ std::chrono::duration_cast<std::chrono::milliseconds>(DISPATCHING_TIMEOUT).count();
return true;
}
};
@@ -106,7 +133,7 @@
class FakeInputReceiver {
public:
void consumeEvent() {
- uint32_t consumeSeq;
+ uint32_t consumeSeq = 0;
InputEvent* event;
std::chrono::time_point start = std::chrono::steady_clock::now();
@@ -132,14 +159,14 @@
protected:
explicit FakeInputReceiver(const sp<InputDispatcher>& dispatcher, const std::string name)
: mDispatcher(dispatcher) {
- InputChannel::openInputChannelPair(name, mServerChannel, mClientChannel);
+ mClientChannel = *mDispatcher->createInputChannel(name);
mConsumer = std::make_unique<InputConsumer>(mClientChannel);
}
virtual ~FakeInputReceiver() {}
sp<InputDispatcher> mDispatcher;
- sp<InputChannel> mServerChannel, mClientChannel;
+ std::shared_ptr<InputChannel> mClientChannel;
std::unique_ptr<InputConsumer> mConsumer;
PreallocatedInputEventFactory mEventFactory;
};
@@ -149,21 +176,18 @@
static const int32_t WIDTH = 200;
static const int32_t HEIGHT = 200;
- FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
+ FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
const sp<InputDispatcher>& dispatcher, const std::string name)
: FakeInputReceiver(dispatcher, name), mFrame(Rect(0, 0, WIDTH, HEIGHT)) {
- mDispatcher->registerInputChannel(mServerChannel);
-
inputApplicationHandle->updateInfo();
mInfo.applicationInfo = *inputApplicationHandle->getInfo();
}
virtual bool updateInfo() override {
- mInfo.token = mServerChannel->getConnectionToken();
+ mInfo.token = mClientChannel->getConnectionToken();
mInfo.name = "FakeWindowHandle";
- mInfo.layoutParamsFlags = 0;
- mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
- mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count();
+ mInfo.type = InputWindowInfo::Type::APPLICATION;
+ mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
mInfo.frameLeft = mFrame.left;
mInfo.frameTop = mFrame.top;
mInfo.frameRight = mFrame.right;
@@ -172,13 +196,11 @@
mInfo.touchableRegion.clear();
mInfo.addTouchableRegion(mFrame);
mInfo.visible = true;
- mInfo.canReceiveKeys = true;
- mInfo.hasFocus = true;
+ mInfo.focusable = true;
mInfo.hasWallpaper = false;
mInfo.paused = false;
mInfo.ownerPid = INJECTOR_PID;
mInfo.ownerUid = INJECTOR_UID;
- mInfo.inputFeatures = 0;
mInfo.displayId = ADISPLAY_ID_DEFAULT;
return true;
@@ -202,13 +224,13 @@
const nsecs_t currentTime = now();
+ ui::Transform identityTransform;
MotionEvent event;
event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN,
/* actionButton */ 0, /* flags */ 0,
/* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
- 1 /* xScale */, 1 /* yScale */,
- /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0,
+ identityTransform, /* xPrecision */ 0,
/* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, currentTime,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
@@ -229,8 +251,9 @@
const nsecs_t currentTime = now();
// Define a valid motion event.
- NotifyMotionArgs args(/* id */ 0, currentTime, DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, POLICY_FLAG_PASS_TO_USER, AMOTION_EVENT_ACTION_DOWN,
+ NotifyMotionArgs args(/* id */ 0, currentTime, currentTime, DEVICE_ID,
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, POLICY_FLAG_PASS_TO_USER,
+ AMOTION_EVENT_ACTION_DOWN,
/* actionButton */ 0, /* flags */ 0, AMETA_NONE, /* buttonState */ 0,
MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
pointerProperties, pointerCoords,
@@ -249,7 +272,7 @@
dispatcher->start();
// Create a window that will receive motion events
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window = new FakeWindowHandle(application, dispatcher, "Fake Window");
dispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
@@ -285,7 +308,7 @@
dispatcher->start();
// Create a window that will receive motion events
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window = new FakeWindowHandle(application, dispatcher, "Fake Window");
dispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
@@ -294,13 +317,13 @@
MotionEvent event = generateMotionEvent();
// Send ACTION_DOWN
dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- INPUT_EVENT_INJECTION_SYNC_NONE, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::NONE, INJECT_EVENT_TIMEOUT,
POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
// Send ACTION_UP
event.setAction(AMOTION_EVENT_ACTION_UP);
dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- INPUT_EVENT_INJECTION_SYNC_NONE, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::NONE, INJECT_EVENT_TIMEOUT,
POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
window->consumeEvent();
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index 40c2409..9750ef9 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -34,6 +34,7 @@
"AnrTracker.cpp",
"Connection.cpp",
"Entry.cpp",
+ "FocusResolver.cpp",
"InjectionState.cpp",
"InputDispatcher.cpp",
"InputDispatcherFactory.cpp",
@@ -41,6 +42,7 @@
"InputTarget.cpp",
"Monitor.cpp",
"TouchState.cpp",
+ "DragState.cpp",
],
}
@@ -56,6 +58,10 @@
"libstatslog",
"libui",
"libutils",
+ "lib-platform-compat-native-api",
+ ],
+ static_libs: [
+ "libattestation",
],
header_libs: [
"libinputdispatcher_headers",
@@ -77,4 +83,5 @@
export_header_lib_headers: [
"libinputdispatcher_headers",
],
+ logtags: ["EventLogTags.logtags"],
}
diff --git a/services/inputflinger/dispatcher/Connection.cpp b/services/inputflinger/dispatcher/Connection.cpp
index f5ea563..cee9c39 100644
--- a/services/inputflinger/dispatcher/Connection.cpp
+++ b/services/inputflinger/dispatcher/Connection.cpp
@@ -20,7 +20,7 @@
namespace android::inputdispatcher {
-Connection::Connection(const sp<InputChannel>& inputChannel, bool monitor,
+Connection::Connection(const std::shared_ptr<InputChannel>& inputChannel, bool monitor,
const IdGenerator& idGenerator)
: status(STATUS_NORMAL),
inputChannel(inputChannel),
diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h
index 3b33f29..c4262ad 100644
--- a/services/inputflinger/dispatcher/Connection.h
+++ b/services/inputflinger/dispatcher/Connection.h
@@ -42,7 +42,7 @@
};
Status status;
- sp<InputChannel> inputChannel; // never null
+ std::shared_ptr<InputChannel> inputChannel; // never null
bool monitor;
InputPublisher inputPublisher;
InputState inputState;
@@ -59,7 +59,8 @@
// yet received a "finished" response from the application.
std::deque<DispatchEntry*> waitQueue;
- Connection(const sp<InputChannel>& inputChannel, bool monitor, const IdGenerator& idGenerator);
+ Connection(const std::shared_ptr<InputChannel>& inputChannel, bool monitor,
+ const IdGenerator& idGenerator);
inline const std::string getInputChannelName() const { return inputChannel->getName(); }
diff --git a/services/inputflinger/dispatcher/DragState.cpp b/services/inputflinger/dispatcher/DragState.cpp
new file mode 100644
index 0000000..2e2df43
--- /dev/null
+++ b/services/inputflinger/dispatcher/DragState.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 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 "DragState.h"
+#include <android-base/stringprintf.h>
+#include <input/InputWindow.h>
+
+using android::InputWindowHandle;
+using android::base::StringPrintf;
+
+namespace android::inputdispatcher {
+
+void DragState::dump(std::string& dump, const char* prefix) {
+ dump += prefix + StringPrintf("Drag Window: %s\n", dragWindow->getName().c_str());
+ if (dragHoverWindowHandle) {
+ dump += prefix +
+ StringPrintf("Drag Hover Window: %s\n", dragHoverWindowHandle->getName().c_str());
+ }
+ dump += prefix + StringPrintf("isStartDrag: %s\n", isStartDrag ? "true" : "false");
+ dump += prefix +
+ StringPrintf("isStylusButtonDownAtStart: %s\n",
+ isStylusButtonDownAtStart ? "true" : "false");
+}
+
+} // namespace android::inputdispatcher
\ No newline at end of file
diff --git a/services/inputflinger/dispatcher/DragState.h b/services/inputflinger/dispatcher/DragState.h
new file mode 100644
index 0000000..06453d8
--- /dev/null
+++ b/services/inputflinger/dispatcher/DragState.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#ifndef _UI_INPUT_INPUTDISPATCHER_DRAGSTATE_H
+#define _UI_INPUT_INPUTDISPATCHER_DRAGSTATE_H
+
+#include <utils/RefBase.h>
+#include <string>
+
+namespace android {
+
+class InputWindowHandle;
+
+namespace inputdispatcher {
+struct DragState {
+ DragState(const sp<android::InputWindowHandle>& windowHandle) : dragWindow(windowHandle) {}
+ void dump(std::string& dump, const char* prefix = "");
+
+ // The window being dragged.
+ const sp<InputWindowHandle> dragWindow;
+ // The last drag hover window which could receive the drag event.
+ sp<InputWindowHandle> dragHoverWindowHandle;
+ // Indicates the if received first event to check for button state.
+ bool isStartDrag = false;
+ // Indicate if the stylus button is down at the start of the drag.
+ bool isStylusButtonDownAtStart = false;
+};
+
+} // namespace inputdispatcher
+} // namespace android
+
+#endif // _UI_INPUT_INPUTDISPATCHER_DRAGSTATE_H
\ No newline at end of file
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index fdbb1d1..5270b8a 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -59,7 +59,6 @@
EventEntry::EventEntry(int32_t id, Type type, nsecs_t eventTime, uint32_t policyFlags)
: id(id),
- refCount(1),
type(type),
eventTime(eventTime),
policyFlags(policyFlags),
@@ -70,21 +69,6 @@
releaseInjectionState();
}
-std::string EventEntry::getDescription() const {
- std::string result;
- appendDescription(result);
- return result;
-}
-
-void EventEntry::release() {
- refCount -= 1;
- if (refCount == 0) {
- delete this;
- } else {
- ALOG_ASSERT(refCount > 0);
- }
-}
-
void EventEntry::releaseInjectionState() {
if (injectionState) {
injectionState->release();
@@ -99,8 +83,8 @@
ConfigurationChangedEntry::~ConfigurationChangedEntry() {}
-void ConfigurationChangedEntry::appendDescription(std::string& msg) const {
- msg += StringPrintf("ConfigurationChangedEvent(), policyFlags=0x%08x", policyFlags);
+std::string ConfigurationChangedEntry::getDescription() const {
+ return StringPrintf("ConfigurationChangedEvent(), policyFlags=0x%08x", policyFlags);
}
// --- DeviceResetEntry ---
@@ -110,22 +94,57 @@
DeviceResetEntry::~DeviceResetEntry() {}
-void DeviceResetEntry::appendDescription(std::string& msg) const {
- msg += StringPrintf("DeviceResetEvent(deviceId=%d), policyFlags=0x%08x", deviceId, policyFlags);
+std::string DeviceResetEntry::getDescription() const {
+ return StringPrintf("DeviceResetEvent(deviceId=%d), policyFlags=0x%08x", deviceId, policyFlags);
}
// --- FocusEntry ---
// Focus notifications always go to apps, so set the flag POLICY_FLAG_PASS_TO_USER for all entries
-FocusEntry::FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus)
+FocusEntry::FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus,
+ const std::string& reason)
: EventEntry(id, Type::FOCUS, eventTime, POLICY_FLAG_PASS_TO_USER),
connectionToken(connectionToken),
- hasFocus(hasFocus) {}
+ hasFocus(hasFocus),
+ reason(reason) {}
FocusEntry::~FocusEntry() {}
-void FocusEntry::appendDescription(std::string& msg) const {
- msg += StringPrintf("FocusEvent(hasFocus=%s)", hasFocus ? "true" : "false");
+std::string FocusEntry::getDescription() const {
+ return StringPrintf("FocusEvent(hasFocus=%s)", hasFocus ? "true" : "false");
+}
+
+// --- PointerCaptureChangedEntry ---
+
+// PointerCaptureChanged notifications always go to apps, so set the flag POLICY_FLAG_PASS_TO_USER
+// for all entries.
+PointerCaptureChangedEntry::PointerCaptureChangedEntry(int32_t id, nsecs_t eventTime,
+ bool hasPointerCapture)
+ : EventEntry(id, Type::POINTER_CAPTURE_CHANGED, eventTime, POLICY_FLAG_PASS_TO_USER),
+ pointerCaptureEnabled(hasPointerCapture) {}
+
+PointerCaptureChangedEntry::~PointerCaptureChangedEntry() {}
+
+std::string PointerCaptureChangedEntry::getDescription() const {
+ return StringPrintf("PointerCaptureChangedEvent(pointerCaptureEnabled=%s)",
+ pointerCaptureEnabled ? "true" : "false");
+}
+
+// --- DragEntry ---
+
+// Drag notifications always go to apps, so set the flag POLICY_FLAG_PASS_TO_USER for all entries
+DragEntry::DragEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool isExiting,
+ float x, float y)
+ : EventEntry(id, Type::DRAG, eventTime, POLICY_FLAG_PASS_TO_USER),
+ connectionToken(connectionToken),
+ isExiting(isExiting),
+ x(x),
+ y(y) {}
+
+DragEntry::~DragEntry() {}
+
+std::string DragEntry::getDescription() const {
+ return StringPrintf("DragEntry(isExiting=%s, x=%f, y=%f)", isExiting ? "true" : "false", x, y);
}
// --- KeyEntry ---
@@ -151,16 +170,16 @@
KeyEntry::~KeyEntry() {}
-void KeyEntry::appendDescription(std::string& msg) const {
- msg += StringPrintf("KeyEvent");
+std::string KeyEntry::getDescription() const {
if (!GetBoolProperty("ro.debuggable", false)) {
- return;
+ return "KeyEvent";
}
- msg += StringPrintf("(deviceId=%d, source=0x%08x, displayId=%" PRId32 ", action=%s, "
+ return StringPrintf("KeyEvent(deviceId=%d, eventTime=%" PRIu64
+ ", source=0x%08x, displayId=%" PRId32 ", action=%s, "
"flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, "
"repeatCount=%d), policyFlags=0x%08x",
- deviceId, source, displayId, KeyEvent::actionToString(action), flags,
- keyCode, scanCode, metaState, repeatCount, policyFlags);
+ deviceId, eventTime, source, displayId, KeyEvent::actionToString(action),
+ flags, keyCode, scanCode, metaState, repeatCount, policyFlags);
}
void KeyEntry::recycle() {
@@ -183,7 +202,6 @@
uint32_t pointerCount, const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords, float xOffset, float yOffset)
: EventEntry(id, Type::MOTION, eventTime, policyFlags),
- eventTime(eventTime),
deviceId(deviceId),
source(source),
displayId(displayId),
@@ -211,20 +229,21 @@
MotionEntry::~MotionEntry() {}
-void MotionEntry::appendDescription(std::string& msg) const {
- msg += StringPrintf("MotionEvent");
+std::string MotionEntry::getDescription() const {
if (!GetBoolProperty("ro.debuggable", false)) {
- return;
+ return "MotionEvent";
}
- msg += StringPrintf("(deviceId=%d, source=0x%08x, displayId=%" PRId32
+ std::string msg;
+ msg += StringPrintf("MotionEvent(deviceId=%d, eventTime=%" PRIu64
+ ", source=0x%08x, displayId=%" PRId32
", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, "
"buttonState=0x%08x, "
"classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, "
"xCursorPosition=%0.1f, yCursorPosition=%0.1f, pointers=[",
- deviceId, source, displayId, MotionEvent::actionToString(action),
- actionButton, flags, metaState, buttonState,
- motionClassificationToString(classification), edgeFlags, xPrecision,
- yPrecision, xCursorPosition, yCursorPosition);
+ deviceId, eventTime, source, displayId,
+ MotionEvent::actionToString(action).c_str(), actionButton, flags, metaState,
+ buttonState, motionClassificationToString(classification), edgeFlags,
+ xPrecision, yPrecision, xCursorPosition, yCursorPosition);
for (uint32_t i = 0; i < pointerCount; i++) {
if (i) {
@@ -234,32 +253,58 @@
pointerCoords[i].getY());
}
msg += StringPrintf("]), policyFlags=0x%08x", policyFlags);
+ return msg;
+}
+
+// --- SensorEntry ---
+
+SensorEntry::SensorEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
+ uint32_t policyFlags, nsecs_t hwTimestamp,
+ InputDeviceSensorType sensorType, InputDeviceSensorAccuracy accuracy,
+ bool accuracyChanged, std::vector<float> values)
+ : EventEntry(id, Type::SENSOR, eventTime, policyFlags),
+ deviceId(deviceId),
+ source(source),
+ sensorType(sensorType),
+ accuracy(accuracy),
+ accuracyChanged(accuracyChanged),
+ hwTimestamp(hwTimestamp),
+ values(std::move(values)) {}
+
+SensorEntry::~SensorEntry() {}
+
+std::string SensorEntry::getDescription() const {
+ std::string msg;
+ msg += StringPrintf("SensorEntry(deviceId=%d, source=0x%08x, sensorType=0x%08x, "
+ "accuracy=0x%08x, hwTimestamp=%" PRId64,
+ deviceId, source, sensorType, accuracy, hwTimestamp);
+
+ if (!GetBoolProperty("ro.debuggable", false)) {
+ for (size_t i = 0; i < values.size(); i++) {
+ if (i > 0) {
+ msg += ", ";
+ }
+ msg += StringPrintf("(%.3f)", values[i]);
+ }
+ }
+ msg += StringPrintf(", policyFlags=0x%08x", policyFlags);
+ return msg;
}
// --- DispatchEntry ---
volatile int32_t DispatchEntry::sNextSeqAtomic;
-DispatchEntry::DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, float xOffset,
- float yOffset, float globalScaleFactor, float windowXScale,
- float windowYScale)
+DispatchEntry::DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags,
+ ui::Transform transform, float globalScaleFactor)
: seq(nextSeq()),
- eventEntry(eventEntry),
+ eventEntry(std::move(eventEntry)),
targetFlags(targetFlags),
- xOffset(xOffset),
- yOffset(yOffset),
+ transform(transform),
globalScaleFactor(globalScaleFactor),
- windowXScale(windowXScale),
- windowYScale(windowYScale),
deliveryTime(0),
resolvedAction(0),
- resolvedFlags(0) {
- eventEntry->refCount += 1;
-}
-
-DispatchEntry::~DispatchEntry() {
- eventEntry->release();
-}
+ resolvedFlags(0) {}
uint32_t DispatchEntry::nextSeq() {
// Sequence number 0 is reserved and will never be returned.
@@ -278,7 +323,8 @@
keyEntry(nullptr),
userActivityEventType(0),
seq(0),
- handled(false) {}
+ handled(false),
+ enabled(false) {}
CommandEntry::~CommandEntry() {}
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 6b7697d..f3ef64b 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -36,25 +36,12 @@
FOCUS,
KEY,
MOTION,
+ SENSOR,
+ POINTER_CAPTURE_CHANGED,
+ DRAG,
};
- static const char* typeToString(Type type) {
- switch (type) {
- case Type::CONFIGURATION_CHANGED:
- return "CONFIGURATION_CHANGED";
- case Type::DEVICE_RESET:
- return "DEVICE_RESET";
- case Type::FOCUS:
- return "FOCUS";
- case Type::KEY:
- return "KEY";
- case Type::MOTION:
- return "MOTION";
- }
- }
-
int32_t id;
- mutable int32_t refCount;
Type type;
nsecs_t eventTime;
uint32_t policyFlags;
@@ -79,23 +66,19 @@
return isInjected() || IdGenerator::getSource(id) != IdGenerator::Source::INPUT_READER;
}
- void release();
+ virtual std::string getDescription() const = 0;
- virtual void appendDescription(std::string& msg) const = 0;
-
- std::string getDescription() const;
-
-protected:
EventEntry(int32_t id, Type type, nsecs_t eventTime, uint32_t policyFlags);
virtual ~EventEntry();
+
+protected:
void releaseInjectionState();
};
struct ConfigurationChangedEntry : EventEntry {
explicit ConfigurationChangedEntry(int32_t id, nsecs_t eventTime);
- virtual void appendDescription(std::string& msg) const;
+ std::string getDescription() const override;
-protected:
virtual ~ConfigurationChangedEntry();
};
@@ -103,23 +86,44 @@
int32_t deviceId;
DeviceResetEntry(int32_t id, nsecs_t eventTime, int32_t deviceId);
- virtual void appendDescription(std::string& msg) const;
+ std::string getDescription() const override;
-protected:
virtual ~DeviceResetEntry();
};
struct FocusEntry : EventEntry {
sp<IBinder> connectionToken;
bool hasFocus;
+ std::string reason;
- FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus);
- virtual void appendDescription(std::string& msg) const;
+ FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus,
+ const std::string& reason);
+ std::string getDescription() const override;
-protected:
virtual ~FocusEntry();
};
+struct PointerCaptureChangedEntry : EventEntry {
+ bool pointerCaptureEnabled;
+
+ PointerCaptureChangedEntry(int32_t id, nsecs_t eventTime, bool hasPointerCapture);
+ std::string getDescription() const override;
+
+ virtual ~PointerCaptureChangedEntry();
+};
+
+struct DragEntry : EventEntry {
+ sp<IBinder> connectionToken;
+ bool isExiting;
+ float x, y;
+
+ DragEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool isExiting, float x,
+ float y);
+ std::string getDescription() const override;
+
+ ~DragEntry() override;
+};
+
struct KeyEntry : EventEntry {
int32_t deviceId;
uint32_t source;
@@ -146,15 +150,13 @@
KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
int32_t metaState, int32_t repeatCount, nsecs_t downTime);
- virtual void appendDescription(std::string& msg) const;
+ std::string getDescription() const override;
void recycle();
-protected:
virtual ~KeyEntry();
};
struct MotionEntry : EventEntry {
- nsecs_t eventTime;
int32_t deviceId;
uint32_t source;
int32_t displayId;
@@ -181,23 +183,38 @@
float yCursorPosition, nsecs_t downTime, uint32_t pointerCount,
const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
float xOffset, float yOffset);
- virtual void appendDescription(std::string& msg) const;
+ std::string getDescription() const override;
-protected:
virtual ~MotionEntry();
};
+struct SensorEntry : EventEntry {
+ int32_t deviceId;
+ uint32_t source;
+ InputDeviceSensorType sensorType;
+ InputDeviceSensorAccuracy accuracy;
+ bool accuracyChanged;
+ nsecs_t hwTimestamp;
+
+ std::vector<float> values;
+
+ SensorEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
+ uint32_t policyFlags, nsecs_t hwTimestamp, InputDeviceSensorType sensorType,
+ InputDeviceSensorAccuracy accuracy, bool accuracyChanged,
+ std::vector<float> values);
+ std::string getDescription() const override;
+
+ virtual ~SensorEntry();
+};
+
// Tracks the progress of dispatching a particular event to a particular connection.
struct DispatchEntry {
const uint32_t seq; // unique sequence number, never 0
- EventEntry* eventEntry; // the event to dispatch
+ std::shared_ptr<EventEntry> eventEntry; // the event to dispatch
int32_t targetFlags;
- float xOffset;
- float yOffset;
+ ui::Transform transform;
float globalScaleFactor;
- float windowXScale = 1.0f;
- float windowYScale = 1.0f;
// Both deliveryTime and timeoutTime are only populated when the entry is sent to the app,
// and will be undefined before that.
nsecs_t deliveryTime; // time when the event was actually delivered
@@ -209,9 +226,8 @@
int32_t resolvedAction;
int32_t resolvedFlags;
- DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, float xOffset, float yOffset,
- float globalScaleFactor, float windowXScale, float windowYScale);
- ~DispatchEntry();
+ DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags,
+ ui::Transform transform, float globalScaleFactor);
inline bool hasForegroundTarget() const { return targetFlags & InputTarget::FLAG_FOREGROUND; }
@@ -256,15 +272,23 @@
// parameters for the command (usage varies by command)
sp<Connection> connection;
nsecs_t eventTime;
- KeyEntry* keyEntry;
- sp<InputApplicationHandle> inputApplicationHandle;
+ std::shared_ptr<KeyEntry> keyEntry;
+ std::shared_ptr<SensorEntry> sensorEntry;
+ std::shared_ptr<InputApplicationHandle> inputApplicationHandle;
std::string reason;
int32_t userActivityEventType;
uint32_t seq;
bool handled;
- sp<InputChannel> inputChannel;
+ sp<IBinder> connectionToken;
sp<IBinder> oldToken;
sp<IBinder> newToken;
+ std::string obscuringPackage;
+ bool enabled;
+ int32_t pid;
+ nsecs_t consumeTime; // time when the event was consumed by InputConsumer
+ int32_t displayId;
+ float x;
+ float y;
};
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/EventLogTags.logtags b/services/inputflinger/dispatcher/EventLogTags.logtags
new file mode 100644
index 0000000..2836467
--- /dev/null
+++ b/services/inputflinger/dispatcher/EventLogTags.logtags
@@ -0,0 +1,42 @@
+# The entries in this file map a sparse set of log tag numbers to tag names.
+# This is installed on the device, in /system/etc, and parsed by logcat.
+#
+# Tag numbers are decimal integers, from 0 to 2^31. (Let's leave the
+# negative values alone for now.)
+#
+# Tag names are one or more ASCII letters and numbers or underscores, i.e.
+# "[A-Z][a-z][0-9]_". Do not include spaces or punctuation (the former
+# impacts log readability, the latter makes regex searches more annoying).
+#
+# Tag numbers and names are separated by whitespace. Blank lines and lines
+# starting with '#' are ignored.
+#
+# Optionally, after the tag names can be put a description for the value(s)
+# of the tag. Description are in the format
+# (<name>|data type[|data unit])
+# Multiple values are separated by commas.
+#
+# The data type is a number from the following values:
+# 1: int
+# 2: long
+# 3: string
+# 4: list
+#
+# The data unit is a number taken from the following list:
+# 1: Number of objects
+# 2: Number of bytes
+# 3: Number of milliseconds
+# 4: Number of allocations
+# 5: Id
+# 6: Percent
+# Default value for data of type int/long is 2 (bytes).
+#
+# See system/core/logcat/event.logtags for the master copy of the tags.
+
+# 62000 - 62199 reserved for inputflinger
+
+62000 input_interaction (windows|4)
+62001 input_focus (window|3),(reason|3)
+
+# NOTE - the range 1000000-2000000 is reserved for partners and others who
+# want to define their own log tags without conflicting with the core platform.
\ No newline at end of file
diff --git a/services/inputflinger/dispatcher/FocusResolver.cpp b/services/inputflinger/dispatcher/FocusResolver.cpp
new file mode 100644
index 0000000..2db8c13
--- /dev/null
+++ b/services/inputflinger/dispatcher/FocusResolver.cpp
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#define LOG_TAG "FocusResolver"
+#define ATRACE_TAG ATRACE_TAG_INPUT
+
+#define INDENT " "
+#define INDENT2 " "
+
+// Log debug messages about input focus tracking.
+static constexpr bool DEBUG_FOCUS = false;
+
+#include <inttypes.h>
+
+#include <android-base/stringprintf.h>
+#include <binder/Binder.h>
+#include <input/InputWindow.h>
+#include <input/NamedEnum.h>
+#include <log/log.h>
+
+#include "FocusResolver.h"
+
+namespace android::inputdispatcher {
+
+sp<IBinder> FocusResolver::getFocusedWindowToken(int32_t displayId) const {
+ auto it = mFocusedWindowTokenByDisplay.find(displayId);
+ return it != mFocusedWindowTokenByDisplay.end() ? it->second.second : nullptr;
+}
+
+std::optional<FocusRequest> FocusResolver::getFocusRequest(int32_t displayId) {
+ auto it = mFocusRequestByDisplay.find(displayId);
+ return it != mFocusRequestByDisplay.end() ? std::make_optional<>(it->second) : std::nullopt;
+}
+
+/**
+ * 'setInputWindows' is called when the window properties change. Here we will check whether the
+ * currently focused window can remain focused. If the currently focused window remains eligible
+ * for focus ('isTokenFocusable' returns OK), then we will continue to grant it focus otherwise
+ * we will check if the previous focus request is eligible to receive focus.
+ */
+std::optional<FocusResolver::FocusChanges> FocusResolver::setInputWindows(
+ int32_t displayId, const std::vector<sp<InputWindowHandle>>& windows) {
+ std::string removeFocusReason;
+
+ // Check if the currently focused window is still focusable.
+ const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
+ if (currentFocus) {
+ Focusability result = isTokenFocusable(currentFocus, windows);
+ if (result == Focusability::OK) {
+ return std::nullopt;
+ }
+ removeFocusReason = NamedEnum::string(result);
+ }
+
+ // We don't have a focused window or the currently focused window is no longer focusable. Check
+ // to see if we can grant focus to the window that previously requested focus.
+ const std::optional<FocusRequest> request = getFocusRequest(displayId);
+ if (request) {
+ sp<IBinder> requestedFocus = request->token;
+ const Focusability result = isTokenFocusable(requestedFocus, windows);
+ const Focusability previousResult = mLastFocusResultByDisplay[displayId];
+ mLastFocusResultByDisplay[displayId] = result;
+ if (result == Focusability::OK) {
+ return updateFocusedWindow(displayId,
+ "Window became focusable. Previous reason: " +
+ NamedEnum::string(previousResult),
+ requestedFocus, request->windowName);
+ }
+ }
+
+ // Focused window is no longer focusable and we don't have a suitable focus request to grant.
+ // Remove focus if needed.
+ return updateFocusedWindow(displayId, removeFocusReason, nullptr);
+}
+
+std::optional<FocusResolver::FocusChanges> FocusResolver::setFocusedWindow(
+ const FocusRequest& request, const std::vector<sp<InputWindowHandle>>& windows) {
+ const int32_t displayId = request.displayId;
+ const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
+ if (currentFocus == request.token) {
+ ALOGD_IF(DEBUG_FOCUS,
+ "setFocusedWindow %s on display %" PRId32 " ignored, reason: already focused",
+ request.windowName.c_str(), displayId);
+ return std::nullopt;
+ }
+
+ // Handle conditional focus requests, i.e. requests that have a focused token. These requests
+ // are not persistent. If the window is no longer focusable, we expect focus to go back to the
+ // previously focused window.
+ if (request.focusedToken) {
+ if (currentFocus != request.focusedToken) {
+ ALOGW("setFocusedWindow %s on display %" PRId32
+ " ignored, reason: focusedToken %s is not focused",
+ request.windowName.c_str(), displayId, request.focusedWindowName.c_str());
+ return std::nullopt;
+ }
+ Focusability result = isTokenFocusable(request.token, windows);
+ if (result == Focusability::OK) {
+ return updateFocusedWindow(displayId, "setFocusedWindow with focus check",
+ request.token, request.windowName);
+ }
+ ALOGW("setFocusedWindow %s on display %" PRId32 " ignored, reason: %s",
+ request.windowName.c_str(), displayId, NamedEnum::string(result).c_str());
+ return std::nullopt;
+ }
+
+ Focusability result = isTokenFocusable(request.token, windows);
+ // Update focus request. The focus resolver will always try to handle this request if there is
+ // no focused window on the display.
+ mFocusRequestByDisplay[displayId] = request;
+ mLastFocusResultByDisplay[displayId] = result;
+
+ if (result == Focusability::OK) {
+ return updateFocusedWindow(displayId, "setFocusedWindow", request.token,
+ request.windowName);
+ }
+
+ // The requested window is not currently focusable. Wait for the window to become focusable
+ // but remove focus from the current window so that input events can go into a pending queue
+ // and be sent to the window when it becomes focused.
+ return updateFocusedWindow(displayId, "Waiting for window because " + NamedEnum::string(result),
+ nullptr);
+}
+
+FocusResolver::Focusability FocusResolver::isTokenFocusable(
+ const sp<IBinder>& token, const std::vector<sp<InputWindowHandle>>& windows) {
+ bool allWindowsAreFocusable = true;
+ bool visibleWindowFound = false;
+ bool windowFound = false;
+ for (const sp<InputWindowHandle>& window : windows) {
+ if (window->getToken() != token) {
+ continue;
+ }
+ windowFound = true;
+ if (window->getInfo()->visible) {
+ // Check if at least a single window is visible.
+ visibleWindowFound = true;
+ }
+ if (!window->getInfo()->focusable) {
+ // Check if all windows with the window token are focusable.
+ allWindowsAreFocusable = false;
+ break;
+ }
+ }
+
+ if (!windowFound) {
+ return Focusability::NO_WINDOW;
+ }
+ if (!allWindowsAreFocusable) {
+ return Focusability::NOT_FOCUSABLE;
+ }
+ if (!visibleWindowFound) {
+ return Focusability::NOT_VISIBLE;
+ }
+
+ return Focusability::OK;
+}
+
+std::optional<FocusResolver::FocusChanges> FocusResolver::updateFocusedWindow(
+ int32_t displayId, const std::string& reason, const sp<IBinder>& newFocus,
+ const std::string& tokenName) {
+ sp<IBinder> oldFocus = getFocusedWindowToken(displayId);
+ if (newFocus == oldFocus) {
+ return std::nullopt;
+ }
+ if (newFocus) {
+ mFocusedWindowTokenByDisplay[displayId] = {tokenName, newFocus};
+ } else {
+ mFocusedWindowTokenByDisplay.erase(displayId);
+ }
+
+ return {{oldFocus, newFocus, displayId, reason}};
+}
+
+std::string FocusResolver::dumpFocusedWindows() const {
+ if (mFocusedWindowTokenByDisplay.empty()) {
+ return INDENT "FocusedWindows: <none>\n";
+ }
+
+ std::string dump;
+ dump += INDENT "FocusedWindows:\n";
+ for (const auto& [displayId, namedToken] : mFocusedWindowTokenByDisplay) {
+ dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId,
+ namedToken.first.c_str());
+ }
+ return dump;
+}
+
+std::string FocusResolver::dump() const {
+ std::string dump = dumpFocusedWindows();
+ if (mFocusRequestByDisplay.empty()) {
+ return dump + INDENT "FocusRequests: <none>\n";
+ }
+
+ dump += INDENT "FocusRequests:\n";
+ for (const auto& [displayId, request] : mFocusRequestByDisplay) {
+ auto it = mLastFocusResultByDisplay.find(displayId);
+ std::string result =
+ it != mLastFocusResultByDisplay.end() ? NamedEnum::string(it->second) : "";
+ dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s' result='%s'\n",
+ displayId, request.windowName.c_str(), result.c_str());
+ }
+ return dump;
+}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/FocusResolver.h b/services/inputflinger/dispatcher/FocusResolver.h
new file mode 100644
index 0000000..dc5eeeb
--- /dev/null
+++ b/services/inputflinger/dispatcher/FocusResolver.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <optional>
+#include <unordered_map>
+
+#include <android/FocusRequest.h>
+#include <binder/Binder.h>
+#include <input/InputWindow.h>
+
+namespace android::inputdispatcher {
+
+// Keeps track of the focused window per display. The class listens to updates from input dispatcher
+// and provides focus changes.
+//
+// Focus Policy
+// Window focusabilty - A window token can be focused if there is at least one window handle that
+// is visible with the same token and all window handles with the same token are focusable.
+// See FocusResolver::isTokenFocusable
+//
+// Focus request - Request will be granted if the window is focusable. If it's not
+// focusable, then the request is persisted and granted when it becomes focusable. The currently
+// focused window will lose focus and any pending keys will be added to a queue so it can be sent
+// to the window when it gets focus.
+//
+// Condition focus request - Request with a focus token specified. Request will be granted if the
+// window is focusable and the focus token is the currently focused. Otherwise, the request is
+// dropped. Conditional focus requests are not persisted. The window will lose focus and go back
+// to the focus token if it becomes not focusable.
+//
+// Window handle updates - Focus is lost when the currently focused window becomes not focusable.
+// If the previous focus request is focusable, then we will try to grant that window focus.
+class FocusResolver {
+public:
+ // Returns the focused window token on the specified display.
+ sp<IBinder> getFocusedWindowToken(int32_t displayId) const;
+
+ struct FocusChanges {
+ sp<IBinder> oldFocus;
+ sp<IBinder> newFocus;
+ int32_t displayId;
+ std::string reason;
+ };
+ std::optional<FocusResolver::FocusChanges> setInputWindows(
+ int32_t displayId, const std::vector<sp<InputWindowHandle>>& windows);
+ std::optional<FocusResolver::FocusChanges> setFocusedWindow(
+ const FocusRequest& request, const std::vector<sp<InputWindowHandle>>& windows);
+
+ // exposed for debugging
+ bool hasFocusedWindowTokens() const { return !mFocusedWindowTokenByDisplay.empty(); }
+ std::string dumpFocusedWindows() const;
+ std::string dump() const;
+
+private:
+ enum class Focusability {
+ OK,
+ NO_WINDOW,
+ NOT_FOCUSABLE,
+ NOT_VISIBLE,
+ };
+
+ // Checks if the window token can be focused on a display. The token can be focused if there is
+ // at least one window handle that is visible with the same token and all window handles with
+ // the same token are focusable.
+ //
+ // In the case of mirroring, two windows may share the same window token and their visibility
+ // might be different. Example, the mirrored window can cover the window its mirroring. However,
+ // we expect the focusability of the windows to match since its hard to reason why one window
+ // can receive focus events and the other cannot when both are backed by the same input channel.
+ //
+ static Focusability isTokenFocusable(const sp<IBinder>& token,
+ const std::vector<sp<InputWindowHandle>>& windows);
+
+ // Focus tracking for keys, trackball, etc. A window token can be associated with one or
+ // more InputWindowHandles. If a window is mirrored, the window and its mirror will share
+ // the same token. Focus is tracked by the token per display and the events are dispatched
+ // to the channel associated by this token.
+ typedef std::pair<std::string /* name */, sp<IBinder>> NamedToken;
+ std::unordered_map<int32_t /* displayId */, NamedToken> mFocusedWindowTokenByDisplay;
+
+ // This map will store the focus request per display. When the input window handles are updated,
+ // the current request will be checked to see if it can be processed at that time.
+ std::unordered_map<int32_t /* displayId */, FocusRequest> mFocusRequestByDisplay;
+
+ // Last reason for not granting a focus request. This is used to add more debug information
+ // in the event logs.
+ std::unordered_map<int32_t /* displayId */, Focusability> mLastFocusResultByDisplay;
+
+ std::optional<FocusResolver::FocusChanges> updateFocusedWindow(
+ int32_t displayId, const std::string& reason, const sp<IBinder>& token,
+ const std::string& tokenName = "");
+ std::optional<FocusRequest> getFocusRequest(int32_t displayId);
+};
+
+} // namespace android::inputdispatcher
\ No newline at end of file
diff --git a/services/inputflinger/dispatcher/InjectionState.cpp b/services/inputflinger/dispatcher/InjectionState.cpp
index b2d0a26..c8024a6 100644
--- a/services/inputflinger/dispatcher/InjectionState.cpp
+++ b/services/inputflinger/dispatcher/InjectionState.cpp
@@ -24,7 +24,7 @@
: refCount(1),
injectorPid(injectorPid),
injectorUid(injectorUid),
- injectionResult(INPUT_EVENT_INJECTION_PENDING),
+ injectionResult(android::os::InputEventInjectionResult::PENDING),
injectionIsAsync(false),
pendingForegroundDispatches(0) {}
diff --git a/services/inputflinger/dispatcher/InjectionState.h b/services/inputflinger/dispatcher/InjectionState.h
index 311a0f1..0bfafb1 100644
--- a/services/inputflinger/dispatcher/InjectionState.h
+++ b/services/inputflinger/dispatcher/InjectionState.h
@@ -17,34 +17,19 @@
#ifndef _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H
#define _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H
+#include <stdint.h>
#include "InputDispatcherInterface.h"
-#include <stdint.h>
+namespace android {
-namespace android::inputdispatcher {
-
-/*
- * Constants used to determine the input event injection synchronization mode.
- */
-enum {
- /* Injection is asynchronous and is assumed always to be successful. */
- INPUT_EVENT_INJECTION_SYNC_NONE = 0,
-
- /* Waits for previous events to be dispatched so that the input dispatcher can determine
- * whether input event injection willbe permitted based on the current input focus.
- * Does not wait for the input event to finish processing. */
- INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT = 1,
-
- /* Waits for the input event to be completely processed. */
- INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED = 2,
-};
+namespace inputdispatcher {
struct InjectionState {
mutable int32_t refCount;
int32_t injectorPid;
int32_t injectorUid;
- int32_t injectionResult; // initially INPUT_EVENT_INJECTION_PENDING
+ android::os::InputEventInjectionResult injectionResult; // initially PENDING
bool injectionIsAsync; // set to true if injection is not waiting for the result
int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress
@@ -55,6 +40,7 @@
~InjectionState();
};
-} // namespace android::inputdispatcher
+} // namespace inputdispatcher
+} // namespace android
#endif // _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 6ea64b4..27443b0 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -28,8 +28,8 @@
// Log debug messages about the dispatch cycle.
#define DEBUG_DISPATCH_CYCLE 0
-// Log debug messages about registrations.
-#define DEBUG_REGISTRATION 0
+// Log debug messages about channel creation
+#define DEBUG_CHANNEL_CREATION 0
// Log debug messages about input event injection.
#define DEBUG_INJECTION 0
@@ -37,50 +37,64 @@
// Log debug messages about input focus tracking.
static constexpr bool DEBUG_FOCUS = false;
+// Log debug messages about touch occlusion
+// STOPSHIP(b/169067926): Set to false
+static constexpr bool DEBUG_TOUCH_OCCLUSION = true;
+
// Log debug messages about the app switch latency optimization.
#define DEBUG_APP_SWITCH 0
// Log debug messages about hover events.
#define DEBUG_HOVER 0
-#include "InputDispatcher.h"
-
-#include "Connection.h"
-
-#include <errno.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <statslog.h>
-#include <stddef.h>
-#include <time.h>
-#include <unistd.h>
-#include <queue>
-#include <sstream>
-
#include <android-base/chrono_utils.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
+#include <android/os/IInputConstants.h>
#include <binder/Binder.h>
+#include <binder/IServiceManager.h>
+#include <com/android/internal/compat/IPlatformCompatNative.h>
#include <input/InputDevice.h>
+#include <input/InputWindow.h>
#include <log/log.h>
-#include <openssl/hmac.h>
-#include <openssl/rand.h>
+#include <log/log_event_list.h>
#include <powermanager/PowerManager.h>
+#include <statslog.h>
+#include <unistd.h>
#include <utils/Trace.h>
+#include <cerrno>
+#include <cinttypes>
+#include <climits>
+#include <cstddef>
+#include <ctime>
+#include <queue>
+#include <sstream>
+
+#include "Connection.h"
+#include "InputDispatcher.h"
+
#define INDENT " "
#define INDENT2 " "
#define INDENT3 " "
#define INDENT4 " "
using android::base::HwTimeoutMultiplier;
+using android::base::Result;
using android::base::StringPrintf;
+using android::os::BlockUntrustedTouchesMode;
+using android::os::IInputConstants;
+using android::os::InputEventInjectionResult;
+using android::os::InputEventInjectionSync;
+using com::android::internal::compat::IPlatformCompatNative;
namespace android::inputdispatcher {
// Default input dispatching timeout if there is no focused application or paused window
// from which to determine an appropriate dispatching timeout.
-const std::chrono::nanoseconds DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5s * HwTimeoutMultiplier();
+const std::chrono::duration DEFAULT_INPUT_DISPATCHING_TIMEOUT = std::chrono::milliseconds(
+ android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS *
+ HwTimeoutMultiplier());
// Amount of time to allow for all pending events to be processed when an app switch
// key is on the way. This is used to preempt input dispatch and drop input events
@@ -105,6 +119,10 @@
// Number of recent events to keep for debugging purposes.
constexpr size_t RECENT_QUEUE_MAX_SIZE = 10;
+// Event log tags. See EventLogTags.logtags for reference
+constexpr int LOGTAG_INPUT_INTERACTION = 62000;
+constexpr int LOGTAG_INPUT_FOCUS = 62001;
+
static inline nsecs_t now() {
return systemTime(SYSTEM_TIME_MONOTONIC);
}
@@ -113,6 +131,13 @@
return value ? "true" : "false";
}
+static inline const std::string toString(sp<IBinder> binder) {
+ if (binder == nullptr) {
+ return "<null>";
+ }
+ return StringPrintf("%p", binder.get());
+}
+
static inline int32_t getMotionEventActionPointerIndex(int32_t action) {
return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >>
AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
@@ -161,6 +186,10 @@
}
}
+static int64_t millis(std::chrono::nanoseconds t) {
+ return std::chrono::duration_cast<std::chrono::milliseconds>(t).count();
+}
+
static bool validateMotionEvent(int32_t action, int32_t actionButton, size_t pointerCount,
const PointerProperties* pointerProperties) {
if (!isValidMotionAction(action, actionButton, pointerCount)) {
@@ -189,12 +218,12 @@
return true;
}
-static void dumpRegion(std::string& dump, const Region& region) {
+static std::string dumpRegion(const Region& region) {
if (region.isEmpty()) {
- dump += "<empty>";
- return;
+ return "<empty>";
}
+ std::string dump;
bool first = true;
Region::const_iterator cur = region.begin();
Region::const_iterator const tail = region.end();
@@ -207,6 +236,37 @@
dump += StringPrintf("[%d,%d][%d,%d]", cur->left, cur->top, cur->right, cur->bottom);
cur++;
}
+ return dump;
+}
+
+static std::string dumpQueue(const std::deque<DispatchEntry*>& queue, nsecs_t currentTime) {
+ constexpr size_t maxEntries = 50; // max events to print
+ constexpr size_t skipBegin = maxEntries / 2;
+ const size_t skipEnd = queue.size() - maxEntries / 2;
+ // skip from maxEntries / 2 ... size() - maxEntries/2
+ // only print from 0 .. skipBegin and then from skipEnd .. size()
+
+ std::string dump;
+ for (size_t i = 0; i < queue.size(); i++) {
+ const DispatchEntry& entry = *queue[i];
+ if (i >= skipBegin && i < skipEnd) {
+ dump += StringPrintf(INDENT4 "<skipped %zu entries>\n", skipEnd - skipBegin);
+ i = skipEnd - 1; // it will be incremented to "skipEnd" by 'continue'
+ continue;
+ }
+ dump.append(INDENT4);
+ dump += entry.eventEntry->getDescription();
+ dump += StringPrintf(", seq=%" PRIu32
+ ", targetFlags=0x%08x, resolvedAction=%d, age=%" PRId64 "ms",
+ entry.seq, entry.targetFlags, entry.resolvedAction,
+ ns2ms(currentTime - entry.eventEntry->eventTime));
+ if (entry.deliveryTime != 0) {
+ // This entry was delivered, so add information on how long we've been waiting
+ dump += StringPrintf(", wait=%" PRId64 "ms", ns2ms(currentTime - entry.deliveryTime));
+ }
+ dump.append("\n");
+ }
+ return dump;
}
/**
@@ -256,63 +316,79 @@
return first->getToken() == second->getToken();
}
+static bool haveSameApplicationToken(const InputWindowInfo* first, const InputWindowInfo* second) {
+ if (first == nullptr || second == nullptr) {
+ return false;
+ }
+ return first->applicationInfo.token != nullptr &&
+ first->applicationInfo.token == second->applicationInfo.token;
+}
+
static bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) {
return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT;
}
static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
- EventEntry* eventEntry,
+ std::shared_ptr<EventEntry> eventEntry,
int32_t inputTargetFlags) {
- if (inputTarget.useDefaultPointerInfo()) {
- const PointerInfo& pointerInfo = inputTarget.getDefaultPointerInfo();
- return std::make_unique<DispatchEntry>(eventEntry, // increments ref
- inputTargetFlags, pointerInfo.xOffset,
- pointerInfo.yOffset, inputTarget.globalScaleFactor,
- pointerInfo.windowXScale, pointerInfo.windowYScale);
+ if (eventEntry->type == EventEntry::Type::MOTION) {
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
+ if ((motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) == 0) {
+ const ui::Transform identityTransform;
+ // Use identity transform for events that are not pointer events because their axes
+ // values do not represent on-screen coordinates, so they should not have any window
+ // transformations applied to them.
+ return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, identityTransform,
+ 1.0f /*globalScaleFactor*/);
+ }
+ }
+
+ if (inputTarget.useDefaultPointerTransform()) {
+ const ui::Transform& transform = inputTarget.getDefaultPointerTransform();
+ return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform,
+ inputTarget.globalScaleFactor);
}
ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION);
const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
- PointerCoords pointerCoords[motionEntry.pointerCount];
+ std::vector<PointerCoords> pointerCoords;
+ pointerCoords.resize(motionEntry.pointerCount);
// Use the first pointer information to normalize all other pointers. This could be any pointer
// as long as all other pointers are normalized to the same value and the final DispatchEntry
- // uses the offset and scale for the normalized pointer.
- const PointerInfo& firstPointerInfo =
- inputTarget.pointerInfos[inputTarget.pointerIds.firstMarkedBit()];
+ // uses the transform for the normalized pointer.
+ const ui::Transform& firstPointerTransform =
+ inputTarget.pointerTransforms[inputTarget.pointerIds.firstMarkedBit()];
+ ui::Transform inverseFirstTransform = firstPointerTransform.inverse();
// Iterate through all pointers in the event to normalize against the first.
for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount; pointerIndex++) {
const PointerProperties& pointerProperties = motionEntry.pointerProperties[pointerIndex];
uint32_t pointerId = uint32_t(pointerProperties.id);
- const PointerInfo& currPointerInfo = inputTarget.pointerInfos[pointerId];
-
- // The scale factor is the ratio of the current pointers scale to the normalized scale.
- float scaleXDiff = currPointerInfo.windowXScale / firstPointerInfo.windowXScale;
- float scaleYDiff = currPointerInfo.windowYScale / firstPointerInfo.windowYScale;
+ const ui::Transform& currTransform = inputTarget.pointerTransforms[pointerId];
pointerCoords[pointerIndex].copyFrom(motionEntry.pointerCoords[pointerIndex]);
- // First apply the current pointers offset to set the window at 0,0
- pointerCoords[pointerIndex].applyOffset(currPointerInfo.xOffset, currPointerInfo.yOffset);
- // Next scale the coordinates.
- pointerCoords[pointerIndex].scale(1, scaleXDiff, scaleYDiff);
- // Lastly, offset the coordinates so they're in the normalized pointer's frame.
- pointerCoords[pointerIndex].applyOffset(-firstPointerInfo.xOffset,
- -firstPointerInfo.yOffset);
+ // First, apply the current pointer's transform to update the coordinates into
+ // window space.
+ pointerCoords[pointerIndex].transform(currTransform);
+ // Next, apply the inverse transform of the normalized coordinates so the
+ // current coordinates are transformed into the normalized coordinate space.
+ pointerCoords[pointerIndex].transform(inverseFirstTransform);
}
- MotionEntry* combinedMotionEntry =
- new MotionEntry(motionEntry.id, motionEntry.eventTime, motionEntry.deviceId,
- motionEntry.source, motionEntry.displayId, motionEntry.policyFlags,
- motionEntry.action, motionEntry.actionButton, motionEntry.flags,
- motionEntry.metaState, motionEntry.buttonState,
- motionEntry.classification, motionEntry.edgeFlags,
- motionEntry.xPrecision, motionEntry.yPrecision,
- motionEntry.xCursorPosition, motionEntry.yCursorPosition,
- motionEntry.downTime, motionEntry.pointerCount,
- motionEntry.pointerProperties, pointerCoords, 0 /* xOffset */,
- 0 /* yOffset */);
+ std::unique_ptr<MotionEntry> combinedMotionEntry =
+ std::make_unique<MotionEntry>(motionEntry.id, motionEntry.eventTime,
+ motionEntry.deviceId, motionEntry.source,
+ motionEntry.displayId, motionEntry.policyFlags,
+ motionEntry.action, motionEntry.actionButton,
+ motionEntry.flags, motionEntry.metaState,
+ motionEntry.buttonState, motionEntry.classification,
+ motionEntry.edgeFlags, motionEntry.xPrecision,
+ motionEntry.yPrecision, motionEntry.xCursorPosition,
+ motionEntry.yCursorPosition, motionEntry.downTime,
+ motionEntry.pointerCount, motionEntry.pointerProperties,
+ pointerCoords.data(), 0 /* xOffset */, 0 /* yOffset */);
if (motionEntry.injectionState) {
combinedMotionEntry->injectionState = motionEntry.injectionState;
@@ -320,12 +396,8 @@
}
std::unique_ptr<DispatchEntry> dispatchEntry =
- std::make_unique<DispatchEntry>(combinedMotionEntry, // increments ref
- inputTargetFlags, firstPointerInfo.xOffset,
- firstPointerInfo.yOffset, inputTarget.globalScaleFactor,
- firstPointerInfo.windowXScale,
- firstPointerInfo.windowYScale);
- combinedMotionEntry->release();
+ std::make_unique<DispatchEntry>(std::move(combinedMotionEntry), inputTargetFlags,
+ firstPointerTransform, inputTarget.globalScaleFactor);
return dispatchEntry;
}
@@ -341,51 +413,56 @@
}
}
-static std::array<uint8_t, 128> getRandomKey() {
- std::array<uint8_t, 128> key;
- if (RAND_bytes(key.data(), key.size()) != 1) {
- LOG_ALWAYS_FATAL("Can't generate HMAC key");
- }
- return key;
+static status_t openInputChannelPair(const std::string& name,
+ std::shared_ptr<InputChannel>& serverChannel,
+ std::unique_ptr<InputChannel>& clientChannel) {
+ std::unique_ptr<InputChannel> uniqueServerChannel;
+ status_t result = InputChannel::openInputChannelPair(name, uniqueServerChannel, clientChannel);
+
+ serverChannel = std::move(uniqueServerChannel);
+ return result;
}
-// --- HmacKeyManager ---
-
-HmacKeyManager::HmacKeyManager() : mHmacKey(getRandomKey()) {}
-
-std::array<uint8_t, 32> HmacKeyManager::sign(const VerifiedInputEvent& event) const {
- size_t size;
- switch (event.type) {
- case VerifiedInputEvent::Type::KEY: {
- size = sizeof(VerifiedKeyEvent);
- break;
- }
- case VerifiedInputEvent::Type::MOTION: {
- size = sizeof(VerifiedMotionEvent);
- break;
- }
+template <typename T>
+static bool sharedPointersEqual(const std::shared_ptr<T>& lhs, const std::shared_ptr<T>& rhs) {
+ if (lhs == nullptr && rhs == nullptr) {
+ return true;
}
- const uint8_t* start = reinterpret_cast<const uint8_t*>(&event);
- return sign(start, size);
+ if (lhs == nullptr || rhs == nullptr) {
+ return false;
+ }
+ return *lhs == *rhs;
}
-std::array<uint8_t, 32> HmacKeyManager::sign(const uint8_t* data, size_t size) const {
- // SHA256 always generates 32-bytes result
- std::array<uint8_t, 32> hash;
- unsigned int hashLen = 0;
- uint8_t* result =
- HMAC(EVP_sha256(), mHmacKey.data(), mHmacKey.size(), data, size, hash.data(), &hashLen);
- if (result == nullptr) {
- ALOGE("Could not sign the data using HMAC");
- return INVALID_HMAC;
+static sp<IPlatformCompatNative> getCompatService() {
+ sp<IBinder> service(defaultServiceManager()->getService(String16("platform_compat_native")));
+ if (service == nullptr) {
+ ALOGE("Failed to link to compat service");
+ return nullptr;
}
+ return interface_cast<IPlatformCompatNative>(service);
+}
- if (hashLen != hash.size()) {
- ALOGE("HMAC-SHA256 has unexpected length");
- return INVALID_HMAC;
+static KeyEvent createKeyEvent(const KeyEntry& entry) {
+ KeyEvent event;
+ event.initialize(entry.id, entry.deviceId, entry.source, entry.displayId, INVALID_HMAC,
+ entry.action, entry.flags, entry.keyCode, entry.scanCode, entry.metaState,
+ entry.repeatCount, entry.downTime, entry.eventTime);
+ return event;
+}
+
+static std::optional<int32_t> findMonitorPidByToken(
+ const std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay,
+ const sp<IBinder>& token) {
+ for (const auto& it : monitorsByDisplay) {
+ const std::vector<Monitor>& monitors = it.second;
+ for (const Monitor& monitor : monitors) {
+ if (monitor.inputChannel->getConnectionToken() == token) {
+ return monitor.pid;
+ }
+ }
}
-
- return hash;
+ return std::nullopt;
}
// --- InputDispatcher ---
@@ -405,7 +482,11 @@
// To avoid leaking stack in case that call never comes, and for tests,
// initialize it here anyways.
mInTouchMode(true),
- mFocusedDisplayId(ADISPLAY_ID_DEFAULT) {
+ mMaximumObscuringOpacityForTouch(1.0f),
+ mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
+ mFocusedWindowRequestedPointerCapture(false),
+ mWindowTokenWithPointerCapture(nullptr),
+ mCompatService(getCompatService()) {
mLooper = new Looper(false);
mReporter = createInputReporter();
@@ -425,7 +506,7 @@
while (!mConnectionsByFd.empty()) {
sp<Connection> connection = mConnectionsByFd.begin()->second;
- unregisterInputChannel(connection->inputChannel);
+ removeInputChannel(connection->inputChannel->getConnectionToken());
}
}
@@ -491,7 +572,7 @@
*/
void InputDispatcher::processNoFocusedWindowAnrLocked() {
// Check if the application that we are waiting for is still focused.
- sp<InputApplicationHandle> focusedApplication =
+ std::shared_ptr<InputApplicationHandle> focusedApplication =
getValueByKey(mFocusedApplicationHandlesByDisplay, mAwaitedApplicationDisplayId);
if (focusedApplication == nullptr ||
focusedApplication->getApplicationToken() !=
@@ -522,13 +603,11 @@
if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) {
if (currentTime >= *mNoFocusedWindowTimeoutTime) {
processNoFocusedWindowAnrLocked();
- mAwaitedFocusedApplication.clear();
+ mAwaitedFocusedApplication.reset();
mNoFocusedWindowTimeoutTime = std::nullopt;
return LONG_LONG_MIN;
} else {
- // Keep waiting
- const nsecs_t millisRemaining = ns2ms(*mNoFocusedWindowTimeoutTime - currentTime);
- ALOGW("Still no focused window. Will drop the event in %" PRId64 "ms", millisRemaining);
+ // Keep waiting. We will drop the event when mNoFocusedWindowTimeoutTime comes.
nextAnrCheck = *mNoFocusedWindowTimeoutTime;
}
}
@@ -548,16 +627,16 @@
connection->responsive = false;
// Stop waking up for this unresponsive connection
mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
- onAnrLocked(*connection);
+ onAnrLocked(connection);
return LONG_LONG_MIN;
}
-nsecs_t InputDispatcher::getDispatchingTimeoutLocked(const sp<IBinder>& token) {
+std::chrono::nanoseconds InputDispatcher::getDispatchingTimeoutLocked(const sp<IBinder>& token) {
sp<InputWindowHandle> window = getWindowHandleLocked(token);
if (window != nullptr) {
- return window->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT).count();
+ return window->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
}
- return DEFAULT_INPUT_DISPATCHING_TIMEOUT.count();
+ return DEFAULT_INPUT_DISPATCHING_TIMEOUT;
}
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
@@ -642,60 +721,96 @@
switch (mPendingEvent->type) {
case EventEntry::Type::CONFIGURATION_CHANGED: {
- ConfigurationChangedEntry* typedEntry =
- static_cast<ConfigurationChangedEntry*>(mPendingEvent);
+ const ConfigurationChangedEntry& typedEntry =
+ static_cast<const ConfigurationChangedEntry&>(*mPendingEvent);
done = dispatchConfigurationChangedLocked(currentTime, typedEntry);
dropReason = DropReason::NOT_DROPPED; // configuration changes are never dropped
break;
}
case EventEntry::Type::DEVICE_RESET: {
- DeviceResetEntry* typedEntry = static_cast<DeviceResetEntry*>(mPendingEvent);
+ const DeviceResetEntry& typedEntry =
+ static_cast<const DeviceResetEntry&>(*mPendingEvent);
done = dispatchDeviceResetLocked(currentTime, typedEntry);
dropReason = DropReason::NOT_DROPPED; // device resets are never dropped
break;
}
case EventEntry::Type::FOCUS: {
- FocusEntry* typedEntry = static_cast<FocusEntry*>(mPendingEvent);
+ std::shared_ptr<FocusEntry> typedEntry =
+ std::static_pointer_cast<FocusEntry>(mPendingEvent);
dispatchFocusLocked(currentTime, typedEntry);
done = true;
dropReason = DropReason::NOT_DROPPED; // focus events are never dropped
break;
}
+ case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+ const auto typedEntry =
+ std::static_pointer_cast<PointerCaptureChangedEntry>(mPendingEvent);
+ dispatchPointerCaptureChangedLocked(currentTime, typedEntry, dropReason);
+ done = true;
+ break;
+ }
+
+ case EventEntry::Type::DRAG: {
+ std::shared_ptr<DragEntry> typedEntry =
+ std::static_pointer_cast<DragEntry>(mPendingEvent);
+ dispatchDragLocked(currentTime, typedEntry);
+ done = true;
+ break;
+ }
+
case EventEntry::Type::KEY: {
- KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
+ std::shared_ptr<KeyEntry> keyEntry = std::static_pointer_cast<KeyEntry>(mPendingEvent);
if (isAppSwitchDue) {
- if (isAppSwitchKeyEvent(*typedEntry)) {
+ if (isAppSwitchKeyEvent(*keyEntry)) {
resetPendingAppSwitchLocked(true);
isAppSwitchDue = false;
} else if (dropReason == DropReason::NOT_DROPPED) {
dropReason = DropReason::APP_SWITCH;
}
}
- if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *typedEntry)) {
+ if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *keyEntry)) {
dropReason = DropReason::STALE;
}
if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {
dropReason = DropReason::BLOCKED;
}
- done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
+ done = dispatchKeyLocked(currentTime, keyEntry, &dropReason, nextWakeupTime);
break;
}
case EventEntry::Type::MOTION: {
- MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
+ std::shared_ptr<MotionEntry> motionEntry =
+ std::static_pointer_cast<MotionEntry>(mPendingEvent);
if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) {
dropReason = DropReason::APP_SWITCH;
}
- if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *typedEntry)) {
+ if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *motionEntry)) {
dropReason = DropReason::STALE;
}
if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {
dropReason = DropReason::BLOCKED;
}
- done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
+ done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime);
+ break;
+ }
+
+ case EventEntry::Type::SENSOR: {
+ std::shared_ptr<SensorEntry> sensorEntry =
+ std::static_pointer_cast<SensorEntry>(mPendingEvent);
+ if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) {
+ dropReason = DropReason::APP_SWITCH;
+ }
+ // Sensor timestamps use SYSTEM_TIME_BOOTTIME time base, so we can't use
+ // 'currentTime' here, get SYSTEM_TIME_BOOTTIME instead.
+ nsecs_t bootTime = systemTime(SYSTEM_TIME_BOOTTIME);
+ if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(bootTime, *sensorEntry)) {
+ dropReason = DropReason::STALE;
+ }
+ dispatchSensorLocked(currentTime, sensorEntry, &dropReason, nextWakeupTime);
+ done = true;
break;
}
}
@@ -771,17 +886,18 @@
return false;
}
-bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
+bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newEntry) {
bool needWake = mInboundQueue.empty();
- mInboundQueue.push_back(entry);
+ mInboundQueue.push_back(std::move(newEntry));
+ EventEntry& entry = *(mInboundQueue.back());
traceInboundQueueLengthLocked();
- switch (entry->type) {
+ switch (entry.type) {
case EventEntry::Type::KEY: {
// Optimize app switch latency.
// If the application takes too long to catch up then we drop all events preceding
// the app switch key.
- const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*entry);
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry);
if (isAppSwitchKeyEvent(keyEntry)) {
if (keyEntry.action == AKEY_EVENT_ACTION_DOWN) {
mAppSwitchSawKeyDown = true;
@@ -800,8 +916,8 @@
}
case EventEntry::Type::MOTION: {
- if (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(*entry))) {
- mNextUnblockedEvent = entry;
+ if (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(entry))) {
+ mNextUnblockedEvent = mInboundQueue.back();
needWake = true;
}
break;
@@ -811,7 +927,10 @@
break;
}
case EventEntry::Type::CONFIGURATION_CHANGED:
- case EventEntry::Type::DEVICE_RESET: {
+ case EventEntry::Type::DEVICE_RESET:
+ case EventEntry::Type::SENSOR:
+ case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+ case EventEntry::Type::DRAG: {
// nothing to do
break;
}
@@ -820,11 +939,12 @@
return needWake;
}
-void InputDispatcher::addRecentEventLocked(EventEntry* entry) {
- entry->refCount += 1;
- mRecentQueue.push_back(entry);
+void InputDispatcher::addRecentEventLocked(std::shared_ptr<EventEntry> entry) {
+ // Do not store sensor event in recent queue to avoid flooding the queue.
+ if (entry->type != EventEntry::Type::SENSOR) {
+ mRecentQueue.push_back(entry);
+ }
if (mRecentQueue.size() > RECENT_QUEUE_MAX_SIZE) {
- mRecentQueue.front()->release();
mRecentQueue.pop_front();
}
}
@@ -832,23 +952,26 @@
sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,
int32_t y, TouchState* touchState,
bool addOutsideTargets,
- bool addPortalWindows) {
+ bool addPortalWindows,
+ bool ignoreDragWindow) {
if ((addPortalWindows || addOutsideTargets) && touchState == nullptr) {
LOG_ALWAYS_FATAL(
"Must provide a valid touch state if adding portal windows or outside targets");
}
// Traverse windows from front to back to find touched window.
- const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
+ const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
+ if (ignoreDragWindow && haveSameToken(windowHandle, mDragState->dragWindow)) {
+ continue;
+ }
const InputWindowInfo* windowInfo = windowHandle->getInfo();
if (windowInfo->displayId == displayId) {
- int32_t flags = windowInfo->layoutParamsFlags;
+ auto flags = windowInfo->flags;
if (windowInfo->visible) {
- if (!(flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) {
- bool isTouchModal = (flags &
- (InputWindowInfo::FLAG_NOT_FOCUSABLE |
- InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
+ if (!flags.test(InputWindowInfo::Flag::NOT_TOUCHABLE)) {
+ bool isTouchModal = !flags.test(InputWindowInfo::Flag::NOT_FOCUSABLE) &&
+ !flags.test(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
int32_t portalToDisplayId = windowInfo->portalToDisplayId;
if (portalToDisplayId != ADISPLAY_ID_NONE &&
@@ -865,7 +988,7 @@
}
}
- if (addOutsideTargets && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
+ if (addOutsideTargets && flags.test(InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH)) {
touchState->addOrUpdateWindow(windowHandle,
InputTarget::FLAG_DISPATCH_AS_OUTSIDE,
BitSet32(0));
@@ -920,6 +1043,10 @@
ALOGI("Dropped event because it is stale.");
reason = "inbound event was dropped because it is stale";
break;
+ case DropReason::NO_POINTER_CAPTURE:
+ ALOGI("Dropped event because there is no window with Pointer Capture.");
+ reason = "inbound event was dropped because there is no window with Pointer Capture";
+ break;
case DropReason::NOT_DROPPED: {
LOG_ALWAYS_FATAL("Should not be dropping a NOT_DROPPED event");
return;
@@ -943,10 +1070,17 @@
}
break;
}
+ case EventEntry::Type::SENSOR: {
+ break;
+ }
+ case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+ case EventEntry::Type::DRAG: {
+ break;
+ }
case EventEntry::Type::FOCUS:
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET: {
- LOG_ALWAYS_FATAL("Should not drop %s events", EventEntry::typeToString(entry.type));
+ LOG_ALWAYS_FATAL("Should not drop %s events", NamedEnum::string(entry.type).c_str());
break;
}
}
@@ -1005,7 +1139,7 @@
void InputDispatcher::drainInboundQueueLocked() {
while (!mInboundQueue.empty()) {
- EventEntry* entry = mInboundQueue.front();
+ std::shared_ptr<EventEntry> entry = mInboundQueue.front();
mInboundQueue.pop_front();
releaseInboundEventLocked(entry);
}
@@ -1019,66 +1153,48 @@
}
}
-void InputDispatcher::releaseInboundEventLocked(EventEntry* entry) {
+void InputDispatcher::releaseInboundEventLocked(std::shared_ptr<EventEntry> entry) {
InjectionState* injectionState = entry->injectionState;
- if (injectionState && injectionState->injectionResult == INPUT_EVENT_INJECTION_PENDING) {
+ if (injectionState && injectionState->injectionResult == InputEventInjectionResult::PENDING) {
#if DEBUG_DISPATCH_CYCLE
ALOGD("Injected inbound event was dropped.");
#endif
- setInjectionResult(entry, INPUT_EVENT_INJECTION_FAILED);
+ setInjectionResult(*entry, InputEventInjectionResult::FAILED);
}
if (entry == mNextUnblockedEvent) {
mNextUnblockedEvent = nullptr;
}
addRecentEventLocked(entry);
- entry->release();
}
void InputDispatcher::resetKeyRepeatLocked() {
if (mKeyRepeatState.lastKeyEntry) {
- mKeyRepeatState.lastKeyEntry->release();
mKeyRepeatState.lastKeyEntry = nullptr;
}
}
-KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t currentTime) {
- KeyEntry* entry = mKeyRepeatState.lastKeyEntry;
+std::shared_ptr<KeyEntry> InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t currentTime) {
+ std::shared_ptr<KeyEntry> entry = mKeyRepeatState.lastKeyEntry;
- // Reuse the repeated key entry if it is otherwise unreferenced.
uint32_t policyFlags = entry->policyFlags &
(POLICY_FLAG_RAW_MASK | POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_TRUSTED);
- if (entry->refCount == 1) {
- entry->recycle();
- entry->id = mIdGenerator.nextId();
- entry->eventTime = currentTime;
- entry->policyFlags = policyFlags;
- entry->repeatCount += 1;
- } else {
- KeyEntry* newEntry =
- new KeyEntry(mIdGenerator.nextId(), currentTime, entry->deviceId, entry->source,
- entry->displayId, policyFlags, entry->action, entry->flags,
- entry->keyCode, entry->scanCode, entry->metaState,
- entry->repeatCount + 1, entry->downTime);
- mKeyRepeatState.lastKeyEntry = newEntry;
- entry->release();
+ std::shared_ptr<KeyEntry> newEntry =
+ std::make_unique<KeyEntry>(mIdGenerator.nextId(), currentTime, entry->deviceId,
+ entry->source, entry->displayId, policyFlags, entry->action,
+ entry->flags, entry->keyCode, entry->scanCode,
+ entry->metaState, entry->repeatCount + 1, entry->downTime);
- entry = newEntry;
- }
- entry->syntheticRepeat = true;
-
- // Increment reference count since we keep a reference to the event in
- // mKeyRepeatState.lastKeyEntry in addition to the one we return.
- entry->refCount += 1;
-
+ newEntry->syntheticRepeat = true;
+ mKeyRepeatState.lastKeyEntry = newEntry;
mKeyRepeatState.nextRepeatTime = currentTime + mConfig.keyRepeatDelay;
- return entry;
+ return newEntry;
}
bool InputDispatcher::dispatchConfigurationChangedLocked(nsecs_t currentTime,
- ConfigurationChangedEntry* entry) {
+ const ConfigurationChangedEntry& entry) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("dispatchConfigurationChanged - eventTime=%" PRId64, entry->eventTime);
+ ALOGD("dispatchConfigurationChanged - eventTime=%" PRId64, entry.eventTime);
#endif
// Reset key repeating in case a keyboard device was added or removed or something.
@@ -1087,24 +1203,26 @@
// Enqueue a command to run outside the lock to tell the policy that the configuration changed.
std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
&InputDispatcher::doNotifyConfigurationChangedLockedInterruptible);
- commandEntry->eventTime = entry->eventTime;
+ commandEntry->eventTime = entry.eventTime;
postCommandLocked(std::move(commandEntry));
return true;
}
-bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime, DeviceResetEntry* entry) {
+bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime,
+ const DeviceResetEntry& entry) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("dispatchDeviceReset - eventTime=%" PRId64 ", deviceId=%d", entry->eventTime,
- entry->deviceId);
+ ALOGD("dispatchDeviceReset - eventTime=%" PRId64 ", deviceId=%d", entry.eventTime,
+ entry.deviceId);
#endif
CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, "device was reset");
- options.deviceId = entry->deviceId;
+ options.deviceId = entry.deviceId;
synthesizeCancelationEventsForAllConnectionsLocked(options);
return true;
}
-void InputDispatcher::enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus) {
+void InputDispatcher::enqueueFocusEventLocked(const sp<IBinder>& windowToken, bool hasFocus,
+ const std::string& reason) {
if (mPendingEvent != nullptr) {
// Move the pending event to the front of the queue. This will give the chance
// for the pending event to get dispatched to the newly focused window
@@ -1112,21 +1230,24 @@
mPendingEvent = nullptr;
}
- FocusEntry* focusEntry =
- new FocusEntry(mIdGenerator.nextId(), now(), window.getToken(), hasFocus);
+ std::unique_ptr<FocusEntry> focusEntry =
+ std::make_unique<FocusEntry>(mIdGenerator.nextId(), now(), windowToken, hasFocus,
+ reason);
// This event should go to the front of the queue, but behind all other focus events
// Find the last focus event, and insert right after it
- std::deque<EventEntry*>::reverse_iterator it =
+ std::deque<std::shared_ptr<EventEntry>>::reverse_iterator it =
std::find_if(mInboundQueue.rbegin(), mInboundQueue.rend(),
- [](EventEntry* event) { return event->type == EventEntry::Type::FOCUS; });
+ [](const std::shared_ptr<EventEntry>& event) {
+ return event->type == EventEntry::Type::FOCUS;
+ });
// Maintain the order of focus events. Insert the entry after all other focus events.
- mInboundQueue.insert(it.base(), focusEntry);
+ mInboundQueue.insert(it.base(), std::move(focusEntry));
}
-void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, FocusEntry* entry) {
- sp<InputChannel> channel = getInputChannelLocked(entry->connectionToken);
+void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, std::shared_ptr<FocusEntry> entry) {
+ std::shared_ptr<InputChannel> channel = getInputChannelLocked(entry->connectionToken);
if (channel == nullptr) {
return; // Window has gone away
}
@@ -1134,11 +1255,70 @@
target.inputChannel = channel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
entry->dispatchInProgress = true;
-
+ std::string message = std::string("Focus ") + (entry->hasFocus ? "entering " : "leaving ") +
+ channel->getName();
+ std::string reason = std::string("reason=").append(entry->reason);
+ android_log_event_list(LOGTAG_INPUT_FOCUS) << message << reason << LOG_ID_EVENTS;
dispatchEventLocked(currentTime, entry, {target});
}
-bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
+void InputDispatcher::dispatchPointerCaptureChangedLocked(
+ nsecs_t currentTime, const std::shared_ptr<PointerCaptureChangedEntry>& entry,
+ DropReason& dropReason) {
+ const bool haveWindowWithPointerCapture = mWindowTokenWithPointerCapture != nullptr;
+ if (entry->pointerCaptureEnabled && haveWindowWithPointerCapture) {
+ LOG_ALWAYS_FATAL("Pointer Capture has already been enabled for the window.");
+ }
+ if (!entry->pointerCaptureEnabled && !haveWindowWithPointerCapture) {
+ // Pointer capture was already forcefully disabled because of focus change.
+ dropReason = DropReason::NOT_DROPPED;
+ return;
+ }
+
+ // Set drop reason for early returns
+ dropReason = DropReason::NO_POINTER_CAPTURE;
+
+ sp<IBinder> token;
+ if (entry->pointerCaptureEnabled) {
+ // Enable Pointer Capture
+ if (!mFocusedWindowRequestedPointerCapture) {
+ // This can happen if a window requests capture and immediately releases capture.
+ ALOGW("No window requested Pointer Capture.");
+ return;
+ }
+ token = mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
+ LOG_ALWAYS_FATAL_IF(!token, "Cannot find focused window for Pointer Capture.");
+ mWindowTokenWithPointerCapture = token;
+ } else {
+ // Disable Pointer Capture
+ token = mWindowTokenWithPointerCapture;
+ mWindowTokenWithPointerCapture = nullptr;
+ if (mFocusedWindowRequestedPointerCapture) {
+ mFocusedWindowRequestedPointerCapture = false;
+ setPointerCaptureLocked(false);
+ }
+ }
+
+ auto channel = getInputChannelLocked(token);
+ if (channel == nullptr) {
+ // Window has gone away, clean up Pointer Capture state.
+ mWindowTokenWithPointerCapture = nullptr;
+ if (mFocusedWindowRequestedPointerCapture) {
+ mFocusedWindowRequestedPointerCapture = false;
+ setPointerCaptureLocked(false);
+ }
+ return;
+ }
+ InputTarget target;
+ target.inputChannel = channel;
+ target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+ entry->dispatchInProgress = true;
+ dispatchEventLocked(currentTime, entry, {target});
+
+ dropReason = DropReason::NOT_DROPPED;
+}
+
+bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<KeyEntry> entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) {
// Preprocessing.
if (!entry->dispatchInProgress) {
@@ -1146,11 +1326,17 @@
(entry->policyFlags & POLICY_FLAG_TRUSTED) &&
(!(entry->policyFlags & POLICY_FLAG_DISABLE_KEY_REPEAT))) {
if (mKeyRepeatState.lastKeyEntry &&
- mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
+ mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode &&
// We have seen two identical key downs in a row which indicates that the device
// driver is automatically generating key repeats itself. We take note of the
// repeat here, but we disable our own next key repeat timer since it is clear that
// we will not need to synthesize key repeats ourselves.
+ mKeyRepeatState.lastKeyEntry->deviceId == entry->deviceId) {
+ // Make sure we don't get key down from a different device. If a different
+ // device Id has same key pressed down, the new device Id will replace the
+ // current one to hold the key repeat with repeat count reset.
+ // In the future when got a KEY_UP on the device id, drop it and do not
+ // stop the key repeat on current device.
entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1;
resetKeyRepeatLocked();
mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves
@@ -1160,7 +1346,12 @@
mKeyRepeatState.nextRepeatTime = entry->eventTime + mConfig.keyRepeatTimeout;
}
mKeyRepeatState.lastKeyEntry = entry;
- entry->refCount += 1;
+ } else if (entry->action == AKEY_EVENT_ACTION_UP && mKeyRepeatState.lastKeyEntry &&
+ mKeyRepeatState.lastKeyEntry->deviceId != entry->deviceId) {
+ // The key on device 'deviceId' is still down, do not stop key repeat
+#if DEBUG_INBOUND_EVENT_DETAILS
+ ALOGD("deviceId=%d got KEY_UP as stale", entry->deviceId);
+#endif
} else if (!entry->syntheticRepeat) {
resetKeyRepeatLocked();
}
@@ -1193,14 +1384,11 @@
if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
&InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
- sp<InputWindowHandle> focusedWindowHandle =
- getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(*entry));
- if (focusedWindowHandle != nullptr) {
- commandEntry->inputChannel = getInputChannelLocked(focusedWindowHandle->getToken());
- }
+ sp<IBinder> focusedWindowToken =
+ mFocusResolver.getFocusedWindowToken(getTargetDisplayId(*entry));
+ commandEntry->connectionToken = focusedWindowToken;
commandEntry->keyEntry = entry;
postCommandLocked(std::move(commandEntry));
- entry->refCount += 1;
return false; // wait for the command to run
} else {
entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
@@ -1213,23 +1401,23 @@
// Clean up if dropping the event.
if (*dropReason != DropReason::NOT_DROPPED) {
- setInjectionResult(entry,
- *dropReason == DropReason::POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED
- : INPUT_EVENT_INJECTION_FAILED);
+ setInjectionResult(*entry,
+ *dropReason == DropReason::POLICY ? InputEventInjectionResult::SUCCEEDED
+ : InputEventInjectionResult::FAILED);
mReporter->reportDroppedKey(entry->id);
return true;
}
// Identify targets.
std::vector<InputTarget> inputTargets;
- int32_t injectionResult =
+ InputEventInjectionResult injectionResult =
findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);
- if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
+ if (injectionResult == InputEventInjectionResult::PENDING) {
return false;
}
- setInjectionResult(entry, injectionResult);
- if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+ setInjectionResult(*entry, injectionResult);
+ if (injectionResult != InputEventInjectionResult::SUCCEEDED) {
return true;
}
@@ -1252,7 +1440,52 @@
#endif
}
-bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry,
+void InputDispatcher::doNotifySensorLockedInterruptible(CommandEntry* commandEntry) {
+ mLock.unlock();
+
+ const std::shared_ptr<SensorEntry>& entry = commandEntry->sensorEntry;
+ if (entry->accuracyChanged) {
+ mPolicy->notifySensorAccuracy(entry->deviceId, entry->sensorType, entry->accuracy);
+ }
+ mPolicy->notifySensorEvent(entry->deviceId, entry->sensorType, entry->accuracy,
+ entry->hwTimestamp, entry->values);
+ mLock.lock();
+}
+
+void InputDispatcher::dispatchSensorLocked(nsecs_t currentTime, std::shared_ptr<SensorEntry> entry,
+ DropReason* dropReason, nsecs_t* nextWakeupTime) {
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ ALOGD("notifySensorEvent eventTime=%" PRId64 ", hwTimestamp=%" PRId64 ", deviceId=%d, "
+ "source=0x%x, sensorType=%s",
+ entry->eventTime, entry->hwTimestamp, entry->deviceId, entry->source,
+ NamedEnum::string(entry->sensorType).c_str());
+#endif
+ std::unique_ptr<CommandEntry> commandEntry =
+ std::make_unique<CommandEntry>(&InputDispatcher::doNotifySensorLockedInterruptible);
+ commandEntry->sensorEntry = entry;
+ postCommandLocked(std::move(commandEntry));
+}
+
+bool InputDispatcher::flushSensor(int deviceId, InputDeviceSensorType sensorType) {
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ ALOGD("flushSensor deviceId=%d, sensorType=%s", deviceId,
+ NamedEnum::string(sensorType).c_str());
+#endif
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+
+ for (auto it = mInboundQueue.begin(); it != mInboundQueue.end(); it++) {
+ std::shared_ptr<EventEntry> entry = *it;
+ if (entry->type == EventEntry::Type::SENSOR) {
+ it = mInboundQueue.erase(it);
+ releaseInboundEventLocked(entry);
+ }
+ }
+ }
+ return true;
+}
+
+bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<MotionEntry> entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) {
ATRACE_CALL();
// Preprocessing.
@@ -1264,9 +1497,9 @@
// Clean up if dropping the event.
if (*dropReason != DropReason::NOT_DROPPED) {
- setInjectionResult(entry,
- *dropReason == DropReason::POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED
- : INPUT_EVENT_INJECTION_FAILED);
+ setInjectionResult(*entry,
+ *dropReason == DropReason::POLICY ? InputEventInjectionResult::SUCCEEDED
+ : InputEventInjectionResult::FAILED);
return true;
}
@@ -1276,7 +1509,7 @@
std::vector<InputTarget> inputTargets;
bool conflictingPointerActions = false;
- int32_t injectionResult;
+ InputEventInjectionResult injectionResult;
if (isPointerEvent) {
// Pointer event. (eg. touchscreen)
injectionResult =
@@ -1287,16 +1520,16 @@
injectionResult =
findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);
}
- if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
+ if (injectionResult == InputEventInjectionResult::PENDING) {
return false;
}
- setInjectionResult(entry, injectionResult);
- if (injectionResult == INPUT_EVENT_INJECTION_PERMISSION_DENIED) {
+ setInjectionResult(*entry, injectionResult);
+ if (injectionResult == InputEventInjectionResult::PERMISSION_DENIED) {
ALOGW("Permission denied, dropping the motion (isPointer=%s)", toString(isPointerEvent));
return true;
}
- if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+ if (injectionResult != InputEventInjectionResult::SUCCEEDED) {
CancelationOptions::Mode mode(isPointerEvent
? CancelationOptions::CANCEL_POINTER_EVENTS
: CancelationOptions::CANCEL_NON_POINTER_EVENTS);
@@ -1335,6 +1568,35 @@
return true;
}
+void InputDispatcher::enqueueDragEventLocked(const sp<InputWindowHandle>& windowHandle,
+ bool isExiting, const MotionEntry& motionEntry) {
+ // If the window needs enqueue a drag event, the pointerCount should be 1 and the action should
+ // be AMOTION_EVENT_ACTION_MOVE, that could guarantee the first pointer is always valid.
+ LOG_ALWAYS_FATAL_IF(motionEntry.pointerCount != 1);
+ PointerCoords pointerCoords;
+ pointerCoords.copyFrom(motionEntry.pointerCoords[0]);
+ pointerCoords.transform(windowHandle->getInfo()->transform);
+
+ std::unique_ptr<DragEntry> dragEntry =
+ std::make_unique<DragEntry>(mIdGenerator.nextId(), motionEntry.eventTime,
+ windowHandle->getToken(), isExiting, pointerCoords.getX(),
+ pointerCoords.getY());
+
+ enqueueInboundEventLocked(std::move(dragEntry));
+}
+
+void InputDispatcher::dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<DragEntry> entry) {
+ std::shared_ptr<InputChannel> channel = getInputChannelLocked(entry->connectionToken);
+ if (channel == nullptr) {
+ return; // Window has gone away
+ }
+ InputTarget target;
+ target.inputChannel = channel;
+ target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+ entry->dispatchInProgress = true;
+ dispatchEventLocked(currentTime, entry, {target});
+}
+
void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry& entry) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
@@ -1365,13 +1627,16 @@
#endif
}
-void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* eventEntry,
+void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
+ std::shared_ptr<EventEntry> eventEntry,
const std::vector<InputTarget>& inputTargets) {
ATRACE_CALL();
#if DEBUG_DISPATCH_CYCLE
ALOGD("dispatchEventToCurrentInputTargets");
#endif
+ updateInteractionTokensLocked(*eventEntry, inputTargets);
+
ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true
pokeUserActivityLocked(*eventEntry);
@@ -1413,7 +1678,7 @@
// Reset input target wait timeout.
mNoFocusedWindowTimeoutTime = std::nullopt;
- mAwaitedFocusedApplication.clear();
+ mAwaitedFocusedApplication.reset();
}
/**
@@ -1434,10 +1699,13 @@
displayId = motionEntry.displayId;
break;
}
+ case EventEntry::Type::POINTER_CAPTURE_CHANGED:
case EventEntry::Type::FOCUS:
case EventEntry::Type::CONFIGURATION_CHANGED:
- case EventEntry::Type::DEVICE_RESET: {
- ALOGE("%s events do not have a target display", EventEntry::typeToString(entry.type));
+ case EventEntry::Type::DEVICE_RESET:
+ case EventEntry::Type::SENSOR:
+ case EventEntry::Type::DRAG: {
+ ALOGE("%s events do not have a target display", NamedEnum::string(entry.type).c_str());
return ADISPLAY_ID_NONE;
}
}
@@ -1454,10 +1722,10 @@
if (!mKeyIsWaitingForEventsTimeout.has_value()) {
// Start the timer
- ALOGD("Waiting to send key to %s because there are unprocessed events that may cause "
- "focus to change",
- focusedWindowName);
- mKeyIsWaitingForEventsTimeout = currentTime + KEY_WAITING_FOR_EVENTS_TIMEOUT.count();
+ // Wait to send key because there are unprocessed events that may cause focus to change
+ mKeyIsWaitingForEventsTimeout = currentTime +
+ std::chrono::duration_cast<std::chrono::nanoseconds>(KEY_WAITING_FOR_EVENTS_TIMEOUT)
+ .count();
return true;
}
@@ -1474,16 +1742,14 @@
return false;
}
-int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
- const EventEntry& entry,
- std::vector<InputTarget>& inputTargets,
- nsecs_t* nextWakeupTime) {
+InputEventInjectionResult InputDispatcher::findFocusedWindowTargetsLocked(
+ nsecs_t currentTime, const EventEntry& entry, std::vector<InputTarget>& inputTargets,
+ nsecs_t* nextWakeupTime) {
std::string reason;
int32_t displayId = getTargetDisplayId(entry);
- sp<InputWindowHandle> focusedWindowHandle =
- getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
- sp<InputApplicationHandle> focusedApplicationHandle =
+ sp<InputWindowHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);
+ std::shared_ptr<InputApplicationHandle> focusedApplicationHandle =
getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
// If there is no currently focused window and no focused application
@@ -1491,8 +1757,8 @@
if (focusedWindowHandle == nullptr && focusedApplicationHandle == nullptr) {
ALOGI("Dropping %s event because there is no focused window or focused application in "
"display %" PRId32 ".",
- EventEntry::typeToString(entry.type), displayId);
- return INPUT_EVENT_INJECTION_FAILED;
+ NamedEnum::string(entry.type).c_str(), displayId);
+ return InputEventInjectionResult::FAILED;
}
// Compatibility behavior: raise ANR if there is a focused application, but no focused window.
@@ -1503,24 +1769,24 @@
if (focusedWindowHandle == nullptr && focusedApplicationHandle != nullptr) {
if (!mNoFocusedWindowTimeoutTime.has_value()) {
// We just discovered that there's no focused window. Start the ANR timer
- const nsecs_t timeout = focusedApplicationHandle->getDispatchingTimeout(
- DEFAULT_INPUT_DISPATCHING_TIMEOUT.count());
- mNoFocusedWindowTimeoutTime = currentTime + timeout;
+ std::chrono::nanoseconds timeout = focusedApplicationHandle->getDispatchingTimeout(
+ DEFAULT_INPUT_DISPATCHING_TIMEOUT);
+ mNoFocusedWindowTimeoutTime = currentTime + timeout.count();
mAwaitedFocusedApplication = focusedApplicationHandle;
mAwaitedApplicationDisplayId = displayId;
ALOGW("Waiting because no window has focus but %s may eventually add a "
"window when it finishes starting up. Will wait for %" PRId64 "ms",
- mAwaitedFocusedApplication->getName().c_str(), ns2ms(timeout));
+ mAwaitedFocusedApplication->getName().c_str(), millis(timeout));
*nextWakeupTime = *mNoFocusedWindowTimeoutTime;
- return INPUT_EVENT_INJECTION_PENDING;
+ return InputEventInjectionResult::PENDING;
} else if (currentTime > *mNoFocusedWindowTimeoutTime) {
// Already raised ANR. Drop the event
ALOGE("Dropping %s event because there is no focused window",
- EventEntry::typeToString(entry.type));
- return INPUT_EVENT_INJECTION_FAILED;
+ NamedEnum::string(entry.type).c_str());
+ return InputEventInjectionResult::FAILED;
} else {
// Still waiting for the focused window
- return INPUT_EVENT_INJECTION_PENDING;
+ return InputEventInjectionResult::PENDING;
}
}
@@ -1529,12 +1795,12 @@
// Check permissions.
if (!checkInjectionPermission(focusedWindowHandle, entry.injectionState)) {
- return INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+ return InputEventInjectionResult::PERMISSION_DENIED;
}
if (focusedWindowHandle->getInfo()->paused) {
ALOGI("Waiting because %s is paused", focusedWindowHandle->getName().c_str());
- return INPUT_EVENT_INJECTION_PENDING;
+ return InputEventInjectionResult::PENDING;
}
// If the event is a key event, then we must wait for all previous events to
@@ -1551,7 +1817,7 @@
if (entry.type == EventEntry::Type::KEY) {
if (shouldWaitToSendKeyLocked(currentTime, focusedWindowHandle->getName().c_str())) {
*nextWakeupTime = *mKeyIsWaitingForEventsTimeout;
- return INPUT_EVENT_INJECTION_PENDING;
+ return InputEventInjectionResult::PENDING;
}
}
@@ -1561,7 +1827,7 @@
BitSet32(0), inputTargets);
// Done.
- return INPUT_EVENT_INJECTION_SUCCEEDED;
+ return InputEventInjectionResult::SUCCEEDED;
}
/**
@@ -1590,11 +1856,9 @@
return responsiveMonitors;
}
-int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
- const MotionEntry& entry,
- std::vector<InputTarget>& inputTargets,
- nsecs_t* nextWakeupTime,
- bool* outConflictingPointerActions) {
+InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked(
+ nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets,
+ nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) {
ATRACE_CALL();
enum InjectionPermission {
INJECTION_PERMISSION_UNKNOWN,
@@ -1609,9 +1873,10 @@
int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
// Update the touch state as needed based on the properties of the touch event.
- int32_t injectionResult = INPUT_EVENT_INJECTION_PENDING;
+ InputEventInjectionResult injectionResult = InputEventInjectionResult::PENDING;
InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN;
- sp<InputWindowHandle> newHoverWindowHandle;
+ sp<InputWindowHandle> newHoverWindowHandle(mLastHoverWindowHandle);
+ sp<InputWindowHandle> newTouchedWindowHandle;
// Copy current touch state into tempTouchState.
// This state will be used to update mTouchStatesByDisplay at the end of this function.
@@ -1643,7 +1908,7 @@
"in display %" PRId32,
displayId);
// TODO: test multiple simultaneous input streams.
- injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ injectionResult = InputEventInjectionResult::FAILED;
switchedDevice = false;
wrongDevice = true;
goto Failed;
@@ -1659,7 +1924,7 @@
"in display %" PRId32,
displayId);
// TODO: test multiple simultaneous input streams.
- injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+ injectionResult = InputEventInjectionResult::PERMISSION_DENIED;
switchedDevice = false;
wrongDevice = true;
goto Failed;
@@ -1680,7 +1945,7 @@
y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y));
}
bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
- sp<InputWindowHandle> newTouchedWindowHandle =
+ newTouchedWindowHandle =
findTouchedWindowAtLocked(displayId, x, y, &tempTouchState,
isDown /*addOutsideTargets*/, true /*addPortalWindows*/);
@@ -1711,20 +1976,37 @@
newTouchedWindowHandle = nullptr;
}
+ // Ensure the window has a connection and the connection is responsive
if (newTouchedWindowHandle != nullptr) {
- sp<Connection> connection = getConnectionLocked(newTouchedWindowHandle->getToken());
- if (connection == nullptr) {
- ALOGI("Could not find connection for %s",
- newTouchedWindowHandle->getName().c_str());
- newTouchedWindowHandle = nullptr;
- } else if (!connection->responsive) {
- // don't send the new touch to an unresponsive window
- ALOGW("Unresponsive window %s will not get the new gesture at %" PRIu64,
+ const bool isResponsive = hasResponsiveConnectionLocked(*newTouchedWindowHandle);
+ if (!isResponsive) {
+ ALOGW("%s will not receive the new gesture at %" PRIu64,
newTouchedWindowHandle->getName().c_str(), entry.eventTime);
newTouchedWindowHandle = nullptr;
}
}
+ // Drop events that can't be trusted due to occlusion
+ if (newTouchedWindowHandle != nullptr &&
+ mBlockUntrustedTouchesMode != BlockUntrustedTouchesMode::DISABLED) {
+ TouchOcclusionInfo occlusionInfo =
+ computeTouchOcclusionInfoLocked(newTouchedWindowHandle, x, y);
+ if (!isTouchTrustedLocked(occlusionInfo)) {
+ if (DEBUG_TOUCH_OCCLUSION) {
+ ALOGD("Stack of obscuring windows during untrusted touch (%d, %d):", x, y);
+ for (const auto& log : occlusionInfo.debugInfo) {
+ ALOGD("%s", log.c_str());
+ }
+ }
+ onUntrustedTouchLocked(occlusionInfo.obscuringPackage);
+ if (mBlockUntrustedTouchesMode == BlockUntrustedTouchesMode::BLOCK) {
+ ALOGW("Dropping untrusted touch event due to %s/%d",
+ occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid);
+ newTouchedWindowHandle = nullptr;
+ }
+ }
+ }
+
// Also don't send the new touch event to unresponsive gesture monitors
newGestureMonitors = selectResponsiveMonitorsLocked(newGestureMonitors);
@@ -1732,7 +2014,7 @@
ALOGI("Dropping event because there is no touchable window or gesture monitor at "
"(%d, %d) in display %" PRId32 ".",
x, y, displayId);
- injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ injectionResult = InputEventInjectionResult::FAILED;
goto Failed;
}
@@ -1749,10 +2031,10 @@
}
// Update hover state.
- if (isHoverAction) {
+ if (maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT) {
+ newHoverWindowHandle = nullptr;
+ } else if (isHoverAction) {
newHoverWindowHandle = newTouchedWindowHandle;
- } else if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) {
- newHoverWindowHandle = mLastHoverWindowHandle;
}
// Update the temporary touch state.
@@ -1775,10 +2057,12 @@
"dropped the pointer down event in display %" PRId32,
displayId);
}
- injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ injectionResult = InputEventInjectionResult::FAILED;
goto Failed;
}
+ addDragEventLocked(entry);
+
// Check whether touches should slip outside of the current foreground window.
if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 &&
tempTouchState.isSlippery()) {
@@ -1787,8 +2071,7 @@
sp<InputWindowHandle> oldTouchedWindowHandle =
tempTouchState.getFirstForegroundWindowHandle();
- sp<InputWindowHandle> newTouchedWindowHandle =
- findTouchedWindowAtLocked(displayId, x, y, &tempTouchState);
+ newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState);
if (oldTouchedWindowHandle != newTouchedWindowHandle &&
oldTouchedWindowHandle != nullptr && newTouchedWindowHandle != nullptr) {
if (DEBUG_FOCUS) {
@@ -1813,6 +2096,8 @@
}
if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) {
targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
+ } else if (isWindowObscuredLocked(newTouchedWindowHandle)) {
+ targetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
}
BitSet32 pointerIds;
@@ -1825,8 +2110,11 @@
}
if (newHoverWindowHandle != mLastHoverWindowHandle) {
- // Let the previous window know that the hover sequence is over.
- if (mLastHoverWindowHandle != nullptr) {
+ // Let the previous window know that the hover sequence is over, unless we already did it
+ // when dispatching it as is to newTouchedWindowHandle.
+ if (mLastHoverWindowHandle != nullptr &&
+ (maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT ||
+ mLastHoverWindowHandle != newTouchedWindowHandle)) {
#if DEBUG_HOVER
ALOGD("Sending hover exit event to window %s.",
mLastHoverWindowHandle->getName().c_str());
@@ -1835,8 +2123,11 @@
InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0));
}
- // Let the new window know that the hover sequence is starting.
- if (newHoverWindowHandle != nullptr) {
+ // Let the new window know that the hover sequence is starting, unless we already did it
+ // when dispatching it as is to newTouchedWindowHandle.
+ if (newHoverWindowHandle != nullptr &&
+ (maskedAction != AMOTION_EVENT_ACTION_HOVER_ENTER ||
+ newHoverWindowHandle != newTouchedWindowHandle)) {
#if DEBUG_HOVER
ALOGD("Sending hover enter event to window %s.",
newHoverWindowHandle->getName().c_str());
@@ -1855,7 +2146,7 @@
if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
haveForegroundWindow = true;
if (!checkInjectionPermission(touchedWindow.windowHandle, entry.injectionState)) {
- injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+ injectionResult = InputEventInjectionResult::PERMISSION_DENIED;
injectionPermission = INJECTION_PERMISSION_DENIED;
goto Failed;
}
@@ -1866,7 +2157,7 @@
ALOGI("Dropping event because there is no touched foreground window in display "
"%" PRId32 " or gesture monitor to receive it.",
displayId);
- injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ injectionResult = InputEventInjectionResult::FAILED;
goto Failed;
}
@@ -1904,12 +2195,12 @@
sp<InputWindowHandle> foregroundWindowHandle =
tempTouchState.getFirstForegroundWindowHandle();
if (foregroundWindowHandle && foregroundWindowHandle->getInfo()->hasWallpaper) {
- const std::vector<sp<InputWindowHandle>> windowHandles =
+ const std::vector<sp<InputWindowHandle>>& windowHandles =
getWindowHandlesLocked(displayId);
for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
const InputWindowInfo* info = windowHandle->getInfo();
if (info->displayId == displayId &&
- windowHandle->getInfo()->layoutParamsType == InputWindowInfo::TYPE_WALLPAPER) {
+ windowHandle->getInfo()->type == InputWindowInfo::Type::WALLPAPER) {
tempTouchState
.addOrUpdateWindow(windowHandle,
InputTarget::FLAG_WINDOW_IS_OBSCURED |
@@ -1923,7 +2214,7 @@
}
// Success! Output targets.
- injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
+ injectionResult = InputEventInjectionResult::SUCCEEDED;
for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
@@ -2027,6 +2318,67 @@
return injectionResult;
}
+void InputDispatcher::finishDragAndDrop(int32_t displayId, float x, float y) {
+ const sp<InputWindowHandle> dropWindow =
+ findTouchedWindowAtLocked(displayId, x, y, nullptr /*touchState*/,
+ false /*addOutsideTargets*/, false /*addPortalWindows*/,
+ true /*ignoreDragWindow*/);
+ if (dropWindow) {
+ vec2 local = dropWindow->getInfo()->transform.transform(x, y);
+ notifyDropWindowLocked(dropWindow->getToken(), local.x, local.y);
+ } else {
+ notifyDropWindowLocked(nullptr, 0, 0);
+ }
+ mDragState.reset();
+}
+
+void InputDispatcher::addDragEventLocked(const MotionEntry& entry) {
+ if (entry.pointerCount != 1 || !mDragState) {
+ return;
+ }
+
+ if (!mDragState->isStartDrag) {
+ mDragState->isStartDrag = true;
+ mDragState->isStylusButtonDownAtStart =
+ (entry.buttonState & AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) != 0;
+ }
+
+ int32_t maskedAction = entry.action & AMOTION_EVENT_ACTION_MASK;
+ int32_t x = static_cast<int32_t>(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
+ int32_t y = static_cast<int32_t>(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+ if (maskedAction == AMOTION_EVENT_ACTION_MOVE) {
+ // Handle the special case : stylus button no longer pressed.
+ bool isStylusButtonDown = (entry.buttonState & AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) != 0;
+ if (mDragState->isStylusButtonDownAtStart && !isStylusButtonDown) {
+ finishDragAndDrop(entry.displayId, x, y);
+ return;
+ }
+
+ const sp<InputWindowHandle> hoverWindowHandle =
+ findTouchedWindowAtLocked(entry.displayId, x, y, nullptr /*touchState*/,
+ false /*addOutsideTargets*/, false /*addPortalWindows*/,
+ true /*ignoreDragWindow*/);
+ // enqueue drag exit if needed.
+ if (hoverWindowHandle != mDragState->dragHoverWindowHandle &&
+ !haveSameToken(hoverWindowHandle, mDragState->dragHoverWindowHandle)) {
+ if (mDragState->dragHoverWindowHandle != nullptr) {
+ enqueueDragEventLocked(mDragState->dragHoverWindowHandle, true /*isExiting*/,
+ entry);
+ }
+ mDragState->dragHoverWindowHandle = hoverWindowHandle;
+ }
+ // enqueue drag location if needed.
+ if (hoverWindowHandle != nullptr) {
+ enqueueDragEventLocked(hoverWindowHandle, false /*isExiting*/, entry);
+ }
+ } else if (maskedAction == AMOTION_EVENT_ACTION_UP) {
+ finishDragAndDrop(entry.displayId, x, y);
+ } else if (maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
+ notifyDropWindowLocked(nullptr, 0, 0);
+ mDragState.reset();
+ }
+}
+
void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds,
std::vector<InputTarget>& inputTargets) {
@@ -2041,7 +2393,8 @@
if (it == inputTargets.end()) {
InputTarget inputTarget;
- sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken());
+ std::shared_ptr<InputChannel> inputChannel =
+ getInputChannelLocked(windowHandle->getToken());
if (inputChannel == nullptr) {
ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str());
return;
@@ -2056,8 +2409,7 @@
ALOG_ASSERT(it->flags == targetFlags);
ALOG_ASSERT(it->globalScaleFactor == windowInfo->globalScaleFactor);
- it->addPointers(pointerIds, -windowInfo->frameLeft, -windowInfo->frameTop,
- windowInfo->windowXScale, windowInfo->windowYScale);
+ it->addPointers(pointerIds, windowInfo->transform);
}
void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets,
@@ -2080,7 +2432,9 @@
InputTarget target;
target.inputChannel = monitor.inputChannel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
- target.setDefaultPointerInfo(xOffset, yOffset, 1 /* windowXScale */, 1 /* windowYScale */);
+ ui::Transform t;
+ t.set(xOffset, yOffset);
+ target.setDefaultPointerTransform(t);
inputTargets.push_back(target);
}
@@ -2119,11 +2473,20 @@
auto otherInfo = otherHandle->getInfo();
if (!otherInfo->visible) {
return false;
- } else if (info->ownerPid == otherInfo->ownerPid) {
- // If ownerPid is the same we don't generate occlusion events as there
- // is no in-process security boundary.
+ } else if (otherInfo->alpha == 0 &&
+ otherInfo->flags.test(InputWindowInfo::Flag::NOT_TOUCHABLE)) {
+ // Those act as if they were invisible, so we don't need to flag them.
+ // We do want to potentially flag touchable windows even if they have 0
+ // opacity, since they can consume touches and alter the effects of the
+ // user interaction (eg. apps that rely on
+ // FLAG_WINDOW_IS_PARTIALLY_OBSCURED should still be told about those
+ // windows), hence we also check for FLAG_NOT_TOUCHABLE.
return false;
- } else if (otherInfo->isTrustedOverlay()) {
+ } else if (info->ownerUid == otherInfo->ownerUid) {
+ // If ownerUid is the same we don't generate occlusion events as there
+ // is no security boundary within an uid.
+ return false;
+ } else if (otherInfo->trustedOverlay) {
return false;
} else if (otherInfo->displayId != info->displayId) {
return false;
@@ -2131,16 +2494,120 @@
return true;
}
+/**
+ * Returns touch occlusion information in the form of TouchOcclusionInfo. To check if the touch is
+ * untrusted, one should check:
+ *
+ * 1. If result.hasBlockingOcclusion is true.
+ * If it's, it means the touch should be blocked due to a window with occlusion mode of
+ * BLOCK_UNTRUSTED.
+ *
+ * 2. If result.obscuringOpacity > mMaximumObscuringOpacityForTouch.
+ * If it is (and 1 is false), then the touch should be blocked because a stack of windows
+ * (possibly only one) with occlusion mode of USE_OPACITY from one UID resulted in a composed
+ * obscuring opacity above the threshold. Note that if there was no window of occlusion mode
+ * USE_OPACITY, result.obscuringOpacity would've been 0 and since
+ * mMaximumObscuringOpacityForTouch >= 0, the condition above would never be true.
+ *
+ * If neither of those is true, then it means the touch can be allowed.
+ */
+InputDispatcher::TouchOcclusionInfo InputDispatcher::computeTouchOcclusionInfoLocked(
+ const sp<InputWindowHandle>& windowHandle, int32_t x, int32_t y) const {
+ const InputWindowInfo* windowInfo = windowHandle->getInfo();
+ int32_t displayId = windowInfo->displayId;
+ const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
+ TouchOcclusionInfo info;
+ info.hasBlockingOcclusion = false;
+ info.obscuringOpacity = 0;
+ info.obscuringUid = -1;
+ std::map<int32_t, float> opacityByUid;
+ for (const sp<InputWindowHandle>& otherHandle : windowHandles) {
+ if (windowHandle == otherHandle) {
+ break; // All future windows are below us. Exit early.
+ }
+ const InputWindowInfo* otherInfo = otherHandle->getInfo();
+ if (canBeObscuredBy(windowHandle, otherHandle) && otherInfo->frameContainsPoint(x, y) &&
+ !haveSameApplicationToken(windowInfo, otherInfo)) {
+ if (DEBUG_TOUCH_OCCLUSION) {
+ info.debugInfo.push_back(
+ dumpWindowForTouchOcclusion(otherInfo, /* isTouchedWindow */ false));
+ }
+ // canBeObscuredBy() has returned true above, which means this window is untrusted, so
+ // we perform the checks below to see if the touch can be propagated or not based on the
+ // window's touch occlusion mode
+ if (otherInfo->touchOcclusionMode == TouchOcclusionMode::BLOCK_UNTRUSTED) {
+ info.hasBlockingOcclusion = true;
+ info.obscuringUid = otherInfo->ownerUid;
+ info.obscuringPackage = otherInfo->packageName;
+ break;
+ }
+ if (otherInfo->touchOcclusionMode == TouchOcclusionMode::USE_OPACITY) {
+ uint32_t uid = otherInfo->ownerUid;
+ float opacity =
+ (opacityByUid.find(uid) == opacityByUid.end()) ? 0 : opacityByUid[uid];
+ // Given windows A and B:
+ // opacity(A, B) = 1 - [1 - opacity(A)] * [1 - opacity(B)]
+ opacity = 1 - (1 - opacity) * (1 - otherInfo->alpha);
+ opacityByUid[uid] = opacity;
+ if (opacity > info.obscuringOpacity) {
+ info.obscuringOpacity = opacity;
+ info.obscuringUid = uid;
+ info.obscuringPackage = otherInfo->packageName;
+ }
+ }
+ }
+ }
+ if (DEBUG_TOUCH_OCCLUSION) {
+ info.debugInfo.push_back(
+ dumpWindowForTouchOcclusion(windowInfo, /* isTouchedWindow */ true));
+ }
+ return info;
+}
+
+std::string InputDispatcher::dumpWindowForTouchOcclusion(const InputWindowInfo* info,
+ bool isTouchedWindow) const {
+ return StringPrintf(INDENT2
+ "* %stype=%s, package=%s/%" PRId32 ", id=%" PRId32 ", mode=%s, alpha=%.2f, "
+ "frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32
+ "], touchableRegion=%s, window={%s}, flags={%s}, inputFeatures={%s}, "
+ "hasToken=%s, applicationInfo.name=%s, applicationInfo.token=%s\n",
+ (isTouchedWindow) ? "[TOUCHED] " : "",
+ NamedEnum::string(info->type, "%" PRId32).c_str(),
+ info->packageName.c_str(), info->ownerUid, info->id,
+ toString(info->touchOcclusionMode).c_str(), info->alpha, info->frameLeft,
+ info->frameTop, info->frameRight, info->frameBottom,
+ dumpRegion(info->touchableRegion).c_str(), info->name.c_str(),
+ info->flags.string().c_str(), info->inputFeatures.string().c_str(),
+ toString(info->token != nullptr), info->applicationInfo.name.c_str(),
+ toString(info->applicationInfo.token).c_str());
+}
+
+bool InputDispatcher::isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const {
+ if (occlusionInfo.hasBlockingOcclusion) {
+ ALOGW("Untrusted touch due to occlusion by %s/%d", occlusionInfo.obscuringPackage.c_str(),
+ occlusionInfo.obscuringUid);
+ return false;
+ }
+ if (occlusionInfo.obscuringOpacity > mMaximumObscuringOpacityForTouch) {
+ ALOGW("Untrusted touch due to occlusion by %s/%d (obscuring opacity = "
+ "%.2f, maximum allowed = %.2f)",
+ occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid,
+ occlusionInfo.obscuringOpacity, mMaximumObscuringOpacityForTouch);
+ return false;
+ }
+ return true;
+}
+
bool InputDispatcher::isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle,
int32_t x, int32_t y) const {
int32_t displayId = windowHandle->getInfo()->displayId;
- const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
+ const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
for (const sp<InputWindowHandle>& otherHandle : windowHandles) {
if (windowHandle == otherHandle) {
break; // All future windows are below us. Exit early.
}
const InputWindowInfo* otherInfo = otherHandle->getInfo();
- if (canBeObscuredBy(windowHandle, otherHandle) &&
+ if (canBeObscuredBy(windowHandle, otherHandle) &&
otherInfo->frameContainsPoint(x, y)) {
return true;
}
@@ -2150,13 +2617,12 @@
bool InputDispatcher::isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const {
int32_t displayId = windowHandle->getInfo()->displayId;
- const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
+ const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
const InputWindowInfo* windowInfo = windowHandle->getInfo();
for (const sp<InputWindowHandle>& otherHandle : windowHandles) {
if (windowHandle == otherHandle) {
break; // All future windows are below us. Exit early.
}
-
const InputWindowInfo* otherInfo = otherHandle->getInfo();
if (canBeObscuredBy(windowHandle, otherHandle) &&
otherInfo->overlaps(windowInfo)) {
@@ -2167,7 +2633,7 @@
}
std::string InputDispatcher::getApplicationWindowLabel(
- const sp<InputApplicationHandle>& applicationHandle,
+ const InputApplicationHandle* applicationHandle,
const sp<InputWindowHandle>& windowHandle) {
if (applicationHandle != nullptr) {
if (windowHandle != nullptr) {
@@ -2183,16 +2649,18 @@
}
void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) {
- if (eventEntry.type == EventEntry::Type::FOCUS) {
- // Focus events are passed to apps, but do not represent user activity.
+ if (eventEntry.type == EventEntry::Type::FOCUS ||
+ eventEntry.type == EventEntry::Type::POINTER_CAPTURE_CHANGED ||
+ eventEntry.type == EventEntry::Type::DRAG) {
+ // Focus or pointer capture changed events are passed to apps, but do not represent user
+ // activity.
return;
}
int32_t displayId = getTargetDisplayId(eventEntry);
- sp<InputWindowHandle> focusedWindowHandle =
- getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
+ sp<InputWindowHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);
if (focusedWindowHandle != nullptr) {
const InputWindowInfo* info = focusedWindowHandle->getInfo();
- if (info->inputFeatures & InputWindowInfo::INPUT_FEATURE_DISABLE_USER_ACTIVITY) {
+ if (info->inputFeatures.test(InputWindowInfo::Feature::DISABLE_USER_ACTIVITY)) {
#if DEBUG_DISPATCH_CYCLE
ALOGD("Not poking user activity: disabled by window '%s'.", info->name.c_str());
#endif
@@ -2223,9 +2691,12 @@
}
case EventEntry::Type::FOCUS:
case EventEntry::Type::CONFIGURATION_CHANGED:
- case EventEntry::Type::DEVICE_RESET: {
+ case EventEntry::Type::DEVICE_RESET:
+ case EventEntry::Type::SENSOR:
+ case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+ case EventEntry::Type::DRAG: {
LOG_ALWAYS_FATAL("%s events are not user activity",
- EventEntry::typeToString(eventEntry.type));
+ NamedEnum::string(eventEntry.type).c_str());
break;
}
}
@@ -2234,12 +2705,13 @@
std::make_unique<CommandEntry>(&InputDispatcher::doPokeUserActivityLockedInterruptible);
commandEntry->eventTime = eventEntry.eventTime;
commandEntry->userActivityEventType = eventType;
+ commandEntry->displayId = displayId;
postCommandLocked(std::move(commandEntry));
}
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection,
- EventEntry* eventEntry,
+ std::shared_ptr<EventEntry> eventEntry,
const InputTarget& inputTarget) {
if (ATRACE_ENABLED()) {
std::string message =
@@ -2269,11 +2741,11 @@
if (inputTarget.flags & InputTarget::FLAG_SPLIT) {
LOG_ALWAYS_FATAL_IF(eventEntry->type != EventEntry::Type::MOTION,
"Entry type %s should not have FLAG_SPLIT",
- EventEntry::typeToString(eventEntry->type));
+ NamedEnum::string(eventEntry->type).c_str());
const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry);
if (inputTarget.pointerIds.count() != originalMotionEntry.pointerCount) {
- MotionEntry* splitMotionEntry =
+ std::unique_ptr<MotionEntry> splitMotionEntry =
splitMotionEvent(originalMotionEntry, inputTarget.pointerIds);
if (!splitMotionEntry) {
return; // split event was dropped
@@ -2283,8 +2755,8 @@
connection->getInputChannelName().c_str());
logOutboundMotionDetails(" ", *splitMotionEntry);
}
- enqueueDispatchEntriesLocked(currentTime, connection, splitMotionEntry, inputTarget);
- splitMotionEntry->release();
+ enqueueDispatchEntriesLocked(currentTime, connection, std::move(splitMotionEntry),
+ inputTarget);
return;
}
}
@@ -2295,7 +2767,7 @@
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
const sp<Connection>& connection,
- EventEntry* eventEntry,
+ std::shared_ptr<EventEntry> eventEntry,
const InputTarget& inputTarget) {
if (ATRACE_ENABLED()) {
std::string message =
@@ -2327,7 +2799,7 @@
}
void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connection,
- EventEntry* eventEntry,
+ std::shared_ptr<EventEntry> eventEntry,
const InputTarget& inputTarget,
int32_t dispatchMode) {
if (ATRACE_ENABLED()) {
@@ -2349,11 +2821,11 @@
// Use the eventEntry from dispatchEntry since the entry may have changed and can now be a
// different EventEntry than what was passed in.
- EventEntry* newEntry = dispatchEntry->eventEntry;
+ EventEntry& newEntry = *(dispatchEntry->eventEntry);
// Apply target flags and update the connection's input state.
- switch (newEntry->type) {
+ switch (newEntry.type) {
case EventEntry::Type::KEY: {
- const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*newEntry);
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(newEntry);
dispatchEntry->resolvedEventId = keyEntry.id;
dispatchEntry->resolvedAction = keyEntry.action;
dispatchEntry->resolvedFlags = keyEntry.flags;
@@ -2370,7 +2842,7 @@
}
case EventEntry::Type::MOTION: {
- const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*newEntry);
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(newEntry);
// Assign a default value to dispatchEntry that will never be generated by InputReader,
// and assign a InputDispatcher value if it doesn't change in the if-else chain below.
constexpr int32_t DEFAULT_RESOLVED_EVENT_ID =
@@ -2435,13 +2907,19 @@
break;
}
- case EventEntry::Type::FOCUS: {
+ case EventEntry::Type::FOCUS:
+ case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+ case EventEntry::Type::DRAG: {
+ break;
+ }
+ case EventEntry::Type::SENSOR: {
+ LOG_ALWAYS_FATAL("SENSOR events should not go to apps via input channel");
break;
}
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET: {
LOG_ALWAYS_FATAL("%s events should not go to apps",
- EventEntry::typeToString(newEntry->type));
+ NamedEnum::string(newEntry.type).c_str());
break;
}
}
@@ -2456,31 +2934,88 @@
traceOutboundQueueLength(connection);
}
+/**
+ * This function is purely for debugging. It helps us understand where the user interaction
+ * was taking place. For example, if user is touching launcher, we will see a log that user
+ * started interacting with launcher. In that example, the event would go to the wallpaper as well.
+ * We will see both launcher and wallpaper in that list.
+ * Once the interaction with a particular set of connections starts, no new logs will be printed
+ * until the set of interacted connections changes.
+ *
+ * The following items are skipped, to reduce the logspam:
+ * ACTION_OUTSIDE: any windows that are receiving ACTION_OUTSIDE are not logged
+ * ACTION_UP: any windows that receive ACTION_UP are not logged (for both keys and motions).
+ * This includes situations like the soft BACK button key. When the user releases (lifts up the
+ * finger) the back button, then navigation bar will inject KEYCODE_BACK with ACTION_UP.
+ * Both of those ACTION_UP events would not be logged
+ */
+void InputDispatcher::updateInteractionTokensLocked(const EventEntry& entry,
+ const std::vector<InputTarget>& targets) {
+ // Skip ACTION_UP events, and all events other than keys and motions
+ if (entry.type == EventEntry::Type::KEY) {
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry);
+ if (keyEntry.action == AKEY_EVENT_ACTION_UP) {
+ return;
+ }
+ } else if (entry.type == EventEntry::Type::MOTION) {
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(entry);
+ if (motionEntry.action == AMOTION_EVENT_ACTION_UP ||
+ motionEntry.action == AMOTION_EVENT_ACTION_CANCEL) {
+ return;
+ }
+ } else {
+ return; // Not a key or a motion
+ }
+
+ std::unordered_set<sp<IBinder>, StrongPointerHash<IBinder>> newConnectionTokens;
+ std::vector<sp<Connection>> newConnections;
+ for (const InputTarget& target : targets) {
+ if ((target.flags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) ==
+ InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
+ continue; // Skip windows that receive ACTION_OUTSIDE
+ }
+
+ sp<IBinder> token = target.inputChannel->getConnectionToken();
+ sp<Connection> connection = getConnectionLocked(token);
+ if (connection == nullptr) {
+ continue;
+ }
+ newConnectionTokens.insert(std::move(token));
+ newConnections.emplace_back(connection);
+ }
+ if (newConnectionTokens == mInteractionConnectionTokens) {
+ return; // no change
+ }
+ mInteractionConnectionTokens = newConnectionTokens;
+
+ std::string targetList;
+ for (const sp<Connection>& connection : newConnections) {
+ targetList += connection->getWindowName() + ", ";
+ }
+ std::string message = "Interaction with: " + targetList;
+ if (targetList.empty()) {
+ message += "<none>";
+ }
+ android_log_event_list(LOGTAG_INPUT_INTERACTION) << message << LOG_ID_EVENTS;
+}
+
void InputDispatcher::dispatchPointerDownOutsideFocus(uint32_t source, int32_t action,
- const sp<IBinder>& newToken) {
+ const sp<IBinder>& token) {
int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
uint32_t maskedSource = source & AINPUT_SOURCE_CLASS_MASK;
if (maskedSource != AINPUT_SOURCE_CLASS_POINTER || maskedAction != AMOTION_EVENT_ACTION_DOWN) {
return;
}
- sp<InputWindowHandle> inputWindowHandle = getWindowHandleLocked(newToken);
- if (inputWindowHandle == nullptr) {
- return;
- }
-
- sp<InputWindowHandle> focusedWindowHandle =
- getValueByKey(mFocusedWindowHandlesByDisplay, mFocusedDisplayId);
-
- bool hasFocusChanged = !focusedWindowHandle || focusedWindowHandle->getToken() != newToken;
-
- if (!hasFocusChanged) {
+ sp<IBinder> focusedToken = mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
+ if (focusedToken == token) {
+ // ignore since token is focused
return;
}
std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
&InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible);
- commandEntry->newToken = newToken;
+ commandEntry->newToken = token;
postCommandLocked(std::move(commandEntry));
}
@@ -2498,51 +3033,44 @@
while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) {
DispatchEntry* dispatchEntry = connection->outboundQueue.front();
dispatchEntry->deliveryTime = currentTime;
- const nsecs_t timeout =
+ const std::chrono::nanoseconds timeout =
getDispatchingTimeoutLocked(connection->inputChannel->getConnectionToken());
- dispatchEntry->timeoutTime = currentTime + timeout;
+ dispatchEntry->timeoutTime = currentTime + timeout.count();
// Publish the event.
status_t status;
- EventEntry* eventEntry = dispatchEntry->eventEntry;
- switch (eventEntry->type) {
+ const EventEntry& eventEntry = *(dispatchEntry->eventEntry);
+ switch (eventEntry.type) {
case EventEntry::Type::KEY: {
- const KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
- std::array<uint8_t, 32> hmac = getSignature(*keyEntry, *dispatchEntry);
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(eventEntry);
+ std::array<uint8_t, 32> hmac = getSignature(keyEntry, *dispatchEntry);
// Publish the key event.
- status =
- connection->inputPublisher
- .publishKeyEvent(dispatchEntry->seq, dispatchEntry->resolvedEventId,
- keyEntry->deviceId, keyEntry->source,
- keyEntry->displayId, std::move(hmac),
- dispatchEntry->resolvedAction,
- dispatchEntry->resolvedFlags, keyEntry->keyCode,
- keyEntry->scanCode, keyEntry->metaState,
- keyEntry->repeatCount, keyEntry->downTime,
- keyEntry->eventTime);
+ status = connection->inputPublisher
+ .publishKeyEvent(dispatchEntry->seq,
+ dispatchEntry->resolvedEventId, keyEntry.deviceId,
+ keyEntry.source, keyEntry.displayId,
+ std::move(hmac), dispatchEntry->resolvedAction,
+ dispatchEntry->resolvedFlags, keyEntry.keyCode,
+ keyEntry.scanCode, keyEntry.metaState,
+ keyEntry.repeatCount, keyEntry.downTime,
+ keyEntry.eventTime);
break;
}
case EventEntry::Type::MOTION: {
- MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
PointerCoords scaledCoords[MAX_POINTERS];
- const PointerCoords* usingCoords = motionEntry->pointerCoords;
+ const PointerCoords* usingCoords = motionEntry.pointerCoords;
// Set the X and Y offset and X and Y scale depending on the input source.
- float xOffset = 0.0f, yOffset = 0.0f;
- float xScale = 1.0f, yScale = 1.0f;
- if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) &&
+ if ((motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) &&
!(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
float globalScaleFactor = dispatchEntry->globalScaleFactor;
- xScale = dispatchEntry->windowXScale;
- yScale = dispatchEntry->windowYScale;
- xOffset = dispatchEntry->xOffset * xScale;
- yOffset = dispatchEntry->yOffset * yScale;
if (globalScaleFactor != 1.0f) {
- for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
- scaledCoords[i] = motionEntry->pointerCoords[i];
+ for (uint32_t i = 0; i < motionEntry.pointerCount; i++) {
+ scaledCoords[i] = motionEntry.pointerCoords[i];
// Don't apply window scale here since we don't want scale to affect raw
// coordinates. The scale will be sent back to the client and applied
// later when requesting relative coordinates.
@@ -2554,50 +3082,70 @@
} else {
// We don't want the dispatch target to know.
if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) {
- for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
+ for (uint32_t i = 0; i < motionEntry.pointerCount; i++) {
scaledCoords[i].clear();
}
usingCoords = scaledCoords;
}
}
- std::array<uint8_t, 32> hmac = getSignature(*motionEntry, *dispatchEntry);
+ std::array<uint8_t, 32> hmac = getSignature(motionEntry, *dispatchEntry);
// Publish the motion event.
status = connection->inputPublisher
.publishMotionEvent(dispatchEntry->seq,
dispatchEntry->resolvedEventId,
- motionEntry->deviceId, motionEntry->source,
- motionEntry->displayId, std::move(hmac),
+ motionEntry.deviceId, motionEntry.source,
+ motionEntry.displayId, std::move(hmac),
dispatchEntry->resolvedAction,
- motionEntry->actionButton,
+ motionEntry.actionButton,
dispatchEntry->resolvedFlags,
- motionEntry->edgeFlags, motionEntry->metaState,
- motionEntry->buttonState,
- motionEntry->classification, xScale, yScale,
- xOffset, yOffset, motionEntry->xPrecision,
- motionEntry->yPrecision,
- motionEntry->xCursorPosition,
- motionEntry->yCursorPosition,
- motionEntry->downTime, motionEntry->eventTime,
- motionEntry->pointerCount,
- motionEntry->pointerProperties, usingCoords);
- reportTouchEventForStatistics(*motionEntry);
+ motionEntry.edgeFlags, motionEntry.metaState,
+ motionEntry.buttonState,
+ motionEntry.classification,
+ dispatchEntry->transform,
+ motionEntry.xPrecision, motionEntry.yPrecision,
+ motionEntry.xCursorPosition,
+ motionEntry.yCursorPosition,
+ motionEntry.downTime, motionEntry.eventTime,
+ motionEntry.pointerCount,
+ motionEntry.pointerProperties, usingCoords);
+ reportTouchEventForStatistics(motionEntry);
break;
}
+
case EventEntry::Type::FOCUS: {
- FocusEntry* focusEntry = static_cast<FocusEntry*>(eventEntry);
+ const FocusEntry& focusEntry = static_cast<const FocusEntry&>(eventEntry);
status = connection->inputPublisher.publishFocusEvent(dispatchEntry->seq,
- focusEntry->id,
- focusEntry->hasFocus,
+ focusEntry.id,
+ focusEntry.hasFocus,
mInTouchMode);
break;
}
+ case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+ const auto& captureEntry =
+ static_cast<const PointerCaptureChangedEntry&>(eventEntry);
+ status = connection->inputPublisher
+ .publishCaptureEvent(dispatchEntry->seq, captureEntry.id,
+ captureEntry.pointerCaptureEnabled);
+ break;
+ }
+
+ case EventEntry::Type::DRAG: {
+ const DragEntry& dragEntry = static_cast<const DragEntry&>(eventEntry);
+ status = connection->inputPublisher.publishDragEvent(dispatchEntry->seq,
+ dragEntry.id, dragEntry.x,
+ dragEntry.y,
+ dragEntry.isExiting);
+ break;
+ }
+
case EventEntry::Type::CONFIGURATION_CHANGED:
- case EventEntry::Type::DEVICE_RESET: {
+ case EventEntry::Type::DEVICE_RESET:
+ case EventEntry::Type::SENSOR: {
LOG_ALWAYS_FATAL("Should never start dispatch cycles for %s events",
- EventEntry::typeToString(eventEntry->type));
+ NamedEnum::string(eventEntry.type).c_str());
return;
}
}
@@ -2609,8 +3157,9 @@
ALOGE("channel '%s' ~ Could not publish event because the pipe is full. "
"This is unexpected because the wait queue is empty, so the pipe "
"should be empty and we shouldn't have any problems writing an "
- "event to it, status=%d",
- connection->getInputChannelName().c_str(), status);
+ "event to it, status=%s(%d)",
+ connection->getInputChannelName().c_str(), statusToString(status).c_str(),
+ status);
abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
} else {
// Pipe is full and we are waiting for the app to finish process some events
@@ -2623,8 +3172,9 @@
}
} else {
ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, "
- "status=%d",
- connection->getInputChannelName().c_str(), status);
+ "status=%s(%d)",
+ connection->getInputChannelName().c_str(), statusToString(status).c_str(),
+ status);
abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
}
return;
@@ -2644,6 +3194,22 @@
}
}
+std::array<uint8_t, 32> InputDispatcher::sign(const VerifiedInputEvent& event) const {
+ size_t size;
+ switch (event.type) {
+ case VerifiedInputEvent::Type::KEY: {
+ size = sizeof(VerifiedKeyEvent);
+ break;
+ }
+ case VerifiedInputEvent::Type::MOTION: {
+ size = sizeof(VerifiedMotionEvent);
+ break;
+ }
+ }
+ const uint8_t* start = reinterpret_cast<const uint8_t*>(&event);
+ return mHmacKeyManager.sign(start, size);
+}
+
const std::array<uint8_t, 32> InputDispatcher::getSignature(
const MotionEntry& motionEntry, const DispatchEntry& dispatchEntry) const {
int32_t actionMasked = dispatchEntry.resolvedAction & AMOTION_EVENT_ACTION_MASK;
@@ -2653,7 +3219,7 @@
VerifiedMotionEvent verifiedEvent = verifiedMotionEventFromMotionEntry(motionEntry);
verifiedEvent.actionMasked = actionMasked;
verifiedEvent.flags = dispatchEntry.resolvedFlags & VERIFIED_MOTION_EVENT_FLAGS;
- return mHmacKeyManager.sign(verifiedEvent);
+ return sign(verifiedEvent);
}
return INVALID_HMAC;
}
@@ -2663,12 +3229,12 @@
VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEntry(keyEntry);
verifiedEvent.flags = dispatchEntry.resolvedFlags & VERIFIED_KEY_EVENT_FLAGS;
verifiedEvent.action = dispatchEntry.resolvedAction;
- return mHmacKeyManager.sign(verifiedEvent);
+ return sign(verifiedEvent);
}
void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection, uint32_t seq,
- bool handled) {
+ bool handled, nsecs_t consumeTime) {
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s",
connection->getInputChannelName().c_str(), seq, toString(handled));
@@ -2680,7 +3246,7 @@
}
// Notify other system components and prepare to start the next dispatch cycle.
- onDispatchCycleFinishedLocked(currentTime, connection, seq, handled);
+ onDispatchCycleFinishedLocked(currentTime, connection, seq, handled, consumeTime);
}
void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime,
@@ -2719,7 +3285,7 @@
void InputDispatcher::releaseDispatchEntry(DispatchEntry* dispatchEntry) {
if (dispatchEntry->hasForegroundTarget()) {
- decrementPendingForegroundDispatches(dispatchEntry->eventEntry);
+ decrementPendingForegroundDispatches(*(dispatchEntry->eventEntry));
}
delete dispatchEntry;
}
@@ -2749,15 +3315,23 @@
nsecs_t currentTime = now();
bool gotOne = false;
- status_t status;
+ status_t status = OK;
for (;;) {
- uint32_t seq;
- bool handled;
- status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled);
- if (status) {
+ Result<InputPublisher::ConsumerResponse> result =
+ connection->inputPublisher.receiveConsumerResponse();
+ if (!result.ok()) {
+ status = result.error().code();
break;
}
- d->finishDispatchCycleLocked(currentTime, connection, seq, handled);
+
+ if (std::holds_alternative<InputPublisher::Finished>(*result)) {
+ const InputPublisher::Finished& finish =
+ std::get<InputPublisher::Finished>(*result);
+ d->finishDispatchCycleLocked(currentTime, connection, finish.seq,
+ finish.handled, finish.consumeTime);
+ } else if (std::holds_alternative<InputPublisher::Timeline>(*result)) {
+ // TODO(b/167947340): Report this data to LatencyTracker
+ }
gotOne = true;
}
if (gotOne) {
@@ -2769,13 +3343,13 @@
notify = status != DEAD_OBJECT || !connection->monitor;
if (notify) {
- ALOGE("channel '%s' ~ Failed to receive finished signal. status=%d",
- connection->getInputChannelName().c_str(), status);
+ ALOGE("channel '%s' ~ Failed to receive finished signal. status=%s(%d)",
+ connection->getInputChannelName().c_str(), statusToString(status).c_str(),
+ status);
}
} else {
// Monitor channels are never explicitly unregistered.
- // We do it automatically when the remote endpoint is closed so don't warn
- // about them.
+ // We do it automatically when the remote endpoint is closed so don't warn about them.
const bool stillHaveWindowHandle =
d->getWindowHandleLocked(connection->inputChannel->getConnectionToken()) !=
nullptr;
@@ -2787,16 +3361,16 @@
}
}
- // Unregister the channel.
- d->unregisterInputChannelLocked(connection->inputChannel, notify);
+ // Remove the channel.
+ d->removeInputChannelLocked(connection->inputChannel->getConnectionToken(), notify);
return 0; // remove the callback
} // release lock
}
void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked(
const CancelationOptions& options) {
- for (const auto& pair : mConnectionsByFd) {
- synthesizeCancelationEventsForConnectionLocked(pair.second, options);
+ for (const auto& [fd, connection] : mConnectionsByFd) {
+ synthesizeCancelationEventsForConnectionLocked(connection, options);
}
}
@@ -2818,7 +3392,7 @@
}
void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked(
- const sp<InputChannel>& channel, const CancelationOptions& options) {
+ const std::shared_ptr<InputChannel>& channel, const CancelationOptions& options) {
sp<Connection> connection = getConnectionLocked(channel->getConnectionToken());
if (connection == nullptr) {
return;
@@ -2835,7 +3409,7 @@
nsecs_t currentTime = now();
- std::vector<EventEntry*> cancelationEvents =
+ std::vector<std::unique_ptr<EventEntry>> cancelationEvents =
connection->inputState.synthesizeCancelationEvents(currentTime, options);
if (cancelationEvents.empty()) {
@@ -2853,15 +3427,14 @@
getWindowHandleLocked(connection->inputChannel->getConnectionToken());
if (windowHandle != nullptr) {
const InputWindowInfo* windowInfo = windowHandle->getInfo();
- target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop,
- windowInfo->windowXScale, windowInfo->windowYScale);
+ target.setDefaultPointerTransform(windowInfo->transform);
target.globalScaleFactor = windowInfo->globalScaleFactor;
}
target.inputChannel = connection->inputChannel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
for (size_t i = 0; i < cancelationEvents.size(); i++) {
- EventEntry* cancelationEventEntry = cancelationEvents[i];
+ std::unique_ptr<EventEntry> cancelationEventEntry = std::move(cancelationEvents[i]);
switch (cancelationEventEntry->type) {
case EventEntry::Type::KEY: {
logOutboundKeyDetails("cancel - ",
@@ -2873,22 +3446,24 @@
static_cast<const MotionEntry&>(*cancelationEventEntry));
break;
}
- case EventEntry::Type::FOCUS: {
- LOG_ALWAYS_FATAL("Canceling focus events is not supported");
+ case EventEntry::Type::FOCUS:
+ case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+ case EventEntry::Type::DRAG: {
+ LOG_ALWAYS_FATAL("Canceling %s events is not supported",
+ NamedEnum::string(cancelationEventEntry->type).c_str());
break;
}
case EventEntry::Type::CONFIGURATION_CHANGED:
- case EventEntry::Type::DEVICE_RESET: {
+ case EventEntry::Type::DEVICE_RESET:
+ case EventEntry::Type::SENSOR: {
LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
- EventEntry::typeToString(cancelationEventEntry->type));
+ NamedEnum::string(cancelationEventEntry->type).c_str());
break;
}
}
- enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
- target, InputTarget::FLAG_DISPATCH_AS_IS);
-
- cancelationEventEntry->release();
+ enqueueDispatchEntryLocked(connection, std::move(cancelationEventEntry), target,
+ InputTarget::FLAG_DISPATCH_AS_IS);
}
startDispatchCycleLocked(currentTime, connection);
@@ -2902,7 +3477,7 @@
nsecs_t currentTime = now();
- std::vector<EventEntry*> downEvents =
+ std::vector<std::unique_ptr<EventEntry>> downEvents =
connection->inputState.synthesizePointerDownEvents(currentTime);
if (downEvents.empty()) {
@@ -2919,14 +3494,13 @@
getWindowHandleLocked(connection->inputChannel->getConnectionToken());
if (windowHandle != nullptr) {
const InputWindowInfo* windowInfo = windowHandle->getInfo();
- target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop,
- windowInfo->windowXScale, windowInfo->windowYScale);
+ target.setDefaultPointerTransform(windowInfo->transform);
target.globalScaleFactor = windowInfo->globalScaleFactor;
}
target.inputChannel = connection->inputChannel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
- for (EventEntry* downEventEntry : downEvents) {
+ for (std::unique_ptr<EventEntry>& downEventEntry : downEvents) {
switch (downEventEntry->type) {
case EventEntry::Type::MOTION: {
logOutboundMotionDetails("down - ",
@@ -2937,24 +3511,25 @@
case EventEntry::Type::KEY:
case EventEntry::Type::FOCUS:
case EventEntry::Type::CONFIGURATION_CHANGED:
- case EventEntry::Type::DEVICE_RESET: {
+ case EventEntry::Type::DEVICE_RESET:
+ case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+ case EventEntry::Type::SENSOR:
+ case EventEntry::Type::DRAG: {
LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
- EventEntry::typeToString(downEventEntry->type));
+ NamedEnum::string(downEventEntry->type).c_str());
break;
}
}
- enqueueDispatchEntryLocked(connection, downEventEntry, // increments ref
- target, InputTarget::FLAG_DISPATCH_AS_IS);
-
- downEventEntry->release();
+ enqueueDispatchEntryLocked(connection, std::move(downEventEntry), target,
+ InputTarget::FLAG_DISPATCH_AS_IS);
}
startDispatchCycleLocked(currentTime, connection);
}
-MotionEntry* InputDispatcher::splitMotionEvent(const MotionEntry& originalMotionEntry,
- BitSet32 pointerIds) {
+std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent(
+ const MotionEntry& originalMotionEntry, BitSet32 pointerIds) {
ALOG_ASSERT(pointerIds.value != 0);
uint32_t splitPointerIndexMap[MAX_POINTERS];
@@ -3004,7 +3579,9 @@
// The first/last pointer went down/up.
action = maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN
? AMOTION_EVENT_ACTION_DOWN
- : AMOTION_EVENT_ACTION_UP;
+ : (originalMotionEntry.flags & AMOTION_EVENT_FLAG_CANCELED) != 0
+ ? AMOTION_EVENT_ACTION_CANCEL
+ : AMOTION_EVENT_ACTION_UP;
} else {
// A secondary pointer went down/up.
uint32_t splitPointerIndex = 0;
@@ -3027,17 +3604,22 @@
originalMotionEntry.id, newId);
ATRACE_NAME(message.c_str());
}
- MotionEntry* splitMotionEntry =
- new MotionEntry(newId, originalMotionEntry.eventTime, originalMotionEntry.deviceId,
- originalMotionEntry.source, originalMotionEntry.displayId,
- originalMotionEntry.policyFlags, action,
- originalMotionEntry.actionButton, originalMotionEntry.flags,
- originalMotionEntry.metaState, originalMotionEntry.buttonState,
- originalMotionEntry.classification, originalMotionEntry.edgeFlags,
- originalMotionEntry.xPrecision, originalMotionEntry.yPrecision,
- originalMotionEntry.xCursorPosition,
- originalMotionEntry.yCursorPosition, originalMotionEntry.downTime,
- splitPointerCount, splitPointerProperties, splitPointerCoords, 0, 0);
+ std::unique_ptr<MotionEntry> splitMotionEntry =
+ std::make_unique<MotionEntry>(newId, originalMotionEntry.eventTime,
+ originalMotionEntry.deviceId, originalMotionEntry.source,
+ originalMotionEntry.displayId,
+ originalMotionEntry.policyFlags, action,
+ originalMotionEntry.actionButton,
+ originalMotionEntry.flags, originalMotionEntry.metaState,
+ originalMotionEntry.buttonState,
+ originalMotionEntry.classification,
+ originalMotionEntry.edgeFlags,
+ originalMotionEntry.xPrecision,
+ originalMotionEntry.yPrecision,
+ originalMotionEntry.xCursorPosition,
+ originalMotionEntry.yCursorPosition,
+ originalMotionEntry.downTime, splitPointerCount,
+ splitPointerProperties, splitPointerCoords, 0, 0);
if (originalMotionEntry.injectionState) {
splitMotionEntry->injectionState = originalMotionEntry.injectionState;
@@ -3056,9 +3638,9 @@
{ // acquire lock
std::scoped_lock _l(mLock);
- ConfigurationChangedEntry* newEntry =
- new ConfigurationChangedEntry(args->id, args->eventTime);
- needWake = enqueueInboundEventLocked(newEntry);
+ std::unique_ptr<ConfigurationChangedEntry> newEntry =
+ std::make_unique<ConfigurationChangedEntry>(args->id, args->eventTime);
+ needWake = enqueueInboundEventLocked(std::move(newEntry));
} // release lock
if (needWake) {
@@ -3162,12 +3744,13 @@
mLock.lock();
}
- KeyEntry* newEntry =
- new KeyEntry(args->id, args->eventTime, args->deviceId, args->source,
- args->displayId, policyFlags, args->action, flags, keyCode,
- args->scanCode, metaState, repeatCount, args->downTime);
+ std::unique_ptr<KeyEntry> newEntry =
+ std::make_unique<KeyEntry>(args->id, args->eventTime, args->deviceId, args->source,
+ args->displayId, policyFlags, args->action, flags,
+ keyCode, args->scanCode, metaState, repeatCount,
+ args->downTime);
- needWake = enqueueInboundEventLocked(newEntry);
+ needWake = enqueueInboundEventLocked(std::move(newEntry));
mLock.unlock();
} // release lock
@@ -3231,13 +3814,13 @@
mLock.unlock();
MotionEvent event;
+ ui::Transform transform;
event.initialize(args->id, args->deviceId, args->source, args->displayId, INVALID_HMAC,
args->action, args->actionButton, args->flags, args->edgeFlags,
- args->metaState, args->buttonState, args->classification, 1 /*xScale*/,
- 1 /*yScale*/, 0 /* xOffset */, 0 /* yOffset */, args->xPrecision,
- args->yPrecision, args->xCursorPosition, args->yCursorPosition,
- args->downTime, args->eventTime, args->pointerCount,
- args->pointerProperties, args->pointerCoords);
+ args->metaState, args->buttonState, args->classification, transform,
+ args->xPrecision, args->yPrecision, args->xCursorPosition,
+ args->yCursorPosition, args->downTime, args->eventTime,
+ args->pointerCount, args->pointerProperties, args->pointerCoords);
policyFlags |= POLICY_FLAG_FILTERED;
if (!mPolicy->filterInputEvent(&event, policyFlags)) {
@@ -3248,16 +3831,18 @@
}
// Just enqueue a new motion event.
- MotionEntry* newEntry =
- new MotionEntry(args->id, args->eventTime, args->deviceId, args->source,
- args->displayId, policyFlags, args->action, args->actionButton,
- args->flags, args->metaState, args->buttonState,
- args->classification, args->edgeFlags, args->xPrecision,
- args->yPrecision, args->xCursorPosition, args->yCursorPosition,
- args->downTime, args->pointerCount, args->pointerProperties,
- args->pointerCoords, 0, 0);
+ std::unique_ptr<MotionEntry> newEntry =
+ std::make_unique<MotionEntry>(args->id, args->eventTime, args->deviceId,
+ args->source, args->displayId, policyFlags,
+ args->action, args->actionButton, args->flags,
+ args->metaState, args->buttonState,
+ args->classification, args->edgeFlags,
+ args->xPrecision, args->yPrecision,
+ args->xCursorPosition, args->yCursorPosition,
+ args->downTime, args->pointerCount,
+ args->pointerProperties, args->pointerCoords, 0, 0);
- needWake = enqueueInboundEventLocked(newEntry);
+ needWake = enqueueInboundEventLocked(std::move(newEntry));
mLock.unlock();
} // release lock
@@ -3266,6 +3851,42 @@
}
}
+void InputDispatcher::notifySensor(const NotifySensorArgs* args) {
+#if DEBUG_INBOUND_EVENT_DETAILS
+ ALOGD("notifySensor - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, "
+ " sensorType=%s",
+ args->id, args->eventTime, args->deviceId, args->source,
+ NamedEnum::string(args->sensorType).c_str());
+#endif
+
+ bool needWake;
+ { // acquire lock
+ mLock.lock();
+
+ // Just enqueue a new sensor event.
+ std::unique_ptr<SensorEntry> newEntry =
+ std::make_unique<SensorEntry>(args->id, args->eventTime, args->deviceId,
+ args->source, 0 /* policyFlags*/, args->hwTimestamp,
+ args->sensorType, args->accuracy,
+ args->accuracyChanged, args->values);
+
+ needWake = enqueueInboundEventLocked(std::move(newEntry));
+ mLock.unlock();
+ } // release lock
+
+ if (needWake) {
+ mLooper->wake();
+ }
+}
+
+void InputDispatcher::notifyVibratorState(const NotifyVibratorStateArgs* args) {
+#if DEBUG_INBOUND_EVENT_DETAILS
+ ALOGD("notifyVibratorState - eventTime=%" PRId64 ", device=%d, isOn=%d", args->eventTime,
+ args->deviceId, args->isOn);
+#endif
+ mPolicy->notifyVibratorState(args->deviceId, args->isOn);
+}
+
bool InputDispatcher::shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) {
return mInputFilterEnabled;
}
@@ -3292,9 +3913,9 @@
{ // acquire lock
std::scoped_lock _l(mLock);
- DeviceResetEntry* newEntry =
- new DeviceResetEntry(args->id, args->eventTime, args->deviceId);
- needWake = enqueueInboundEventLocked(newEntry);
+ std::unique_ptr<DeviceResetEntry> newEntry =
+ std::make_unique<DeviceResetEntry>(args->id, args->eventTime, args->deviceId);
+ needWake = enqueueInboundEventLocked(std::move(newEntry));
} // release lock
if (needWake) {
@@ -3302,15 +3923,33 @@
}
}
-int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injectorPid,
- int32_t injectorUid, int32_t syncMode,
- std::chrono::milliseconds timeout, uint32_t policyFlags) {
+void InputDispatcher::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
+#if DEBUG_INBOUND_EVENT_DETAILS
+ ALOGD("notifyPointerCaptureChanged - eventTime=%" PRId64 ", enabled=%s", args->eventTime,
+ args->enabled ? "true" : "false");
+#endif
+
+ bool needWake;
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+ auto entry = std::make_unique<PointerCaptureChangedEntry>(args->id, args->eventTime,
+ args->enabled);
+ needWake = enqueueInboundEventLocked(std::move(entry));
+ } // release lock
+
+ if (needWake) {
+ mLooper->wake();
+ }
+}
+
+InputEventInjectionResult InputDispatcher::injectInputEvent(
+ const InputEvent* event, int32_t injectorPid, int32_t injectorUid,
+ InputEventInjectionSync syncMode, std::chrono::milliseconds timeout, uint32_t policyFlags) {
#if DEBUG_INBOUND_EVENT_DETAILS
ALOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
"syncMode=%d, timeout=%lld, policyFlags=0x%08x",
event->getType(), injectorPid, injectorUid, syncMode, timeout.count(), policyFlags);
#endif
-
nsecs_t endTime = now() + std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count();
policyFlags |= POLICY_FLAG_INJECTED;
@@ -3318,13 +3957,13 @@
policyFlags |= POLICY_FLAG_TRUSTED;
}
- std::queue<EventEntry*> injectedEntries;
+ std::queue<std::unique_ptr<EventEntry>> injectedEntries;
switch (event->getType()) {
case AINPUT_EVENT_TYPE_KEY: {
const KeyEvent& incomingKey = static_cast<const KeyEvent&>(*event);
int32_t action = incomingKey.getAction();
if (!validateKeyEvent(action)) {
- return INPUT_EVENT_INJECTION_FAILED;
+ return InputEventInjectionResult::FAILED;
}
int32_t flags = incomingKey.getFlags();
@@ -3352,13 +3991,14 @@
}
mLock.lock();
- KeyEntry* injectedEntry =
- new KeyEntry(incomingKey.getId(), incomingKey.getEventTime(),
- VIRTUAL_KEYBOARD_ID, incomingKey.getSource(),
- incomingKey.getDisplayId(), policyFlags, action, flags, keyCode,
- incomingKey.getScanCode(), metaState, incomingKey.getRepeatCount(),
- incomingKey.getDownTime());
- injectedEntries.push(injectedEntry);
+ std::unique_ptr<KeyEntry> injectedEntry =
+ std::make_unique<KeyEntry>(incomingKey.getId(), incomingKey.getEventTime(),
+ VIRTUAL_KEYBOARD_ID, incomingKey.getSource(),
+ incomingKey.getDisplayId(), policyFlags, action,
+ flags, keyCode, incomingKey.getScanCode(), metaState,
+ incomingKey.getRepeatCount(),
+ incomingKey.getDownTime());
+ injectedEntries.push(std::move(injectedEntry));
break;
}
@@ -3370,7 +4010,7 @@
int32_t actionButton = motionEvent->getActionButton();
int32_t displayId = motionEvent->getDisplayId();
if (!validateMotionEvent(action, actionButton, pointerCount, pointerProperties)) {
- return INPUT_EVENT_INJECTION_FAILED;
+ return InputEventInjectionResult::FAILED;
}
if (!(policyFlags & POLICY_FLAG_FILTERED)) {
@@ -3386,48 +4026,57 @@
mLock.lock();
const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords();
- MotionEntry* injectedEntry =
- new MotionEntry(motionEvent->getId(), *sampleEventTimes, VIRTUAL_KEYBOARD_ID,
- motionEvent->getSource(), motionEvent->getDisplayId(),
- policyFlags, action, actionButton, motionEvent->getFlags(),
- motionEvent->getMetaState(), motionEvent->getButtonState(),
- motionEvent->getClassification(), motionEvent->getEdgeFlags(),
- motionEvent->getXPrecision(), motionEvent->getYPrecision(),
- motionEvent->getRawXCursorPosition(),
- motionEvent->getRawYCursorPosition(),
- motionEvent->getDownTime(), uint32_t(pointerCount),
- pointerProperties, samplePointerCoords,
- motionEvent->getXOffset(), motionEvent->getYOffset());
- injectedEntries.push(injectedEntry);
+ std::unique_ptr<MotionEntry> injectedEntry =
+ std::make_unique<MotionEntry>(motionEvent->getId(), *sampleEventTimes,
+ VIRTUAL_KEYBOARD_ID, motionEvent->getSource(),
+ motionEvent->getDisplayId(), policyFlags, action,
+ actionButton, motionEvent->getFlags(),
+ motionEvent->getMetaState(),
+ motionEvent->getButtonState(),
+ motionEvent->getClassification(),
+ motionEvent->getEdgeFlags(),
+ motionEvent->getXPrecision(),
+ motionEvent->getYPrecision(),
+ motionEvent->getRawXCursorPosition(),
+ motionEvent->getRawYCursorPosition(),
+ motionEvent->getDownTime(),
+ uint32_t(pointerCount), pointerProperties,
+ samplePointerCoords, motionEvent->getXOffset(),
+ motionEvent->getYOffset());
+ injectedEntries.push(std::move(injectedEntry));
for (size_t i = motionEvent->getHistorySize(); i > 0; i--) {
sampleEventTimes += 1;
samplePointerCoords += pointerCount;
- MotionEntry* nextInjectedEntry =
- new MotionEntry(motionEvent->getId(), *sampleEventTimes,
- VIRTUAL_KEYBOARD_ID, motionEvent->getSource(),
- motionEvent->getDisplayId(), policyFlags, action,
- actionButton, motionEvent->getFlags(),
- motionEvent->getMetaState(), motionEvent->getButtonState(),
- motionEvent->getClassification(),
- motionEvent->getEdgeFlags(), motionEvent->getXPrecision(),
- motionEvent->getYPrecision(),
- motionEvent->getRawXCursorPosition(),
- motionEvent->getRawYCursorPosition(),
- motionEvent->getDownTime(), uint32_t(pointerCount),
- pointerProperties, samplePointerCoords,
- motionEvent->getXOffset(), motionEvent->getYOffset());
- injectedEntries.push(nextInjectedEntry);
+ std::unique_ptr<MotionEntry> nextInjectedEntry =
+ std::make_unique<MotionEntry>(motionEvent->getId(), *sampleEventTimes,
+ VIRTUAL_KEYBOARD_ID, motionEvent->getSource(),
+ motionEvent->getDisplayId(), policyFlags,
+ action, actionButton, motionEvent->getFlags(),
+ motionEvent->getMetaState(),
+ motionEvent->getButtonState(),
+ motionEvent->getClassification(),
+ motionEvent->getEdgeFlags(),
+ motionEvent->getXPrecision(),
+ motionEvent->getYPrecision(),
+ motionEvent->getRawXCursorPosition(),
+ motionEvent->getRawYCursorPosition(),
+ motionEvent->getDownTime(),
+ uint32_t(pointerCount), pointerProperties,
+ samplePointerCoords,
+ motionEvent->getXOffset(),
+ motionEvent->getYOffset());
+ injectedEntries.push(std::move(nextInjectedEntry));
}
break;
}
default:
ALOGW("Cannot inject %s events", inputEventTypeToString(event->getType()));
- return INPUT_EVENT_INJECTION_FAILED;
+ return InputEventInjectionResult::FAILED;
}
InjectionState* injectionState = new InjectionState(injectorPid, injectorUid);
- if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) {
+ if (syncMode == InputEventInjectionSync::NONE) {
injectionState->injectionIsAsync = true;
}
@@ -3436,7 +4085,7 @@
bool needWake = false;
while (!injectedEntries.empty()) {
- needWake |= enqueueInboundEventLocked(injectedEntries.front());
+ needWake |= enqueueInboundEventLocked(std::move(injectedEntries.front()));
injectedEntries.pop();
}
@@ -3446,16 +4095,16 @@
mLooper->wake();
}
- int32_t injectionResult;
+ InputEventInjectionResult injectionResult;
{ // acquire lock
std::unique_lock _l(mLock);
- if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) {
- injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
+ if (syncMode == InputEventInjectionSync::NONE) {
+ injectionResult = InputEventInjectionResult::SUCCEEDED;
} else {
for (;;) {
injectionResult = injectionState->injectionResult;
- if (injectionResult != INPUT_EVENT_INJECTION_PENDING) {
+ if (injectionResult != InputEventInjectionResult::PENDING) {
break;
}
@@ -3465,15 +4114,15 @@
ALOGD("injectInputEvent - Timed out waiting for injection result "
"to become available.");
#endif
- injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
+ injectionResult = InputEventInjectionResult::TIMED_OUT;
break;
}
mInjectionResultAvailable.wait_for(_l, std::chrono::nanoseconds(remainingTimeout));
}
- if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED &&
- syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) {
+ if (injectionResult == InputEventInjectionResult::SUCCEEDED &&
+ syncMode == InputEventInjectionSync::WAIT_FOR_FINISHED) {
while (injectionState->pendingForegroundDispatches != 0) {
#if DEBUG_INJECTION
ALOGD("injectInputEvent - Waiting for %d pending foreground dispatches.",
@@ -3485,7 +4134,7 @@
ALOGD("injectInputEvent - Timed out waiting for pending foreground "
"dispatches to finish.");
#endif
- injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
+ injectionResult = InputEventInjectionResult::TIMED_OUT;
break;
}
@@ -3513,7 +4162,7 @@
const KeyEvent& keyEvent = static_cast<const KeyEvent&>(event);
VerifiedKeyEvent verifiedKeyEvent = verifiedKeyEventFromKeyEvent(keyEvent);
result = std::make_unique<VerifiedKeyEvent>(verifiedKeyEvent);
- calculatedHmac = mHmacKeyManager.sign(verifiedKeyEvent);
+ calculatedHmac = sign(verifiedKeyEvent);
break;
}
case AINPUT_EVENT_TYPE_MOTION: {
@@ -3521,7 +4170,7 @@
VerifiedMotionEvent verifiedMotionEvent =
verifiedMotionEventFromMotionEvent(motionEvent);
result = std::make_unique<VerifiedMotionEvent>(verifiedMotionEvent);
- calculatedHmac = mHmacKeyManager.sign(verifiedMotionEvent);
+ calculatedHmac = sign(verifiedMotionEvent);
break;
}
default: {
@@ -3543,8 +4192,9 @@
mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid);
}
-void InputDispatcher::setInjectionResult(EventEntry* entry, int32_t injectionResult) {
- InjectionState* injectionState = entry->injectionState;
+void InputDispatcher::setInjectionResult(EventEntry& entry,
+ InputEventInjectionResult injectionResult) {
+ InjectionState* injectionState = entry.injectionState;
if (injectionState) {
#if DEBUG_INJECTION
ALOGD("Setting input event injection result to %d. "
@@ -3552,21 +4202,24 @@
injectionResult, injectionState->injectorPid, injectionState->injectorUid);
#endif
- if (injectionState->injectionIsAsync && !(entry->policyFlags & POLICY_FLAG_FILTERED)) {
+ if (injectionState->injectionIsAsync && !(entry.policyFlags & POLICY_FLAG_FILTERED)) {
// Log the outcome since the injector did not wait for the injection result.
switch (injectionResult) {
- case INPUT_EVENT_INJECTION_SUCCEEDED:
+ case InputEventInjectionResult::SUCCEEDED:
ALOGV("Asynchronous input event injection succeeded.");
break;
- case INPUT_EVENT_INJECTION_FAILED:
+ case InputEventInjectionResult::FAILED:
ALOGW("Asynchronous input event injection failed.");
break;
- case INPUT_EVENT_INJECTION_PERMISSION_DENIED:
+ case InputEventInjectionResult::PERMISSION_DENIED:
ALOGW("Asynchronous input event injection permission denied.");
break;
- case INPUT_EVENT_INJECTION_TIMED_OUT:
+ case InputEventInjectionResult::TIMED_OUT:
ALOGW("Asynchronous input event injection timed out.");
break;
+ case InputEventInjectionResult::PENDING:
+ ALOGE("Setting result to 'PENDING' for asynchronous injection");
+ break;
}
}
@@ -3575,15 +4228,15 @@
}
}
-void InputDispatcher::incrementPendingForegroundDispatches(EventEntry* entry) {
- InjectionState* injectionState = entry->injectionState;
+void InputDispatcher::incrementPendingForegroundDispatches(EventEntry& entry) {
+ InjectionState* injectionState = entry.injectionState;
if (injectionState) {
injectionState->pendingForegroundDispatches += 1;
}
}
-void InputDispatcher::decrementPendingForegroundDispatches(EventEntry* entry) {
- InjectionState* injectionState = entry->injectionState;
+void InputDispatcher::decrementPendingForegroundDispatches(EventEntry& entry) {
+ InjectionState* injectionState = entry.injectionState;
if (injectionState) {
injectionState->pendingForegroundDispatches -= 1;
@@ -3593,13 +4246,11 @@
}
}
-std::vector<sp<InputWindowHandle>> InputDispatcher::getWindowHandlesLocked(
+const std::vector<sp<InputWindowHandle>>& InputDispatcher::getWindowHandlesLocked(
int32_t displayId) const {
- return getValueByKey(mWindowHandlesByDisplay, displayId);
-}
-
-sp<InputWindowHandle> InputDispatcher::getFocusedWindowHandleLocked(int displayId) const {
- return getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
+ static const std::vector<sp<InputWindowHandle>> EMPTY_WINDOW_HANDLES;
+ auto it = mWindowHandlesByDisplay.find(displayId);
+ return it != mWindowHandlesByDisplay.end() ? it->second : EMPTY_WINDOW_HANDLES;
}
sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(
@@ -3609,7 +4260,7 @@
}
for (auto& it : mWindowHandlesByDisplay) {
- const std::vector<sp<InputWindowHandle>> windowHandles = it.second;
+ const std::vector<sp<InputWindowHandle>>& windowHandles = it.second;
for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
if (windowHandle->getToken() == windowHandleToken) {
return windowHandle;
@@ -3619,9 +4270,24 @@
return nullptr;
}
-bool InputDispatcher::hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const {
+sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(const sp<IBinder>& windowHandleToken,
+ int displayId) const {
+ if (windowHandleToken == nullptr) {
+ return nullptr;
+ }
+
+ for (const sp<InputWindowHandle>& windowHandle : getWindowHandlesLocked(displayId)) {
+ if (windowHandle->getToken() == windowHandleToken) {
+ return windowHandle;
+ }
+ }
+ return nullptr;
+}
+
+sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(
+ const sp<InputWindowHandle>& windowHandle) const {
for (auto& it : mWindowHandlesByDisplay) {
- const std::vector<sp<InputWindowHandle>> windowHandles = it.second;
+ const std::vector<sp<InputWindowHandle>>& windowHandles = it.second;
for (const sp<InputWindowHandle>& handle : windowHandles) {
if (handle->getId() == windowHandle->getId() &&
handle->getToken() == windowHandle->getToken()) {
@@ -3631,14 +4297,43 @@
windowHandle->getName().c_str(), it.first,
windowHandle->getInfo()->displayId);
}
- return true;
+ return handle;
}
}
}
- return false;
+ return nullptr;
}
-sp<InputChannel> InputDispatcher::getInputChannelLocked(const sp<IBinder>& token) const {
+sp<InputWindowHandle> InputDispatcher::getFocusedWindowHandleLocked(int displayId) const {
+ sp<IBinder> focusedToken = mFocusResolver.getFocusedWindowToken(displayId);
+ return getWindowHandleLocked(focusedToken, displayId);
+}
+
+bool InputDispatcher::hasResponsiveConnectionLocked(InputWindowHandle& windowHandle) const {
+ sp<Connection> connection = getConnectionLocked(windowHandle.getToken());
+ const bool noInputChannel =
+ windowHandle.getInfo()->inputFeatures.test(InputWindowInfo::Feature::NO_INPUT_CHANNEL);
+ if (connection != nullptr && noInputChannel) {
+ ALOGW("%s has feature NO_INPUT_CHANNEL, but it matched to connection %s",
+ windowHandle.getName().c_str(), connection->inputChannel->getName().c_str());
+ return false;
+ }
+
+ if (connection == nullptr) {
+ if (!noInputChannel) {
+ ALOGI("Could not find connection for %s", windowHandle.getName().c_str());
+ }
+ return false;
+ }
+ if (!connection->responsive) {
+ ALOGW("Window %s is not responsive", windowHandle.getName().c_str());
+ return false;
+ }
+ return true;
+}
+
+std::shared_ptr<InputChannel> InputDispatcher::getInputChannelLocked(
+ const sp<IBinder>& token) const {
size_t count = mInputChannelsByToken.count(token);
if (count == 0) {
return nullptr;
@@ -3673,10 +4368,9 @@
if ((getInputChannelLocked(handle->getToken()) == nullptr &&
info->portalToDisplayId == ADISPLAY_ID_NONE)) {
const bool noInputChannel =
- info->inputFeatures & InputWindowInfo::INPUT_FEATURE_NO_INPUT_CHANNEL;
- const bool canReceiveInput =
- !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_TOUCHABLE) ||
- !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_FOCUSABLE);
+ info->inputFeatures.test(InputWindowInfo::Feature::NO_INPUT_CHANNEL);
+ const bool canReceiveInput = !info->flags.test(InputWindowInfo::Flag::NOT_TOUCHABLE) ||
+ !info->flags.test(InputWindowInfo::Flag::NOT_FOCUSABLE);
if (canReceiveInput && !noInputChannel) {
ALOGV("Window handle %s has no registered input channel",
handle->getName().c_str());
@@ -3708,8 +4402,8 @@
const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>& handlesPerDisplay) {
{ // acquire lock
std::scoped_lock _l(mLock);
- for (auto const& i : handlesPerDisplay) {
- setInputWindowsLocked(i.second, i.first);
+ for (const auto& [displayId, handles] : handlesPerDisplay) {
+ setInputWindowsLocked(handles, displayId);
}
}
// Wake up poll loop since it may need to make new input dispatching choices.
@@ -3733,59 +4427,33 @@
ALOGD("setInputWindows displayId=%" PRId32 " %s", displayId, windowList.c_str());
}
+ // Ensure all tokens are null if the window has feature NO_INPUT_CHANNEL
+ for (const sp<InputWindowHandle>& window : inputWindowHandles) {
+ const bool noInputWindow =
+ window->getInfo()->inputFeatures.test(InputWindowInfo::Feature::NO_INPUT_CHANNEL);
+ if (noInputWindow && window->getToken() != nullptr) {
+ ALOGE("%s has feature NO_INPUT_WINDOW, but a non-null token. Clearing",
+ window->getName().c_str());
+ window->releaseChannel();
+ }
+ }
+
// Copy old handles for release if they are no longer present.
const std::vector<sp<InputWindowHandle>> oldWindowHandles = getWindowHandlesLocked(displayId);
updateWindowHandlesForDisplayLocked(inputWindowHandles, displayId);
- sp<InputWindowHandle> newFocusedWindowHandle = nullptr;
- bool foundHoveredWindow = false;
- for (const sp<InputWindowHandle>& windowHandle : getWindowHandlesLocked(displayId)) {
- // Set newFocusedWindowHandle to the top most focused window instead of the last one
- if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus &&
- windowHandle->getInfo()->visible) {
- newFocusedWindowHandle = windowHandle;
- }
- if (windowHandle == mLastHoverWindowHandle) {
- foundHoveredWindow = true;
- }
- }
-
- if (!foundHoveredWindow) {
+ const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
+ if (mLastHoverWindowHandle &&
+ std::find(windowHandles.begin(), windowHandles.end(), mLastHoverWindowHandle) ==
+ windowHandles.end()) {
mLastHoverWindowHandle = nullptr;
}
- sp<InputWindowHandle> oldFocusedWindowHandle =
- getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
-
- if (!haveSameToken(oldFocusedWindowHandle, newFocusedWindowHandle)) {
- if (oldFocusedWindowHandle != nullptr) {
- if (DEBUG_FOCUS) {
- ALOGD("Focus left window: %s in display %" PRId32,
- oldFocusedWindowHandle->getName().c_str(), displayId);
- }
- sp<InputChannel> focusedInputChannel =
- getInputChannelLocked(oldFocusedWindowHandle->getToken());
- if (focusedInputChannel != nullptr) {
- CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
- "focus left window");
- synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options);
- enqueueFocusEventLocked(*oldFocusedWindowHandle, false /*hasFocus*/);
- }
- mFocusedWindowHandlesByDisplay.erase(displayId);
- }
- if (newFocusedWindowHandle != nullptr) {
- if (DEBUG_FOCUS) {
- ALOGD("Focus entered window: %s in display %" PRId32,
- newFocusedWindowHandle->getName().c_str(), displayId);
- }
- mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle;
- enqueueFocusEventLocked(*newFocusedWindowHandle, true /*hasFocus*/);
- }
-
- if (mFocusedDisplayId == displayId) {
- onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
- }
+ std::optional<FocusResolver::FocusChanges> changes =
+ mFocusResolver.setInputWindows(displayId, windowHandles);
+ if (changes) {
+ onFocusChangedLocked(*changes);
}
std::unordered_map<int32_t, TouchState>::iterator stateIt =
@@ -3794,12 +4462,12 @@
TouchState& state = stateIt->second;
for (size_t i = 0; i < state.windows.size();) {
TouchedWindow& touchedWindow = state.windows[i];
- if (!hasWindowHandleLocked(touchedWindow.windowHandle)) {
+ if (getWindowHandleLocked(touchedWindow.windowHandle) == nullptr) {
if (DEBUG_FOCUS) {
ALOGD("Touched window was removed: %s in display %" PRId32,
touchedWindow.windowHandle->getName().c_str(), displayId);
}
- sp<InputChannel> touchedInputChannel =
+ std::shared_ptr<InputChannel> touchedInputChannel =
getInputChannelLocked(touchedWindow.windowHandle->getToken());
if (touchedInputChannel != nullptr) {
CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
@@ -3811,6 +4479,14 @@
++i;
}
}
+
+ // If drag window is gone, it would receive a cancel event and broadcast the DRAG_END. We
+ // could just clear the state here.
+ if (mDragState &&
+ std::find(windowHandles.begin(), windowHandles.end(), mDragState->dragWindow) ==
+ windowHandles.end()) {
+ mDragState.reset();
+ }
}
// Release information for windows that are no longer present.
@@ -3818,17 +4494,29 @@
// Otherwise, they might stick around until the window handle is destroyed
// which might not happen until the next GC.
for (const sp<InputWindowHandle>& oldWindowHandle : oldWindowHandles) {
- if (!hasWindowHandleLocked(oldWindowHandle)) {
+ if (getWindowHandleLocked(oldWindowHandle) == nullptr) {
if (DEBUG_FOCUS) {
ALOGD("Window went away: %s", oldWindowHandle->getName().c_str());
}
oldWindowHandle->releaseChannel();
+ // To avoid making too many calls into the compat framework, only
+ // check for window flags when windows are going away.
+ // TODO(b/157929241) : delete this. This is only needed temporarily
+ // in order to gather some data about the flag usage
+ if (oldWindowHandle->getInfo()->flags.test(InputWindowInfo::Flag::SLIPPERY)) {
+ ALOGW("%s has FLAG_SLIPPERY. Please report this in b/157929241",
+ oldWindowHandle->getName().c_str());
+ if (mCompatService != nullptr) {
+ mCompatService->reportChangeByUid(IInputConstants::BLOCK_FLAG_SLIPPERY,
+ oldWindowHandle->getInfo()->ownerUid);
+ }
+ }
}
}
}
void InputDispatcher::setFocusedApplication(
- int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) {
+ int32_t displayId, const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) {
if (DEBUG_FOCUS) {
ALOGD("setFocusedApplication displayId=%" PRId32 " %s", displayId,
inputApplicationHandle ? inputApplicationHandle->getName().c_str() : "<nullptr>");
@@ -3836,22 +4524,23 @@
{ // acquire lock
std::scoped_lock _l(mLock);
- sp<InputApplicationHandle> oldFocusedApplicationHandle =
+ std::shared_ptr<InputApplicationHandle> oldFocusedApplicationHandle =
getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
- if (oldFocusedApplicationHandle == mAwaitedFocusedApplication &&
- inputApplicationHandle != oldFocusedApplicationHandle) {
- resetNoFocusedWindowTimeoutLocked();
+ if (sharedPointersEqual(oldFocusedApplicationHandle, inputApplicationHandle)) {
+ return; // This application is already focused. No need to wake up or change anything.
}
- if (inputApplicationHandle != nullptr && inputApplicationHandle->updateInfo()) {
- if (oldFocusedApplicationHandle != inputApplicationHandle) {
- mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle;
- }
- } else if (oldFocusedApplicationHandle != nullptr) {
- oldFocusedApplicationHandle.clear();
+ // Set the new application handle.
+ if (inputApplicationHandle != nullptr) {
+ mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle;
+ } else {
mFocusedApplicationHandlesByDisplay.erase(displayId);
}
+
+ // No matter what the old focused application was, stop waiting on it because it is
+ // no longer focused.
+ resetNoFocusedWindowTimeoutLocked();
} // release lock
// Wake up poll loop since it may need to make new input dispatching choices.
@@ -3875,11 +4564,11 @@
std::scoped_lock _l(mLock);
if (mFocusedDisplayId != displayId) {
- sp<InputWindowHandle> oldFocusedWindowHandle =
- getValueByKey(mFocusedWindowHandlesByDisplay, mFocusedDisplayId);
- if (oldFocusedWindowHandle != nullptr) {
- sp<InputChannel> inputChannel =
- getInputChannelLocked(oldFocusedWindowHandle->getToken());
+ sp<IBinder> oldFocusedWindowToken =
+ mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
+ if (oldFocusedWindowToken != nullptr) {
+ std::shared_ptr<InputChannel> inputChannel =
+ getInputChannelLocked(oldFocusedWindowToken);
if (inputChannel != nullptr) {
CancelationOptions
options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
@@ -3890,21 +4579,15 @@
}
mFocusedDisplayId = displayId;
- // Sanity check
- sp<InputWindowHandle> newFocusedWindowHandle =
- getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
- onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
+ // Find new focused window and validate
+ sp<IBinder> newFocusedWindowToken = mFocusResolver.getFocusedWindowToken(displayId);
+ notifyFocusChangedLocked(oldFocusedWindowToken, newFocusedWindowToken);
- if (newFocusedWindowHandle == nullptr) {
+ if (newFocusedWindowToken == nullptr) {
ALOGW("Focused display #%" PRId32 " does not have a focused window.", displayId);
- if (!mFocusedWindowHandlesByDisplay.empty()) {
- ALOGE("But another display has a focused window:");
- for (auto& it : mFocusedWindowHandlesByDisplay) {
- const int32_t displayId = it.first;
- const sp<InputWindowHandle>& windowHandle = it.second;
- ALOGE("Display #%" PRId32 " has focused window: '%s'\n", displayId,
- windowHandle->getName().c_str());
- }
+ if (mFocusResolver.hasFocusedWindowTokens()) {
+ ALOGE("But another display has a focused window\n%s",
+ mFocusResolver.dumpFocusedWindows().c_str());
}
}
}
@@ -3979,7 +4662,23 @@
mInTouchMode = inTouchMode;
}
-bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
+void InputDispatcher::setMaximumObscuringOpacityForTouch(float opacity) {
+ if (opacity < 0 || opacity > 1) {
+ LOG_ALWAYS_FATAL("Maximum obscuring opacity for touch should be >= 0 and <= 1");
+ return;
+ }
+
+ std::scoped_lock lock(mLock);
+ mMaximumObscuringOpacityForTouch = opacity;
+}
+
+void InputDispatcher::setBlockUntrustedTouchesMode(BlockUntrustedTouchesMode mode) {
+ std::scoped_lock lock(mLock);
+ mBlockUntrustedTouchesMode = mode;
+}
+
+bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
+ bool isDragDrop) {
if (fromToken == toToken) {
if (DEBUG_FOCUS) {
ALOGD("Trivial transfer to same window.");
@@ -4023,6 +4722,11 @@
InputTarget::FLAG_DISPATCH_AS_IS);
state.addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds);
+ // Store the dragging window.
+ if (isDragDrop) {
+ mDragState = std::make_unique<DragState>(toWindowHandle);
+ }
+
found = true;
goto Found;
}
@@ -4089,6 +4793,24 @@
}
}
+std::string InputDispatcher::dumpPointerCaptureStateLocked() {
+ std::string dump;
+
+ dump += StringPrintf(INDENT "FocusedWindowRequestedPointerCapture: %s\n",
+ toString(mFocusedWindowRequestedPointerCapture));
+
+ std::string windowName = "None";
+ if (mWindowTokenWithPointerCapture) {
+ const sp<InputWindowHandle> captureWindowHandle =
+ getWindowHandleLocked(mWindowTokenWithPointerCapture);
+ windowName = captureWindowHandle ? captureWindowHandle->getName().c_str()
+ : "token has capture without window";
+ }
+ dump += StringPrintf(INDENT "CurrentWindowWithPointerCapture: %s\n", windowName.c_str());
+
+ return dump;
+}
+
void InputDispatcher::dumpDispatchStateLocked(std::string& dump) {
dump += StringPrintf(INDENT "DispatchEnabled: %s\n", toString(mDispatchEnabled));
dump += StringPrintf(INDENT "DispatchFrozen: %s\n", toString(mDispatchFrozen));
@@ -4099,30 +4821,19 @@
dump += StringPrintf(INDENT "FocusedApplications:\n");
for (auto& it : mFocusedApplicationHandlesByDisplay) {
const int32_t displayId = it.first;
- const sp<InputApplicationHandle>& applicationHandle = it.second;
+ const std::shared_ptr<InputApplicationHandle>& applicationHandle = it.second;
+ const std::chrono::duration timeout =
+ applicationHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
dump += StringPrintf(INDENT2 "displayId=%" PRId32
", name='%s', dispatchingTimeout=%" PRId64 "ms\n",
- displayId, applicationHandle->getName().c_str(),
- ns2ms(applicationHandle
- ->getDispatchingTimeout(
- DEFAULT_INPUT_DISPATCHING_TIMEOUT)
- .count()));
+ displayId, applicationHandle->getName().c_str(), millis(timeout));
}
} else {
dump += StringPrintf(INDENT "FocusedApplications: <none>\n");
}
- if (!mFocusedWindowHandlesByDisplay.empty()) {
- dump += StringPrintf(INDENT "FocusedWindows:\n");
- for (auto& it : mFocusedWindowHandlesByDisplay) {
- const int32_t displayId = it.first;
- const sp<InputWindowHandle>& windowHandle = it.second;
- dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId,
- windowHandle->getName().c_str());
- }
- } else {
- dump += StringPrintf(INDENT "FocusedWindows: <none>\n");
- }
+ dump += mFocusResolver.dump();
+ dump += dumpPointerCaptureStateLocked();
if (!mTouchStatesByDisplay.empty()) {
dump += StringPrintf(INDENT "TouchStatesByDisplay:\n");
@@ -4156,6 +4867,11 @@
dump += INDENT "TouchStates: <no displays touched>\n";
}
+ if (mDragState) {
+ dump += StringPrintf(INDENT "DragState:\n");
+ mDragState->dump(dump, INDENT2);
+ }
+
if (!mWindowHandlesByDisplay.empty()) {
for (auto& it : mWindowHandlesByDisplay) {
const std::vector<sp<InputWindowHandle>> windowHandles = it.second;
@@ -4166,30 +4882,39 @@
const sp<InputWindowHandle>& windowHandle = windowHandles[i];
const InputWindowInfo* windowInfo = windowHandle->getInfo();
- dump += StringPrintf(INDENT3 "%zu: name='%s', displayId=%d, "
- "portalToDisplayId=%d, paused=%s, hasFocus=%s, "
- "hasWallpaper=%s, visible=%s, canReceiveKeys=%s, "
- "flags=0x%08x, type=0x%08x, "
+ dump += StringPrintf(INDENT3 "%zu: name='%s', id=%" PRId32 ", displayId=%d, "
+ "portalToDisplayId=%d, paused=%s, focusable=%s, "
+ "hasWallpaper=%s, visible=%s, alpha=%.2f, "
+ "flags=%s, type=%s, "
"frame=[%d,%d][%d,%d], globalScale=%f, "
- "windowScale=(%f,%f), touchableRegion=",
- i, windowInfo->name.c_str(), windowInfo->displayId,
- windowInfo->portalToDisplayId,
+ "applicationInfo.name=%s, "
+ "applicationInfo.token=%s, "
+ "touchableRegion=",
+ i, windowInfo->name.c_str(), windowInfo->id,
+ windowInfo->displayId, windowInfo->portalToDisplayId,
toString(windowInfo->paused),
- toString(windowInfo->hasFocus),
+ toString(windowInfo->focusable),
toString(windowInfo->hasWallpaper),
- toString(windowInfo->visible),
- toString(windowInfo->canReceiveKeys),
- windowInfo->layoutParamsFlags,
- windowInfo->layoutParamsType, windowInfo->frameLeft,
- windowInfo->frameTop, windowInfo->frameRight,
- windowInfo->frameBottom, windowInfo->globalScaleFactor,
- windowInfo->windowXScale, windowInfo->windowYScale);
- dumpRegion(dump, windowInfo->touchableRegion);
- dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures);
+ toString(windowInfo->visible), windowInfo->alpha,
+ windowInfo->flags.string().c_str(),
+ NamedEnum::string(windowInfo->type).c_str(),
+ windowInfo->frameLeft, windowInfo->frameTop,
+ windowInfo->frameRight, windowInfo->frameBottom,
+ windowInfo->globalScaleFactor,
+ windowInfo->applicationInfo.name.c_str(),
+ toString(windowInfo->applicationInfo.token).c_str());
+ dump += dumpRegion(windowInfo->touchableRegion);
+ dump += StringPrintf(", inputFeatures=%s",
+ windowInfo->inputFeatures.string().c_str());
dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%" PRId64
- "ms\n",
+ "ms, trustedOverlay=%s, hasToken=%s, "
+ "touchOcclusionMode=%s\n",
windowInfo->ownerPid, windowInfo->ownerUid,
- ns2ms(windowInfo->dispatchingTimeout));
+ millis(windowInfo->dispatchingTimeout),
+ toString(windowInfo->trustedOverlay),
+ toString(windowInfo->token != nullptr),
+ toString(windowInfo->touchOcclusionMode).c_str());
+ windowInfo->transform.dump(dump, "transform", INDENT4);
}
} else {
dump += INDENT2 "Windows: <none>\n";
@@ -4219,9 +4944,9 @@
// Dump recently dispatched or dropped events from oldest to newest.
if (!mRecentQueue.empty()) {
dump += StringPrintf(INDENT "RecentQueue: length=%zu\n", mRecentQueue.size());
- for (EventEntry* entry : mRecentQueue) {
+ for (std::shared_ptr<EventEntry>& entry : mRecentQueue) {
dump += INDENT2;
- entry->appendDescription(dump);
+ dump += entry->getDescription();
dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime));
}
} else {
@@ -4232,7 +4957,7 @@
if (mPendingEvent) {
dump += INDENT "PendingEvent:\n";
dump += INDENT2;
- mPendingEvent->appendDescription(dump);
+ dump += mPendingEvent->getDescription();
dump += StringPrintf(", age=%" PRId64 "ms\n",
ns2ms(currentTime - mPendingEvent->eventTime));
} else {
@@ -4242,9 +4967,9 @@
// Dump inbound events from oldest to newest.
if (!mInboundQueue.empty()) {
dump += StringPrintf(INDENT "InboundQueue: length=%zu\n", mInboundQueue.size());
- for (EventEntry* entry : mInboundQueue) {
+ for (std::shared_ptr<EventEntry>& entry : mInboundQueue) {
dump += INDENT2;
- entry->appendDescription(dump);
+ dump += entry->getDescription();
dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime));
}
} else {
@@ -4276,14 +5001,8 @@
if (!connection->outboundQueue.empty()) {
dump += StringPrintf(INDENT3 "OutboundQueue: length=%zu\n",
connection->outboundQueue.size());
- for (DispatchEntry* entry : connection->outboundQueue) {
- dump.append(INDENT4);
- entry->eventEntry->appendDescription(dump);
- dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, age=%" PRId64
- "ms\n",
- entry->targetFlags, entry->resolvedAction,
- ns2ms(currentTime - entry->eventEntry->eventTime));
- }
+ dump += dumpQueue(connection->outboundQueue, currentTime);
+
} else {
dump += INDENT3 "OutboundQueue: <empty>\n";
}
@@ -4291,15 +5010,7 @@
if (!connection->waitQueue.empty()) {
dump += StringPrintf(INDENT3 "WaitQueue: length=%zu\n",
connection->waitQueue.size());
- for (DispatchEntry* entry : connection->waitQueue) {
- dump += INDENT4;
- entry->eventEntry->appendDescription(dump);
- dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, "
- "age=%" PRId64 "ms, wait=%" PRId64 "ms\n",
- entry->targetFlags, entry->resolvedAction,
- ns2ms(currentTime - entry->eventEntry->eventTime),
- ns2ms(currentTime - entry->deliveryTime));
- }
+ dump += dumpQueue(connection->waitQueue, currentTime);
} else {
dump += INDENT3 "WaitQueue: <empty>\n";
}
@@ -4325,81 +5036,85 @@
const size_t numMonitors = monitors.size();
for (size_t i = 0; i < numMonitors; i++) {
const Monitor& monitor = monitors[i];
- const sp<InputChannel>& channel = monitor.inputChannel;
+ const std::shared_ptr<InputChannel>& channel = monitor.inputChannel;
dump += StringPrintf(INDENT2 "%zu: '%s', ", i, channel->getName().c_str());
dump += "\n";
}
}
-status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) {
-#if DEBUG_REGISTRATION
- ALOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().c_str());
+Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::string& name) {
+#if DEBUG_CHANNEL_CREATION
+ ALOGD("channel '%s' ~ createInputChannel", name.c_str());
#endif
+ std::shared_ptr<InputChannel> serverChannel;
+ std::unique_ptr<InputChannel> clientChannel;
+ status_t result = openInputChannelPair(name, serverChannel, clientChannel);
+
+ if (result) {
+ return base::Error(result) << "Failed to open input channel pair with name " << name;
+ }
+
{ // acquire lock
std::scoped_lock _l(mLock);
- sp<Connection> existingConnection = getConnectionLocked(inputChannel->getConnectionToken());
- if (existingConnection != nullptr) {
- ALOGW("Attempted to register already registered input channel '%s'",
- inputChannel->getName().c_str());
- return BAD_VALUE;
- }
+ sp<Connection> connection = new Connection(serverChannel, false /*monitor*/, mIdGenerator);
- sp<Connection> connection = new Connection(inputChannel, false /*monitor*/, mIdGenerator);
-
- int fd = inputChannel->getFd();
+ int fd = serverChannel->getFd();
mConnectionsByFd[fd] = connection;
- mInputChannelsByToken[inputChannel->getConnectionToken()] = inputChannel;
+ mInputChannelsByToken[serverChannel->getConnectionToken()] = serverChannel;
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
} // release lock
// Wake the looper because some connections have changed.
mLooper->wake();
- return OK;
+ return clientChannel;
}
-status_t InputDispatcher::registerInputMonitor(const sp<InputChannel>& inputChannel,
- int32_t displayId, bool isGestureMonitor) {
+Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor(int32_t displayId,
+ bool isGestureMonitor,
+ const std::string& name,
+ int32_t pid) {
+ std::shared_ptr<InputChannel> serverChannel;
+ std::unique_ptr<InputChannel> clientChannel;
+ status_t result = openInputChannelPair(name, serverChannel, clientChannel);
+ if (result) {
+ return base::Error(result) << "Failed to open input channel pair with name " << name;
+ }
+
{ // acquire lock
std::scoped_lock _l(mLock);
if (displayId < 0) {
- ALOGW("Attempted to register input monitor without a specified display.");
- return BAD_VALUE;
+ return base::Error(BAD_VALUE) << "Attempted to create input monitor with name " << name
+ << " without a specified display.";
}
- if (inputChannel->getConnectionToken() == nullptr) {
- ALOGW("Attempted to register input monitor without an identifying token.");
- return BAD_VALUE;
- }
+ sp<Connection> connection = new Connection(serverChannel, true /*monitor*/, mIdGenerator);
- sp<Connection> connection = new Connection(inputChannel, true /*monitor*/, mIdGenerator);
-
- const int fd = inputChannel->getFd();
+ const int fd = serverChannel->getFd();
mConnectionsByFd[fd] = connection;
- mInputChannelsByToken[inputChannel->getConnectionToken()] = inputChannel;
+ mInputChannelsByToken[serverChannel->getConnectionToken()] = serverChannel;
auto& monitorsByDisplay =
isGestureMonitor ? mGestureMonitorsByDisplay : mGlobalMonitorsByDisplay;
- monitorsByDisplay[displayId].emplace_back(inputChannel);
+ monitorsByDisplay[displayId].emplace_back(serverChannel, pid);
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
+ ALOGI("Created monitor %s for display %" PRId32 ", gesture=%s, pid=%" PRId32, name.c_str(),
+ displayId, toString(isGestureMonitor), pid);
}
+
// Wake the looper because some connections have changed.
mLooper->wake();
- return OK;
+ return clientChannel;
}
-status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputChannel) {
-#if DEBUG_REGISTRATION
- ALOGD("channel '%s' ~ unregisterInputChannel", inputChannel->getName().c_str());
-#endif
-
+status_t InputDispatcher::removeInputChannel(const sp<IBinder>& connectionToken) {
{ // acquire lock
std::scoped_lock _l(mLock);
- status_t status = unregisterInputChannelLocked(inputChannel, false /*notify*/);
+ status_t status = removeInputChannelLocked(connectionToken, false /*notify*/);
if (status) {
return status;
}
@@ -4411,23 +5126,22 @@
return OK;
}
-status_t InputDispatcher::unregisterInputChannelLocked(const sp<InputChannel>& inputChannel,
- bool notify) {
- sp<Connection> connection = getConnectionLocked(inputChannel->getConnectionToken());
+status_t InputDispatcher::removeInputChannelLocked(const sp<IBinder>& connectionToken,
+ bool notify) {
+ sp<Connection> connection = getConnectionLocked(connectionToken);
if (connection == nullptr) {
- ALOGW("Attempted to unregister already unregistered input channel '%s'",
- inputChannel->getName().c_str());
+ // Connection can be removed via socket hang up or an explicit call to 'removeInputChannel'
return BAD_VALUE;
}
removeConnectionLocked(connection);
- mInputChannelsByToken.erase(inputChannel->getConnectionToken());
+ mInputChannelsByToken.erase(connectionToken);
if (connection->monitor) {
- removeMonitorChannelLocked(inputChannel);
+ removeMonitorChannelLocked(connectionToken);
}
- mLooper->removeFd(inputChannel->getFd());
+ mLooper->removeFd(connection->inputChannel->getFd());
nsecs_t currentTime = now();
abortBrokenDispatchCycleLocked(currentTime, connection, notify);
@@ -4436,19 +5150,21 @@
return OK;
}
-void InputDispatcher::removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) {
- removeMonitorChannelLocked(inputChannel, mGlobalMonitorsByDisplay);
- removeMonitorChannelLocked(inputChannel, mGestureMonitorsByDisplay);
+void InputDispatcher::removeMonitorChannelLocked(const sp<IBinder>& connectionToken) {
+ removeMonitorChannelLocked(connectionToken, mGlobalMonitorsByDisplay);
+ removeMonitorChannelLocked(connectionToken, mGestureMonitorsByDisplay);
}
void InputDispatcher::removeMonitorChannelLocked(
- const sp<InputChannel>& inputChannel,
+ const sp<IBinder>& connectionToken,
std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) {
for (auto it = monitorsByDisplay.begin(); it != monitorsByDisplay.end();) {
std::vector<Monitor>& monitors = it->second;
const size_t numMonitors = monitors.size();
for (size_t i = 0; i < numMonitors; i++) {
- if (monitors[i].inputChannel == inputChannel) {
+ if (monitors[i].inputChannel->getConnectionToken() == connectionToken) {
+ ALOGI("Erasing monitor %s on display %" PRId32 ", pid=%" PRId32,
+ monitors[i].inputChannel->getName().c_str(), it->first, monitors[i].pid);
monitors.erase(monitors.begin() + i);
break;
}
@@ -4480,9 +5196,11 @@
}
TouchState& state = stateIt->second;
+ std::shared_ptr<InputChannel> requestingChannel;
std::optional<int32_t> foundDeviceId;
for (const TouchedMonitor& touchedMonitor : state.gestureMonitors) {
if (touchedMonitor.monitor.inputChannel->getConnectionToken() == token) {
+ requestingChannel = touchedMonitor.monitor.inputChannel;
foundDeviceId = state.deviceId;
}
}
@@ -4498,18 +5216,57 @@
"gesture monitor stole pointer stream");
options.deviceId = deviceId;
options.displayId = displayId;
+ std::string canceledWindows = "[";
for (const TouchedWindow& window : state.windows) {
- sp<InputChannel> channel = getInputChannelLocked(window.windowHandle->getToken());
+ std::shared_ptr<InputChannel> channel =
+ getInputChannelLocked(window.windowHandle->getToken());
if (channel != nullptr) {
synthesizeCancelationEventsForInputChannelLocked(channel, options);
+ canceledWindows += channel->getName() + ", ";
}
}
+ canceledWindows += "]";
+ ALOGI("Monitor %s is stealing touch from %s", requestingChannel->getName().c_str(),
+ canceledWindows.c_str());
+
// Then clear the current touch state so we stop dispatching to them as well.
state.filterNonMonitors();
}
return OK;
}
+void InputDispatcher::requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) {
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+ if (DEBUG_FOCUS) {
+ const sp<InputWindowHandle> windowHandle = getWindowHandleLocked(windowToken);
+ ALOGI("Request to %s Pointer Capture from: %s.", enabled ? "enable" : "disable",
+ windowHandle != nullptr ? windowHandle->getName().c_str()
+ : "token without window");
+ }
+
+ const sp<IBinder> focusedToken = mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
+ if (focusedToken != windowToken) {
+ ALOGW("Ignoring request to %s Pointer Capture: window does not have focus.",
+ enabled ? "enable" : "disable");
+ return;
+ }
+
+ if (enabled == mFocusedWindowRequestedPointerCapture) {
+ ALOGW("Ignoring request to %s Pointer Capture: "
+ "window has %s requested pointer capture.",
+ enabled ? "enable" : "disable", enabled ? "already" : "not");
+ return;
+ }
+
+ mFocusedWindowRequestedPointerCapture = enabled;
+ setPointerCaptureLocked(enabled);
+ } // release lock
+
+ // Wake the thread to process command entries.
+ mLooper->wake();
+}
+
std::optional<int32_t> InputDispatcher::findGestureMonitorDisplayByTokenLocked(
const sp<IBinder>& token) {
for (const auto& it : mGestureMonitorsByDisplay) {
@@ -4523,6 +5280,14 @@
return std::nullopt;
}
+std::optional<int32_t> InputDispatcher::findMonitorPidByTokenLocked(const sp<IBinder>& token) {
+ std::optional<int32_t> gesturePid = findMonitorPidByToken(mGestureMonitorsByDisplay, token);
+ if (gesturePid.has_value()) {
+ return gesturePid;
+ }
+ return findMonitorPidByToken(mGlobalMonitorsByDisplay, token);
+}
+
sp<Connection> InputDispatcher::getConnectionLocked(const sp<IBinder>& inputConnectionToken) const {
if (inputConnectionToken == nullptr) {
return nullptr;
@@ -4538,6 +5303,14 @@
return nullptr;
}
+std::string InputDispatcher::getConnectionNameLocked(const sp<IBinder>& connectionToken) const {
+ sp<Connection> connection = getConnectionLocked(connectionToken);
+ if (connection == nullptr) {
+ return "<nullptr>";
+ }
+ return connection->getInputChannelName();
+}
+
void InputDispatcher::removeConnectionLocked(const sp<Connection>& connection) {
mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
removeByValue(mConnectionsByFd, connection);
@@ -4545,13 +5318,14 @@
void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime,
const sp<Connection>& connection, uint32_t seq,
- bool handled) {
+ bool handled, nsecs_t consumeTime) {
std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
&InputDispatcher::doDispatchCycleFinishedLockedInterruptible);
commandEntry->connection = connection;
commandEntry->eventTime = currentTime;
commandEntry->seq = seq;
commandEntry->handled = handled;
+ commandEntry->consumeTime = consumeTime;
postCommandLocked(std::move(commandEntry));
}
@@ -4566,10 +5340,8 @@
postCommandLocked(std::move(commandEntry));
}
-void InputDispatcher::onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
- const sp<InputWindowHandle>& newFocus) {
- sp<IBinder> oldToken = oldFocus != nullptr ? oldFocus->getToken() : nullptr;
- sp<IBinder> newToken = newFocus != nullptr ? newFocus->getToken() : nullptr;
+void InputDispatcher::notifyFocusChangedLocked(const sp<IBinder>& oldToken,
+ const sp<IBinder>& newToken) {
std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
&InputDispatcher::doNotifyFocusChangedLockedInterruptible);
commandEntry->oldToken = oldToken;
@@ -4577,12 +5349,24 @@
postCommandLocked(std::move(commandEntry));
}
-void InputDispatcher::onAnrLocked(const Connection& connection) {
+void InputDispatcher::notifyDropWindowLocked(const sp<IBinder>& token, float x, float y) {
+ std::unique_ptr<CommandEntry> commandEntry =
+ std::make_unique<CommandEntry>(&InputDispatcher::doNotifyDropWindowLockedInterruptible);
+ commandEntry->newToken = token;
+ commandEntry->x = x;
+ commandEntry->y = y;
+ postCommandLocked(std::move(commandEntry));
+}
+
+void InputDispatcher::onAnrLocked(const sp<Connection>& connection) {
+ if (connection == nullptr) {
+ LOG_ALWAYS_FATAL("Caller must check for nullness");
+ }
// Since we are allowing the policy to extend the timeout, maybe the waitQueue
// is already healthy again. Don't raise ANR in this situation
- if (connection.waitQueue.empty()) {
+ if (connection->waitQueue.empty()) {
ALOGI("Not raising ANR because the connection %s has recovered",
- connection.inputChannel->getName().c_str());
+ connection->inputChannel->getName().c_str());
return;
}
/**
@@ -4593,36 +5377,37 @@
* processes the events linearly. So providing information about the oldest entry seems to be
* most useful.
*/
- DispatchEntry* oldestEntry = *connection.waitQueue.begin();
+ DispatchEntry* oldestEntry = *connection->waitQueue.begin();
const nsecs_t currentWait = now() - oldestEntry->deliveryTime;
std::string reason =
android::base::StringPrintf("%s is not responding. Waited %" PRId64 "ms for %s",
- connection.inputChannel->getName().c_str(),
+ connection->inputChannel->getName().c_str(),
ns2ms(currentWait),
oldestEntry->eventEntry->getDescription().c_str());
+ sp<IBinder> connectionToken = connection->inputChannel->getConnectionToken();
+ updateLastAnrStateLocked(getWindowHandleLocked(connectionToken), reason);
- updateLastAnrStateLocked(getWindowHandleLocked(connection.inputChannel->getConnectionToken()),
- reason);
+ processConnectionUnresponsiveLocked(*connection, std::move(reason));
- std::unique_ptr<CommandEntry> commandEntry =
- std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
- commandEntry->inputApplicationHandle = nullptr;
- commandEntry->inputChannel = connection.inputChannel;
- commandEntry->reason = std::move(reason);
+ // Stop waking up for events on this connection, it is already unresponsive
+ cancelEventsForAnrLocked(connection);
+}
+
+void InputDispatcher::onAnrLocked(std::shared_ptr<InputApplicationHandle> application) {
+ std::string reason =
+ StringPrintf("%s does not have a focused window", application->getName().c_str());
+ updateLastAnrStateLocked(*application, reason);
+
+ std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
+ &InputDispatcher::doNotifyNoFocusedWindowAnrLockedInterruptible);
+ commandEntry->inputApplicationHandle = std::move(application);
postCommandLocked(std::move(commandEntry));
}
-void InputDispatcher::onAnrLocked(const sp<InputApplicationHandle>& application) {
- std::string reason = android::base::StringPrintf("%s does not have a focused window",
- application->getName().c_str());
-
- updateLastAnrStateLocked(application, reason);
-
- std::unique_ptr<CommandEntry> commandEntry =
- std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
- commandEntry->inputApplicationHandle = application;
- commandEntry->inputChannel = nullptr;
- commandEntry->reason = std::move(reason);
+void InputDispatcher::onUntrustedTouchLocked(const std::string& obscuringPackage) {
+ std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
+ &InputDispatcher::doNotifyUntrustedTouchLockedInterruptible);
+ commandEntry->obscuringPackage = obscuringPackage;
postCommandLocked(std::move(commandEntry));
}
@@ -4632,9 +5417,9 @@
updateLastAnrStateLocked(windowLabel, reason);
}
-void InputDispatcher::updateLastAnrStateLocked(const sp<InputApplicationHandle>& application,
+void InputDispatcher::updateLastAnrStateLocked(const InputApplicationHandle& application,
const std::string& reason) {
- const std::string windowLabel = getApplicationWindowLabel(application, nullptr);
+ const std::string windowLabel = getApplicationWindowLabel(&application, nullptr);
updateLastAnrStateLocked(windowLabel, reason);
}
@@ -4682,69 +5467,71 @@
mLock.lock();
}
-void InputDispatcher::doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) {
- sp<IBinder> token =
- commandEntry->inputChannel ? commandEntry->inputChannel->getConnectionToken() : nullptr;
+void InputDispatcher::doNotifyDropWindowLockedInterruptible(CommandEntry* commandEntry) {
+ sp<IBinder> newToken = commandEntry->newToken;
mLock.unlock();
-
- const nsecs_t timeoutExtension =
- mPolicy->notifyAnr(commandEntry->inputApplicationHandle, token, commandEntry->reason);
-
+ mPolicy->notifyDropWindow(newToken, commandEntry->x, commandEntry->y);
mLock.lock();
-
- if (timeoutExtension > 0) {
- extendAnrTimeoutsLocked(commandEntry->inputApplicationHandle, token, timeoutExtension);
- } else {
- // stop waking up for events in this connection, it is already not responding
- sp<Connection> connection = getConnectionLocked(token);
- if (connection == nullptr) {
- return;
- }
- cancelEventsForAnrLocked(connection);
- }
}
-void InputDispatcher::extendAnrTimeoutsLocked(const sp<InputApplicationHandle>& application,
- const sp<IBinder>& connectionToken,
- nsecs_t timeoutExtension) {
- if (connectionToken == nullptr && application != nullptr) {
- // The ANR happened because there's no focused window
- mNoFocusedWindowTimeoutTime = now() + timeoutExtension;
- mAwaitedFocusedApplication = application;
- }
+void InputDispatcher::doNotifyNoFocusedWindowAnrLockedInterruptible(CommandEntry* commandEntry) {
+ mLock.unlock();
- sp<Connection> connection = getConnectionLocked(connectionToken);
- if (connection == nullptr) {
- // It's possible that the connection already disappeared. No action necessary.
- return;
- }
+ mPolicy->notifyNoFocusedWindowAnr(commandEntry->inputApplicationHandle);
- ALOGI("Raised ANR, but the policy wants to keep waiting on %s for %" PRId64 "ms longer",
- connection->inputChannel->getName().c_str(), ns2ms(timeoutExtension));
+ mLock.lock();
+}
- connection->responsive = true;
- const nsecs_t newTimeout = now() + timeoutExtension;
- for (DispatchEntry* entry : connection->waitQueue) {
- if (newTimeout >= entry->timeoutTime) {
- // Already removed old entries when connection was marked unresponsive
- entry->timeoutTime = newTimeout;
- mAnrTracker.insert(entry->timeoutTime, connectionToken);
- }
- }
+void InputDispatcher::doNotifyWindowUnresponsiveLockedInterruptible(CommandEntry* commandEntry) {
+ mLock.unlock();
+
+ mPolicy->notifyWindowUnresponsive(commandEntry->connectionToken, commandEntry->reason);
+
+ mLock.lock();
+}
+
+void InputDispatcher::doNotifyMonitorUnresponsiveLockedInterruptible(CommandEntry* commandEntry) {
+ mLock.unlock();
+
+ mPolicy->notifyMonitorUnresponsive(commandEntry->pid, commandEntry->reason);
+
+ mLock.lock();
+}
+
+void InputDispatcher::doNotifyWindowResponsiveLockedInterruptible(CommandEntry* commandEntry) {
+ mLock.unlock();
+
+ mPolicy->notifyWindowResponsive(commandEntry->connectionToken);
+
+ mLock.lock();
+}
+
+void InputDispatcher::doNotifyMonitorResponsiveLockedInterruptible(CommandEntry* commandEntry) {
+ mLock.unlock();
+
+ mPolicy->notifyMonitorResponsive(commandEntry->pid);
+
+ mLock.lock();
+}
+
+void InputDispatcher::doNotifyUntrustedTouchLockedInterruptible(CommandEntry* commandEntry) {
+ mLock.unlock();
+
+ mPolicy->notifyUntrustedTouch(commandEntry->obscuringPackage);
+
+ mLock.lock();
}
void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
CommandEntry* commandEntry) {
- KeyEntry* entry = commandEntry->keyEntry;
- KeyEvent event = createKeyEvent(*entry);
+ KeyEntry& entry = *(commandEntry->keyEntry);
+ KeyEvent event = createKeyEvent(entry);
mLock.unlock();
android::base::Timer t;
- sp<IBinder> token = commandEntry->inputChannel != nullptr
- ? commandEntry->inputChannel->getConnectionToken()
- : nullptr;
- nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token, &event, entry->policyFlags);
+ const sp<IBinder>& token = commandEntry->connectionToken;
+ nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token, &event, entry.policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptKeyBeforeDispatching; took %s ms",
std::to_string(t.duration().count()).c_str());
@@ -4753,14 +5540,13 @@
mLock.lock();
if (delay < 0) {
- entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP;
+ entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP;
} else if (!delay) {
- entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
+ entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
} else {
- entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER;
- entry->interceptKeyWakeupTime = now() + delay;
+ entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER;
+ entry.interceptKeyWakeupTime = now() + delay;
}
- entry->release();
}
void InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) {
@@ -4804,11 +5590,11 @@
bool restartEvent;
if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) {
- KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
+ KeyEntry& keyEntry = static_cast<KeyEntry&>(*(dispatchEntry->eventEntry));
restartEvent =
afterKeyEventLockedInterruptible(connection, dispatchEntry, keyEntry, handled);
} else if (dispatchEntry->eventEntry->type == EventEntry::Type::MOTION) {
- MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry);
+ MotionEntry& motionEntry = static_cast<MotionEntry&>(*(dispatchEntry->eventEntry));
restartEvent = afterMotionEventLockedInterruptible(connection, dispatchEntry, motionEntry,
handled);
} else {
@@ -4823,10 +5609,14 @@
if (dispatchEntryIt != connection->waitQueue.end()) {
dispatchEntry = *dispatchEntryIt;
connection->waitQueue.erase(dispatchEntryIt);
- mAnrTracker.erase(dispatchEntry->timeoutTime,
- connection->inputChannel->getConnectionToken());
+ const sp<IBinder>& connectionToken = connection->inputChannel->getConnectionToken();
+ mAnrTracker.erase(dispatchEntry->timeoutTime, connectionToken);
if (!connection->responsive) {
connection->responsive = isConnectionResponsive(*connection);
+ if (connection->responsive) {
+ // The connection was unresponsive, and now it's responsive.
+ processConnectionResponsiveLocked(*connection);
+ }
}
traceWaitQueueLength(connection);
if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
@@ -4841,22 +5631,98 @@
startDispatchCycleLocked(now(), connection);
}
+void InputDispatcher::sendMonitorUnresponsiveCommandLocked(int32_t pid, std::string reason) {
+ std::unique_ptr<CommandEntry> monitorUnresponsiveCommand = std::make_unique<CommandEntry>(
+ &InputDispatcher::doNotifyMonitorUnresponsiveLockedInterruptible);
+ monitorUnresponsiveCommand->pid = pid;
+ monitorUnresponsiveCommand->reason = std::move(reason);
+ postCommandLocked(std::move(monitorUnresponsiveCommand));
+}
+
+void InputDispatcher::sendWindowUnresponsiveCommandLocked(sp<IBinder> connectionToken,
+ std::string reason) {
+ std::unique_ptr<CommandEntry> windowUnresponsiveCommand = std::make_unique<CommandEntry>(
+ &InputDispatcher::doNotifyWindowUnresponsiveLockedInterruptible);
+ windowUnresponsiveCommand->connectionToken = std::move(connectionToken);
+ windowUnresponsiveCommand->reason = std::move(reason);
+ postCommandLocked(std::move(windowUnresponsiveCommand));
+}
+
+void InputDispatcher::sendMonitorResponsiveCommandLocked(int32_t pid) {
+ std::unique_ptr<CommandEntry> monitorResponsiveCommand = std::make_unique<CommandEntry>(
+ &InputDispatcher::doNotifyMonitorResponsiveLockedInterruptible);
+ monitorResponsiveCommand->pid = pid;
+ postCommandLocked(std::move(monitorResponsiveCommand));
+}
+
+void InputDispatcher::sendWindowResponsiveCommandLocked(sp<IBinder> connectionToken) {
+ std::unique_ptr<CommandEntry> windowResponsiveCommand = std::make_unique<CommandEntry>(
+ &InputDispatcher::doNotifyWindowResponsiveLockedInterruptible);
+ windowResponsiveCommand->connectionToken = std::move(connectionToken);
+ postCommandLocked(std::move(windowResponsiveCommand));
+}
+
+/**
+ * Tell the policy that a connection has become unresponsive so that it can start ANR.
+ * Check whether the connection of interest is a monitor or a window, and add the corresponding
+ * command entry to the command queue.
+ */
+void InputDispatcher::processConnectionUnresponsiveLocked(const Connection& connection,
+ std::string reason) {
+ const sp<IBinder>& connectionToken = connection.inputChannel->getConnectionToken();
+ if (connection.monitor) {
+ ALOGW("Monitor %s is unresponsive: %s", connection.inputChannel->getName().c_str(),
+ reason.c_str());
+ std::optional<int32_t> pid = findMonitorPidByTokenLocked(connectionToken);
+ if (!pid.has_value()) {
+ ALOGE("Could not find unresponsive monitor for connection %s",
+ connection.inputChannel->getName().c_str());
+ return;
+ }
+ sendMonitorUnresponsiveCommandLocked(pid.value(), std::move(reason));
+ return;
+ }
+ // If not a monitor, must be a window
+ ALOGW("Window %s is unresponsive: %s", connection.inputChannel->getName().c_str(),
+ reason.c_str());
+ sendWindowUnresponsiveCommandLocked(connectionToken, std::move(reason));
+}
+
+/**
+ * Tell the policy that a connection has become responsive so that it can stop ANR.
+ */
+void InputDispatcher::processConnectionResponsiveLocked(const Connection& connection) {
+ const sp<IBinder>& connectionToken = connection.inputChannel->getConnectionToken();
+ if (connection.monitor) {
+ std::optional<int32_t> pid = findMonitorPidByTokenLocked(connectionToken);
+ if (!pid.has_value()) {
+ ALOGE("Could not find responsive monitor for connection %s",
+ connection.inputChannel->getName().c_str());
+ return;
+ }
+ sendMonitorResponsiveCommandLocked(pid.value());
+ return;
+ }
+ // If not a monitor, must be a window
+ sendWindowResponsiveCommandLocked(connectionToken);
+}
+
bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& connection,
DispatchEntry* dispatchEntry,
- KeyEntry* keyEntry, bool handled) {
- if (keyEntry->flags & AKEY_EVENT_FLAG_FALLBACK) {
+ KeyEntry& keyEntry, bool handled) {
+ if (keyEntry.flags & AKEY_EVENT_FLAG_FALLBACK) {
if (!handled) {
// Report the key as unhandled, since the fallback was not handled.
- mReporter->reportUnhandledKey(keyEntry->id);
+ mReporter->reportUnhandledKey(keyEntry.id);
}
return false;
}
// Get the fallback key state.
// Clear it out after dispatching the UP.
- int32_t originalKeyCode = keyEntry->keyCode;
+ int32_t originalKeyCode = keyEntry.keyCode;
int32_t fallbackKeyCode = connection->inputState.getFallbackKey(originalKeyCode);
- if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
+ if (keyEntry.action == AKEY_EVENT_ACTION_UP) {
connection->inputState.removeFallbackKey(originalKeyCode);
}
@@ -4869,16 +5735,15 @@
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("Unhandled key event: Asking policy to cancel fallback action. "
"keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
- keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount,
- keyEntry->policyFlags);
+ keyEntry.keyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags);
#endif
- KeyEvent event = createKeyEvent(*keyEntry);
+ KeyEvent event = createKeyEvent(keyEntry);
event.setFlags(event.getFlags() | AKEY_EVENT_FLAG_CANCELED);
mLock.unlock();
mPolicy->dispatchUnhandledKey(connection->inputChannel->getConnectionToken(), &event,
- keyEntry->policyFlags, &event);
+ keyEntry.policyFlags, &event);
mLock.lock();
@@ -4897,13 +5762,13 @@
// If the application did not handle a non-fallback key, first check
// that we are in a good state to perform unhandled key event processing
// Then ask the policy what to do with it.
- bool initialDown = keyEntry->action == AKEY_EVENT_ACTION_DOWN && keyEntry->repeatCount == 0;
+ bool initialDown = keyEntry.action == AKEY_EVENT_ACTION_DOWN && keyEntry.repeatCount == 0;
if (fallbackKeyCode == -1 && !initialDown) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("Unhandled key event: Skipping unhandled key event processing "
"since this is not an initial down. "
"keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
- originalKeyCode, keyEntry->action, keyEntry->repeatCount, keyEntry->policyFlags);
+ originalKeyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags);
#endif
return false;
}
@@ -4912,15 +5777,15 @@
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("Unhandled key event: Asking policy to perform fallback action. "
"keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
- keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount, keyEntry->policyFlags);
+ keyEntry.keyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags);
#endif
- KeyEvent event = createKeyEvent(*keyEntry);
+ KeyEvent event = createKeyEvent(keyEntry);
mLock.unlock();
bool fallback =
mPolicy->dispatchUnhandledKey(connection->inputChannel->getConnectionToken(),
- &event, keyEntry->policyFlags, &event);
+ &event, keyEntry.policyFlags, &event);
mLock.lock();
@@ -4968,7 +5833,7 @@
fallback = false;
fallbackKeyCode = AKEYCODE_UNKNOWN;
- if (keyEntry->action != AKEY_EVENT_ACTION_UP) {
+ if (keyEntry.action != AKEY_EVENT_ACTION_UP) {
connection->inputState.setFallbackKey(originalKeyCode, fallbackKeyCode);
}
}
@@ -4988,22 +5853,22 @@
if (fallback) {
// Restart the dispatch cycle using the fallback key.
- keyEntry->eventTime = event.getEventTime();
- keyEntry->deviceId = event.getDeviceId();
- keyEntry->source = event.getSource();
- keyEntry->displayId = event.getDisplayId();
- keyEntry->flags = event.getFlags() | AKEY_EVENT_FLAG_FALLBACK;
- keyEntry->keyCode = fallbackKeyCode;
- keyEntry->scanCode = event.getScanCode();
- keyEntry->metaState = event.getMetaState();
- keyEntry->repeatCount = event.getRepeatCount();
- keyEntry->downTime = event.getDownTime();
- keyEntry->syntheticRepeat = false;
+ keyEntry.eventTime = event.getEventTime();
+ keyEntry.deviceId = event.getDeviceId();
+ keyEntry.source = event.getSource();
+ keyEntry.displayId = event.getDisplayId();
+ keyEntry.flags = event.getFlags() | AKEY_EVENT_FLAG_FALLBACK;
+ keyEntry.keyCode = fallbackKeyCode;
+ keyEntry.scanCode = event.getScanCode();
+ keyEntry.metaState = event.getMetaState();
+ keyEntry.repeatCount = event.getRepeatCount();
+ keyEntry.downTime = event.getDownTime();
+ keyEntry.syntheticRepeat = false;
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("Unhandled key event: Dispatching fallback key. "
"originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
- originalKeyCode, fallbackKeyCode, keyEntry->metaState);
+ originalKeyCode, fallbackKeyCode, keyEntry.metaState);
#endif
return true; // restart the event
} else {
@@ -5012,7 +5877,7 @@
#endif
// Report the key as unhandled, since there is no fallback key.
- mReporter->reportUnhandledKey(keyEntry->id);
+ mReporter->reportUnhandledKey(keyEntry.id);
}
}
return false;
@@ -5020,26 +5885,19 @@
bool InputDispatcher::afterMotionEventLockedInterruptible(const sp<Connection>& connection,
DispatchEntry* dispatchEntry,
- MotionEntry* motionEntry, bool handled) {
+ MotionEntry& motionEntry, bool handled) {
return false;
}
void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) {
mLock.unlock();
- mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->userActivityEventType);
+ mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->userActivityEventType,
+ commandEntry->displayId);
mLock.lock();
}
-KeyEvent InputDispatcher::createKeyEvent(const KeyEntry& entry) {
- KeyEvent event;
- event.initialize(entry.id, entry.deviceId, entry.source, entry.displayId, INVALID_HMAC,
- entry.action, entry.flags, entry.keyCode, entry.scanCode, entry.metaState,
- entry.repeatCount, entry.downTime, entry.eventTime);
- return event;
-}
-
void InputDispatcher::reportDispatchStatistics(std::chrono::nanoseconds eventDuration,
const Connection& connection, bool handled) {
// TODO Write some statistics about how long we spend waiting.
@@ -5132,4 +5990,105 @@
return result == std::cv_status::no_timeout;
}
+/**
+ * Sets focus to the window identified by the token. This must be called
+ * after updating any input window handles.
+ *
+ * Params:
+ * request.token - input channel token used to identify the window that should gain focus.
+ * request.focusedToken - the token that the caller expects currently to be focused. If the
+ * specified token does not match the currently focused window, this request will be dropped.
+ * If the specified focused token matches the currently focused window, the call will succeed.
+ * Set this to "null" if this call should succeed no matter what the currently focused token is.
+ * request.timestamp - SYSTEM_TIME_MONOTONIC timestamp in nanos set by the client (wm)
+ * when requesting the focus change. This determines which request gets
+ * precedence if there is a focus change request from another source such as pointer down.
+ */
+void InputDispatcher::setFocusedWindow(const FocusRequest& request) {
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+ std::optional<FocusResolver::FocusChanges> changes =
+ mFocusResolver.setFocusedWindow(request, getWindowHandlesLocked(request.displayId));
+ if (changes) {
+ onFocusChangedLocked(*changes);
+ }
+ } // release lock
+ // Wake up poll loop since it may need to make new input dispatching choices.
+ mLooper->wake();
+}
+
+void InputDispatcher::onFocusChangedLocked(const FocusResolver::FocusChanges& changes) {
+ if (changes.oldFocus) {
+ std::shared_ptr<InputChannel> focusedInputChannel = getInputChannelLocked(changes.oldFocus);
+ if (focusedInputChannel) {
+ CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
+ "focus left window");
+ synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options);
+ enqueueFocusEventLocked(changes.oldFocus, false /*hasFocus*/, changes.reason);
+ }
+ }
+ if (changes.newFocus) {
+ enqueueFocusEventLocked(changes.newFocus, true /*hasFocus*/, changes.reason);
+ }
+
+ // If a window has pointer capture, then it must have focus. We need to ensure that this
+ // contract is upheld when pointer capture is being disabled due to a loss of window focus.
+ // If the window loses focus before it loses pointer capture, then the window can be in a state
+ // where it has pointer capture but not focus, violating the contract. Therefore we must
+ // dispatch the pointer capture event before the focus event. Since focus events are added to
+ // the front of the queue (above), we add the pointer capture event to the front of the queue
+ // after the focus events are added. This ensures the pointer capture event ends up at the
+ // front.
+ disablePointerCaptureForcedLocked();
+
+ if (mFocusedDisplayId == changes.displayId) {
+ notifyFocusChangedLocked(changes.oldFocus, changes.newFocus);
+ }
+}
+
+void InputDispatcher::disablePointerCaptureForcedLocked() {
+ if (!mFocusedWindowRequestedPointerCapture && !mWindowTokenWithPointerCapture) {
+ return;
+ }
+
+ ALOGD_IF(DEBUG_FOCUS, "Disabling Pointer Capture because the window lost focus.");
+
+ if (mFocusedWindowRequestedPointerCapture) {
+ mFocusedWindowRequestedPointerCapture = false;
+ setPointerCaptureLocked(false);
+ }
+
+ if (!mWindowTokenWithPointerCapture) {
+ // No need to send capture changes because no window has capture.
+ return;
+ }
+
+ if (mPendingEvent != nullptr) {
+ // Move the pending event to the front of the queue. This will give the chance
+ // for the pending event to be dropped if it is a captured event.
+ mInboundQueue.push_front(mPendingEvent);
+ mPendingEvent = nullptr;
+ }
+
+ auto entry = std::make_unique<PointerCaptureChangedEntry>(mIdGenerator.nextId(), now(),
+ false /* hasCapture */);
+ mInboundQueue.push_front(std::move(entry));
+}
+
+void InputDispatcher::setPointerCaptureLocked(bool enabled) {
+ std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
+ &InputDispatcher::doSetPointerCaptureLockedInterruptible);
+ commandEntry->enabled = enabled;
+ postCommandLocked(std::move(commandEntry));
+}
+
+void InputDispatcher::doSetPointerCaptureLockedInterruptible(
+ android::inputdispatcher::CommandEntry* commandEntry) {
+ mLock.unlock();
+
+ mPolicy->setPointerCapture(commandEntry->enabled);
+
+ mLock.lock();
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 31fad1f..7ab4fd7 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -19,7 +19,9 @@
#include "AnrTracker.h"
#include "CancelationOptions.h"
+#include "DragState.h"
#include "Entry.h"
+#include "FocusResolver.h"
#include "InjectionState.h"
#include "InputDispatcherConfiguration.h"
#include "InputDispatcherInterface.h"
@@ -31,6 +33,8 @@
#include "TouchState.h"
#include "TouchedWindow.h"
+#include <attestation/HmacKeyManager.h>
+#include <com/android/internal/compat/IPlatformCompatNative.h>
#include <input/Input.h>
#include <input/InputApplication.h>
#include <input/InputTransport.h>
@@ -49,6 +53,7 @@
#include <deque>
#include <optional>
#include <unordered_map>
+#include <unordered_set>
#include <InputListener.h>
#include <InputReporterInterface.h>
@@ -57,16 +62,6 @@
class Connection;
-class HmacKeyManager {
-public:
- HmacKeyManager();
- std::array<uint8_t, 32> sign(const VerifiedInputEvent& event) const;
-
-private:
- std::array<uint8_t, 32> sign(const uint8_t* data, size_t size) const;
- const std::array<uint8_t, 128> mHmacKey;
-};
-
/* Dispatches events to input targets. Some functions of the input dispatcher, such as
* identifying input targets, are controlled by a separate policy object.
*
@@ -101,12 +96,15 @@
virtual void notifyKey(const NotifyKeyArgs* args) override;
virtual void notifyMotion(const NotifyMotionArgs* args) override;
virtual void notifySwitch(const NotifySwitchArgs* args) override;
+ virtual void notifySensor(const NotifySensorArgs* args) override;
+ virtual void notifyVibratorState(const NotifyVibratorStateArgs* args) override;
virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
+ virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
- virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid,
- int32_t injectorUid, int32_t syncMode,
- std::chrono::milliseconds timeout,
- uint32_t policyFlags) override;
+ virtual android::os::InputEventInjectionResult injectInputEvent(
+ const InputEvent* event, int32_t injectorPid, int32_t injectorUid,
+ android::os::InputEventInjectionSync syncMode, std::chrono::milliseconds timeout,
+ uint32_t policyFlags) override;
virtual std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) override;
@@ -114,20 +112,31 @@
const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>&
handlesPerDisplay) override;
virtual void setFocusedApplication(
- int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) override;
+ int32_t displayId,
+ const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) override;
virtual void setFocusedDisplay(int32_t displayId) override;
virtual void setInputDispatchMode(bool enabled, bool frozen) override;
virtual void setInputFilterEnabled(bool enabled) override;
virtual void setInTouchMode(bool inTouchMode) override;
+ virtual void setMaximumObscuringOpacityForTouch(float opacity) override;
+ virtual void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) override;
- virtual bool transferTouchFocus(const sp<IBinder>& fromToken,
- const sp<IBinder>& toToken) override;
+ virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
+ bool isDragDrop = false) override;
- virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel) override;
- virtual status_t registerInputMonitor(const sp<InputChannel>& inputChannel, int32_t displayId,
- bool isGestureMonitor) override;
- virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) override;
+ virtual base::Result<std::unique_ptr<InputChannel>> createInputChannel(
+ const std::string& name) override;
+ virtual void setFocusedWindow(const FocusRequest&) override;
+ virtual base::Result<std::unique_ptr<InputChannel>> createInputMonitor(int32_t displayId,
+ bool isGestureMonitor,
+ const std::string& name,
+ int32_t pid) override;
+ virtual status_t removeInputChannel(const sp<IBinder>& connectionToken) override;
virtual status_t pilferPointers(const sp<IBinder>& token) override;
+ virtual void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) override;
+ virtual bool flushSensor(int deviceId, InputDeviceSensorType sensorType) override;
+
+ std::array<uint8_t, 32> sign(const VerifiedInputEvent& event) const;
private:
enum class DropReason {
@@ -137,6 +146,7 @@
DISABLED,
BLOCKED,
STALE,
+ NO_POINTER_CAPTURE,
};
std::unique_ptr<InputThread> mThread;
@@ -151,9 +161,9 @@
sp<Looper> mLooper;
- EventEntry* mPendingEvent GUARDED_BY(mLock);
- std::deque<EventEntry*> mInboundQueue GUARDED_BY(mLock);
- std::deque<EventEntry*> mRecentQueue GUARDED_BY(mLock);
+ std::shared_ptr<EventEntry> mPendingEvent GUARDED_BY(mLock);
+ std::deque<std::shared_ptr<EventEntry>> mInboundQueue GUARDED_BY(mLock);
+ std::deque<std::shared_ptr<EventEntry>> mRecentQueue GUARDED_BY(mLock);
std::deque<std::unique_ptr<CommandEntry>> mCommandQueue GUARDED_BY(mLock);
DropReason mLastDropReason GUARDED_BY(mLock);
@@ -168,16 +178,20 @@
void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) REQUIRES(mLock);
// Enqueues an inbound event. Returns true if mLooper->wake() should be called.
- bool enqueueInboundEventLocked(EventEntry* entry) REQUIRES(mLock);
+ bool enqueueInboundEventLocked(std::unique_ptr<EventEntry> entry) REQUIRES(mLock);
// Cleans up input state when dropping an inbound event.
void dropInboundEventLocked(const EventEntry& entry, DropReason dropReason) REQUIRES(mLock);
// Enqueues a focus event.
- void enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus) REQUIRES(mLock);
+ void enqueueFocusEventLocked(const sp<IBinder>& windowToken, bool hasFocus,
+ const std::string& reason) REQUIRES(mLock);
+ // Enqueues a drag event.
+ void enqueueDragEventLocked(const sp<InputWindowHandle>& windowToken, bool isExiting,
+ const MotionEntry& motionEntry) REQUIRES(mLock);
// Adds an event to a queue of recent events for debugging purposes.
- void addRecentEventLocked(EventEntry* entry) REQUIRES(mLock);
+ void addRecentEventLocked(std::shared_ptr<EventEntry> entry) REQUIRES(mLock);
// App switch latency optimization.
bool mAppSwitchSawKeyDown GUARDED_BY(mLock);
@@ -189,12 +203,13 @@
// Blocked event latency optimization. Drops old events when the user intends
// to transfer focus to a new application.
- EventEntry* mNextUnblockedEvent GUARDED_BY(mLock);
+ std::shared_ptr<EventEntry> mNextUnblockedEvent GUARDED_BY(mLock);
sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y,
TouchState* touchState,
bool addOutsideTargets = false,
- bool addPortalWindows = false) REQUIRES(mLock);
+ bool addPortalWindows = false,
+ bool ignoreDragWindow = false) REQUIRES(mLock);
// All registered connections mapped by channel file descriptor.
std::unordered_map<int, sp<Connection>> mConnectionsByFd GUARDED_BY(mLock);
@@ -202,19 +217,22 @@
sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const
REQUIRES(mLock);
+ std::string getConnectionNameLocked(const sp<IBinder>& connectionToken) const REQUIRES(mLock);
+
void removeConnectionLocked(const sp<Connection>& connection) REQUIRES(mLock);
- struct IBinderHash {
- std::size_t operator()(const sp<IBinder>& b) const {
- return std::hash<IBinder*>{}(b.get());
- }
+ template <typename T>
+ struct StrongPointerHash {
+ std::size_t operator()(const sp<T>& b) const { return std::hash<T*>{}(b.get()); }
};
- std::unordered_map<sp<IBinder>, sp<InputChannel>, IBinderHash> mInputChannelsByToken
- GUARDED_BY(mLock);
+ std::unordered_map<sp<IBinder>, std::shared_ptr<InputChannel>, StrongPointerHash<IBinder>>
+ mInputChannelsByToken GUARDED_BY(mLock);
// Finds the display ID of the gesture monitor identified by the provided token.
std::optional<int32_t> findGestureMonitorDisplayByTokenLocked(const sp<IBinder>& token)
REQUIRES(mLock);
+ // Find a monitor pid by the provided token.
+ std::optional<int32_t> findMonitorPidByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock);
// Input channels that will receive a copy of all input events sent to the provided display.
std::unordered_map<int32_t, std::vector<Monitor>> mGlobalMonitorsByDisplay GUARDED_BY(mLock);
@@ -234,20 +252,21 @@
// Event injection and synchronization.
std::condition_variable mInjectionResultAvailable;
bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid);
- void setInjectionResult(EventEntry* entry, int32_t injectionResult);
+ void setInjectionResult(EventEntry& entry,
+ android::os::InputEventInjectionResult injectionResult);
std::condition_variable mInjectionSyncFinished;
- void incrementPendingForegroundDispatches(EventEntry* entry);
- void decrementPendingForegroundDispatches(EventEntry* entry);
+ void incrementPendingForegroundDispatches(EventEntry& entry);
+ void decrementPendingForegroundDispatches(EventEntry& entry);
// Key repeat tracking.
struct KeyRepeatState {
- KeyEntry* lastKeyEntry; // or null if no repeat
+ std::shared_ptr<KeyEntry> lastKeyEntry; // or null if no repeat
nsecs_t nextRepeatTime;
} mKeyRepeatState GUARDED_BY(mLock);
void resetKeyRepeatLocked() REQUIRES(mLock);
- KeyEntry* synthesizeKeyRepeatLocked(nsecs_t currentTime) REQUIRES(mLock);
+ std::shared_ptr<KeyEntry> synthesizeKeyRepeatLocked(nsecs_t currentTime) REQUIRES(mLock);
// Key replacement tracking
struct KeyReplacement {
@@ -274,7 +293,7 @@
void postCommandLocked(std::unique_ptr<CommandEntry> commandEntry) REQUIRES(mLock);
nsecs_t processAnrsLocked() REQUIRES(mLock);
- nsecs_t getDispatchingTimeoutLocked(const sp<IBinder>& token) REQUIRES(mLock);
+ std::chrono::nanoseconds getDispatchingTimeoutLocked(const sp<IBinder>& token) REQUIRES(mLock);
// Input filter processing.
bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) REQUIRES(mLock);
@@ -283,26 +302,36 @@
// Inbound event processing.
void drainInboundQueueLocked() REQUIRES(mLock);
void releasePendingEventLocked() REQUIRES(mLock);
- void releaseInboundEventLocked(EventEntry* entry) REQUIRES(mLock);
+ void releaseInboundEventLocked(std::shared_ptr<EventEntry> entry) REQUIRES(mLock);
// Dispatch state.
bool mDispatchEnabled GUARDED_BY(mLock);
bool mDispatchFrozen GUARDED_BY(mLock);
bool mInputFilterEnabled GUARDED_BY(mLock);
bool mInTouchMode GUARDED_BY(mLock);
+ float mMaximumObscuringOpacityForTouch GUARDED_BY(mLock);
+ android::os::BlockUntrustedTouchesMode mBlockUntrustedTouchesMode GUARDED_BY(mLock);
std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mWindowHandlesByDisplay
GUARDED_BY(mLock);
void setInputWindowsLocked(const std::vector<sp<InputWindowHandle>>& inputWindowHandles,
int32_t displayId) REQUIRES(mLock);
- // Get window handles by display, return an empty vector if not found.
- std::vector<sp<InputWindowHandle>> getWindowHandlesLocked(int32_t displayId) const
+ // Get a reference to window handles by display, return an empty vector if not found.
+ const std::vector<sp<InputWindowHandle>>& getWindowHandlesLocked(int32_t displayId) const
REQUIRES(mLock);
sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken) const
REQUIRES(mLock);
+
+ // Same function as above, but faster. Since displayId is provided, this avoids the need
+ // to loop through all displays.
+ sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken,
+ int displayId) const REQUIRES(mLock);
+ sp<InputWindowHandle> getWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const
+ REQUIRES(mLock);
+ std::shared_ptr<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const
+ REQUIRES(mLock);
sp<InputWindowHandle> getFocusedWindowHandleLocked(int displayId) const REQUIRES(mLock);
- sp<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const REQUIRES(mLock);
- bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
+ bool hasResponsiveConnectionLocked(InputWindowHandle& windowHandle) const REQUIRES(mLock);
/*
* Validate and update InputWindowHandles for a given display.
@@ -311,34 +340,61 @@
const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId)
REQUIRES(mLock);
- // Focus tracking for keys, trackball, etc.
- std::unordered_map<int32_t, sp<InputWindowHandle>> mFocusedWindowHandlesByDisplay
- GUARDED_BY(mLock);
-
std::unordered_map<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock);
+ std::unique_ptr<DragState> mDragState GUARDED_BY(mLock);
// Focused applications.
- std::unordered_map<int32_t, sp<InputApplicationHandle>> mFocusedApplicationHandlesByDisplay
- GUARDED_BY(mLock);
+ std::unordered_map<int32_t, std::shared_ptr<InputApplicationHandle>>
+ mFocusedApplicationHandlesByDisplay GUARDED_BY(mLock);
// Top focused display.
int32_t mFocusedDisplayId GUARDED_BY(mLock);
+ // Keeps track of the focused window per display and determines focus changes.
+ FocusResolver mFocusResolver GUARDED_BY(mLock);
+ // Whether the focused window on the focused display has requested Pointer Capture.
+ // The state of this variable should always be in sync with the state of Pointer Capture in the
+ // policy, which is updated through setPointerCaptureLocked(enabled).
+ bool mFocusedWindowRequestedPointerCapture GUARDED_BY(mLock);
+
+ // The window token that has Pointer Capture.
+ // This should be in sync with PointerCaptureChangedEvents dispatched to the input channel.
+ sp<IBinder> mWindowTokenWithPointerCapture GUARDED_BY(mLock);
+
+ // Disable Pointer Capture as a result of loss of window focus.
+ void disablePointerCaptureForcedLocked() REQUIRES(mLock);
+
+ // Set the Pointer Capture state in the Policy.
+ void setPointerCaptureLocked(bool enabled) REQUIRES(mLock);
+
// Dispatcher state at time of last ANR.
std::string mLastAnrState GUARDED_BY(mLock);
- // Dispatch inbound events.
- bool dispatchConfigurationChangedLocked(nsecs_t currentTime, ConfigurationChangedEntry* entry)
- REQUIRES(mLock);
- bool dispatchDeviceResetLocked(nsecs_t currentTime, DeviceResetEntry* entry) REQUIRES(mLock);
- bool dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, DropReason* dropReason,
- nsecs_t* nextWakeupTime) REQUIRES(mLock);
- bool dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason,
- nsecs_t* nextWakeupTime) REQUIRES(mLock);
- void dispatchFocusLocked(nsecs_t currentTime, FocusEntry* entry) REQUIRES(mLock);
- void dispatchEventLocked(nsecs_t currentTime, EventEntry* entry,
- const std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
+ // The connection tokens of the channels that the user last interacted, for debugging
+ std::unordered_set<sp<IBinder>, StrongPointerHash<IBinder>> mInteractionConnectionTokens
+ GUARDED_BY(mLock);
+ void updateInteractionTokensLocked(const EventEntry& entry,
+ const std::vector<InputTarget>& targets) REQUIRES(mLock);
+ // Dispatch inbound events.
+ bool dispatchConfigurationChangedLocked(nsecs_t currentTime,
+ const ConfigurationChangedEntry& entry) REQUIRES(mLock);
+ bool dispatchDeviceResetLocked(nsecs_t currentTime, const DeviceResetEntry& entry)
+ REQUIRES(mLock);
+ bool dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<KeyEntry> entry,
+ DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
+ bool dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<MotionEntry> entry,
+ DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
+ void dispatchFocusLocked(nsecs_t currentTime, std::shared_ptr<FocusEntry> entry)
+ REQUIRES(mLock);
+ void dispatchPointerCaptureChangedLocked(
+ nsecs_t currentTime, const std::shared_ptr<PointerCaptureChangedEntry>& entry,
+ DropReason& dropReason) REQUIRES(mLock);
+ void dispatchEventLocked(nsecs_t currentTime, std::shared_ptr<EventEntry> entry,
+ const std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
+ void dispatchSensorLocked(nsecs_t currentTime, std::shared_ptr<SensorEntry> entry,
+ DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
+ void dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<DragEntry> entry) REQUIRES(mLock);
void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry);
void logOutboundMotionDetails(const char* prefix, const MotionEntry& entry);
@@ -366,22 +422,48 @@
* The focused application at the time when no focused window was present.
* Used to raise an ANR when we have no focused window.
*/
- sp<InputApplicationHandle> mAwaitedFocusedApplication GUARDED_BY(mLock);
+ std::shared_ptr<InputApplicationHandle> mAwaitedFocusedApplication GUARDED_BY(mLock);
/**
* The displayId that the focused application is associated with.
*/
int32_t mAwaitedApplicationDisplayId GUARDED_BY(mLock);
void processNoFocusedWindowAnrLocked() REQUIRES(mLock);
+ /**
+ * Tell policy about a window or a monitor that just became unresponsive. Starts ANR.
+ */
+ void processConnectionUnresponsiveLocked(const Connection& connection, std::string reason)
+ REQUIRES(mLock);
+ /**
+ * Tell policy about a window or a monitor that just became responsive.
+ */
+ void processConnectionResponsiveLocked(const Connection& connection) REQUIRES(mLock);
+
+ /**
+ * Post `doNotifyMonitorUnresponsiveLockedInterruptible` command.
+ */
+ void sendMonitorUnresponsiveCommandLocked(int32_t pid, std::string reason) REQUIRES(mLock);
+ /**
+ * Post `doNotifyWindowUnresponsiveLockedInterruptible` command.
+ */
+ void sendWindowUnresponsiveCommandLocked(sp<IBinder> connectionToken, std::string reason)
+ REQUIRES(mLock);
+ /**
+ * Post `doNotifyMonitorResponsiveLockedInterruptible` command.
+ */
+ void sendMonitorResponsiveCommandLocked(int32_t pid) REQUIRES(mLock);
+ /**
+ * Post `doNotifyWindowResponsiveLockedInterruptible` command.
+ */
+ void sendWindowResponsiveCommandLocked(sp<IBinder> connectionToken) REQUIRES(mLock);
+
+
// Optimization: AnrTracker is used to quickly find which connection is due for a timeout next.
// AnrTracker must be kept in-sync with all responsive connection.waitQueues.
// If a connection is not responsive, then the entries should not be added to the AnrTracker.
// Once a connection becomes unresponsive, its entries are removed from AnrTracker to
// prevent unneeded wakeups.
AnrTracker mAnrTracker GUARDED_BY(mLock);
- void extendAnrTimeoutsLocked(const sp<InputApplicationHandle>& application,
- const sp<IBinder>& connectionToken, nsecs_t timeoutExtension)
- REQUIRES(mLock);
// Contains the last window which received a hover event.
sp<InputWindowHandle> mLastHoverWindowHandle GUARDED_BY(mLock);
@@ -396,13 +478,12 @@
void resetNoFocusedWindowTimeoutLocked() REQUIRES(mLock);
int32_t getTargetDisplayId(const EventEntry& entry);
- int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry& entry,
- std::vector<InputTarget>& inputTargets,
- nsecs_t* nextWakeupTime) REQUIRES(mLock);
- int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry& entry,
- std::vector<InputTarget>& inputTargets,
- nsecs_t* nextWakeupTime,
- bool* outConflictingPointerActions) REQUIRES(mLock);
+ android::os::InputEventInjectionResult findFocusedWindowTargetsLocked(
+ nsecs_t currentTime, const EventEntry& entry, std::vector<InputTarget>& inputTargets,
+ nsecs_t* nextWakeupTime) REQUIRES(mLock);
+ android::os::InputEventInjectionResult findTouchedWindowTargetsLocked(
+ nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets,
+ nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) REQUIRES(mLock);
std::vector<TouchedMonitor> findTouchedGestureMonitorsLocked(
int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows) const
REQUIRES(mLock);
@@ -416,14 +497,30 @@
std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId,
float xOffset = 0, float yOffset = 0) REQUIRES(mLock);
-
void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock);
bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
const InjectionState* injectionState);
+ // Enqueue a drag event if needed, and update the touch state.
+ // Uses findTouchedWindowTargetsLocked to make the decision
+ void addDragEventLocked(const MotionEntry& entry) REQUIRES(mLock);
+ void finishDragAndDrop(int32_t displayId, float x, float y) REQUIRES(mLock);
+
+ struct TouchOcclusionInfo {
+ bool hasBlockingOcclusion;
+ float obscuringOpacity;
+ std::string obscuringPackage;
+ int32_t obscuringUid;
+ std::vector<std::string> debugInfo;
+ };
+
+ TouchOcclusionInfo computeTouchOcclusionInfoLocked(const sp<InputWindowHandle>& windowHandle,
+ int32_t x, int32_t y) const REQUIRES(mLock);
+ bool isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const REQUIRES(mLock);
bool isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle, int32_t x,
int32_t y) const REQUIRES(mLock);
bool isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
- std::string getApplicationWindowLabel(const sp<InputApplicationHandle>& applicationHandle,
+ std::string dumpWindowForTouchOcclusion(const InputWindowInfo* info, bool isTouchWindow) const;
+ std::string getApplicationWindowLabel(const InputApplicationHandle* applicationHandle,
const sp<InputWindowHandle>& windowHandle);
// Manage the dispatch cycle for a single connection.
@@ -431,18 +528,18 @@
// with the mutex held makes it easier to ensure that connection invariants are maintained.
// If needed, the methods post commands to run later once the critical bits are done.
void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
- EventEntry* eventEntry, const InputTarget& inputTarget)
+ std::shared_ptr<EventEntry>, const InputTarget& inputTarget)
REQUIRES(mLock);
void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection,
- EventEntry* eventEntry, const InputTarget& inputTarget)
+ std::shared_ptr<EventEntry>, const InputTarget& inputTarget)
REQUIRES(mLock);
- void enqueueDispatchEntryLocked(const sp<Connection>& connection, EventEntry* eventEntry,
+ void enqueueDispatchEntryLocked(const sp<Connection>& connection, std::shared_ptr<EventEntry>,
const InputTarget& inputTarget, int32_t dispatchMode)
REQUIRES(mLock);
void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection)
REQUIRES(mLock);
void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
- uint32_t seq, bool handled) REQUIRES(mLock);
+ uint32_t seq, bool handled, nsecs_t consumeTime) REQUIRES(mLock);
void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
bool notify) REQUIRES(mLock);
void drainDispatchQueue(std::deque<DispatchEntry*>& queue);
@@ -459,8 +556,8 @@
void synthesizeCancelationEventsForMonitorsLocked(
const CancelationOptions& options,
std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) REQUIRES(mLock);
- void synthesizeCancelationEventsForInputChannelLocked(const sp<InputChannel>& channel,
- const CancelationOptions& options)
+ void synthesizeCancelationEventsForInputChannelLocked(
+ const std::shared_ptr<InputChannel>& channel, const CancelationOptions& options)
REQUIRES(mLock);
void synthesizeCancelationEventsForConnectionLocked(const sp<Connection>& connection,
const CancelationOptions& options)
@@ -470,7 +567,8 @@
REQUIRES(mLock);
// Splitting motion events across windows.
- MotionEntry* splitMotionEvent(const MotionEntry& originalMotionEntry, BitSet32 pointerIds);
+ std::unique_ptr<MotionEntry> splitMotionEvent(const MotionEntry& originalMotionEntry,
+ BitSet32 pointerIds);
// Reset and drop everything the dispatcher is doing.
void resetAndDropEverythingLocked(const char* reason) REQUIRES(mLock);
@@ -479,27 +577,32 @@
void dumpDispatchStateLocked(std::string& dump) REQUIRES(mLock);
void dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors);
void logDispatchStateLocked() REQUIRES(mLock);
+ std::string dumpPointerCaptureStateLocked() REQUIRES(mLock);
// Registration.
- void removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) REQUIRES(mLock);
+ void removeMonitorChannelLocked(const sp<IBinder>& connectionToken) REQUIRES(mLock);
void removeMonitorChannelLocked(
- const sp<InputChannel>& inputChannel,
+ const sp<IBinder>& connectionToken,
std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) REQUIRES(mLock);
- status_t unregisterInputChannelLocked(const sp<InputChannel>& inputChannel, bool notify)
+ status_t removeInputChannelLocked(const sp<IBinder>& connectionToken, bool notify)
REQUIRES(mLock);
// Interesting events that we might like to log or tell the framework about.
void onDispatchCycleFinishedLocked(nsecs_t currentTime, const sp<Connection>& connection,
- uint32_t seq, bool handled) REQUIRES(mLock);
+ uint32_t seq, bool handled, nsecs_t consumeTime)
+ REQUIRES(mLock);
void onDispatchCycleBrokenLocked(nsecs_t currentTime, const sp<Connection>& connection)
REQUIRES(mLock);
- void onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
- const sp<InputWindowHandle>& newFocus) REQUIRES(mLock);
- void onAnrLocked(const Connection& connection) REQUIRES(mLock);
- void onAnrLocked(const sp<InputApplicationHandle>& application) REQUIRES(mLock);
+ void onFocusChangedLocked(const FocusResolver::FocusChanges& changes) REQUIRES(mLock);
+ void notifyFocusChangedLocked(const sp<IBinder>& oldFocus, const sp<IBinder>& newFocus)
+ REQUIRES(mLock);
+ void notifyDropWindowLocked(const sp<IBinder>& token, float x, float y) REQUIRES(mLock);
+ void onAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
+ void onAnrLocked(std::shared_ptr<InputApplicationHandle> application) REQUIRES(mLock);
+ void onUntrustedTouchLocked(const std::string& obscuringPackage) REQUIRES(mLock);
void updateLastAnrStateLocked(const sp<InputWindowHandle>& window, const std::string& reason)
REQUIRES(mLock);
- void updateLastAnrStateLocked(const sp<InputApplicationHandle>& application,
+ void updateLastAnrStateLocked(const InputApplicationHandle& application,
const std::string& reason) REQUIRES(mLock);
void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason)
REQUIRES(mLock);
@@ -509,18 +612,28 @@
REQUIRES(mLock);
void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
- void doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+ void doNotifyDropWindowLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+
+ // ANR-related callbacks - start
+ void doNotifyNoFocusedWindowAnrLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+ void doNotifyWindowUnresponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+ void doNotifyMonitorUnresponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+ void doNotifyWindowResponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+ void doNotifyMonitorResponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+ // ANR-related callbacks - end
+ void doNotifySensorLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+ void doNotifyUntrustedTouchLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry)
REQUIRES(mLock);
void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+ void doSetPointerCaptureLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
bool afterKeyEventLockedInterruptible(const sp<Connection>& connection,
- DispatchEntry* dispatchEntry, KeyEntry* keyEntry,
+ DispatchEntry* dispatchEntry, KeyEntry& keyEntry,
bool handled) REQUIRES(mLock);
bool afterMotionEventLockedInterruptible(const sp<Connection>& connection,
- DispatchEntry* dispatchEntry, MotionEntry* motionEntry,
+ DispatchEntry* dispatchEntry, MotionEntry& motionEntry,
bool handled) REQUIRES(mLock);
void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
- KeyEvent createKeyEvent(const KeyEntry& entry);
void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
// Statistics gathering.
@@ -535,6 +648,7 @@
void traceWaitQueueLength(const sp<Connection>& connection);
sp<InputReporterInterface> mReporter;
+ sp<com::android::internal::compat::IPlatformCompatNative> mCompatService;
};
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index 386056d..3bb0bc9 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "input/InputDevice.h"
+
#include "InputState.h"
#include "InputDispatcher.h"
@@ -265,17 +267,18 @@
}
}
-std::vector<EventEntry*> InputState::synthesizeCancelationEvents(
+std::vector<std::unique_ptr<EventEntry>> InputState::synthesizeCancelationEvents(
nsecs_t currentTime, const CancelationOptions& options) {
- std::vector<EventEntry*> events;
+ std::vector<std::unique_ptr<EventEntry>> events;
for (KeyMemento& memento : mKeyMementos) {
if (shouldCancelKey(memento, options)) {
- events.push_back(new KeyEntry(mIdGenerator.nextId(), currentTime, memento.deviceId,
- memento.source, memento.displayId, memento.policyFlags,
- AKEY_EVENT_ACTION_UP,
- memento.flags | AKEY_EVENT_FLAG_CANCELED, memento.keyCode,
- memento.scanCode, memento.metaState, 0 /*repeatCount*/,
- memento.downTime));
+ events.push_back(
+ std::make_unique<KeyEntry>(mIdGenerator.nextId(), currentTime, memento.deviceId,
+ memento.source, memento.displayId,
+ memento.policyFlags, AKEY_EVENT_ACTION_UP,
+ memento.flags | AKEY_EVENT_FLAG_CANCELED,
+ memento.keyCode, memento.scanCode, memento.metaState,
+ 0 /*repeatCount*/, memento.downTime));
}
}
@@ -283,22 +286,26 @@
if (shouldCancelMotion(memento, options)) {
const int32_t action = memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT
: AMOTION_EVENT_ACTION_CANCEL;
- events.push_back(new MotionEntry(mIdGenerator.nextId(), currentTime, memento.deviceId,
- memento.source, memento.displayId, memento.policyFlags,
- action, 0 /*actionButton*/, memento.flags, AMETA_NONE,
- 0 /*buttonState*/, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
- memento.yPrecision, memento.xCursorPosition,
- memento.yCursorPosition, memento.downTime,
- memento.pointerCount, memento.pointerProperties,
- memento.pointerCoords, 0 /*xOffset*/, 0 /*yOffset*/));
+ events.push_back(
+ std::make_unique<MotionEntry>(mIdGenerator.nextId(), currentTime,
+ memento.deviceId, memento.source,
+ memento.displayId, memento.policyFlags, action,
+ 0 /*actionButton*/, memento.flags, AMETA_NONE,
+ 0 /*buttonState*/, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
+ memento.yPrecision, memento.xCursorPosition,
+ memento.yCursorPosition, memento.downTime,
+ memento.pointerCount, memento.pointerProperties,
+ memento.pointerCoords, 0 /*xOffset*/,
+ 0 /*yOffset*/));
}
}
return events;
}
-std::vector<EventEntry*> InputState::synthesizePointerDownEvents(nsecs_t currentTime) {
- std::vector<EventEntry*> events;
+std::vector<std::unique_ptr<EventEntry>> InputState::synthesizePointerDownEvents(
+ nsecs_t currentTime) {
+ std::vector<std::unique_ptr<EventEntry>> events;
for (MotionMemento& memento : mMotionMementos) {
if (!(memento.source & AINPUT_SOURCE_CLASS_POINTER)) {
continue;
@@ -333,15 +340,17 @@
: AMOTION_EVENT_ACTION_POINTER_DOWN
| (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- events.push_back(new MotionEntry(mIdGenerator.nextId(), currentTime, memento.deviceId,
- memento.source, memento.displayId, memento.policyFlags,
- action, 0 /*actionButton*/, memento.flags, AMETA_NONE,
- 0 /*buttonState*/, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
- memento.yPrecision, memento.xCursorPosition,
- memento.yCursorPosition, memento.downTime,
- pointerCount, pointerProperties, pointerCoords,
- 0 /*xOffset*/, 0 /*yOffset*/));
+ events.push_back(
+ std::make_unique<MotionEntry>(mIdGenerator.nextId(), currentTime,
+ memento.deviceId, memento.source,
+ memento.displayId, memento.policyFlags, action,
+ 0 /*actionButton*/, memento.flags, AMETA_NONE,
+ 0 /*buttonState*/, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
+ memento.yPrecision, memento.xCursorPosition,
+ memento.yCursorPosition, memento.downTime,
+ pointerCount, pointerProperties, pointerCoords,
+ 0 /*xOffset*/, 0 /*yOffset*/));
}
memento.firstNewPointerIdx = INVALID_POINTER_INDEX;
diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h
index d97a664..74ae21f 100644
--- a/services/inputflinger/dispatcher/InputState.h
+++ b/services/inputflinger/dispatcher/InputState.h
@@ -51,11 +51,11 @@
bool trackMotion(const MotionEntry& entry, int32_t action, int32_t flags);
// Synthesizes cancelation events for the current state and resets the tracked state.
- std::vector<EventEntry*> synthesizeCancelationEvents(nsecs_t currentTime,
- const CancelationOptions& options);
+ std::vector<std::unique_ptr<EventEntry>> synthesizeCancelationEvents(
+ nsecs_t currentTime, const CancelationOptions& options);
// Synthesizes down events for the current state.
- std::vector<EventEntry*> synthesizePointerDownEvents(nsecs_t currentTime);
+ std::vector<std::unique_ptr<EventEntry>> synthesizePointerDownEvents(nsecs_t currentTime);
// Clears the current state.
void clear();
diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp
index 0588374..d39113b 100644
--- a/services/inputflinger/dispatcher/InputTarget.cpp
+++ b/services/inputflinger/dispatcher/InputTarget.cpp
@@ -42,12 +42,11 @@
return StringPrintf("%" PRId32, dispatchMode);
}
-void InputTarget::addPointers(BitSet32 newPointerIds, float xOffset, float yOffset,
- float windowXScale, float windowYScale) {
+void InputTarget::addPointers(BitSet32 newPointerIds, const ui::Transform& transform) {
// The pointerIds can be empty, but still a valid InputTarget. This can happen for Monitors
// and non splittable windows since we will just use all the pointers from the input event.
if (newPointerIds.isEmpty()) {
- setDefaultPointerInfo(xOffset, yOffset, windowXScale, windowYScale);
+ setDefaultPointerTransform(transform);
return;
}
@@ -57,47 +56,38 @@
pointerIds |= newPointerIds;
while (!newPointerIds.isEmpty()) {
int32_t pointerId = newPointerIds.clearFirstMarkedBit();
- pointerInfos[pointerId].xOffset = xOffset;
- pointerInfos[pointerId].yOffset = yOffset;
- pointerInfos[pointerId].windowXScale = windowXScale;
- pointerInfos[pointerId].windowYScale = windowYScale;
+ pointerTransforms[pointerId] = transform;
}
}
-void InputTarget::setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale,
- float windowYScale) {
+void InputTarget::setDefaultPointerTransform(const ui::Transform& transform) {
pointerIds.clear();
- pointerInfos[0].xOffset = xOffset;
- pointerInfos[0].yOffset = yOffset;
- pointerInfos[0].windowXScale = windowXScale;
- pointerInfos[0].windowYScale = windowYScale;
+ pointerTransforms[0] = transform;
}
-bool InputTarget::useDefaultPointerInfo() const {
+bool InputTarget::useDefaultPointerTransform() const {
return pointerIds.isEmpty();
}
-const PointerInfo& InputTarget::getDefaultPointerInfo() const {
- return pointerInfos[0];
+const ui::Transform& InputTarget::getDefaultPointerTransform() const {
+ return pointerTransforms[0];
}
std::string InputTarget::getPointerInfoString() const {
- if (useDefaultPointerInfo()) {
- const PointerInfo& pointerInfo = getDefaultPointerInfo();
- return StringPrintf("xOffset=%.1f, yOffset=%.1f windowScaleFactor=(%.1f, %.1f)",
- pointerInfo.xOffset, pointerInfo.yOffset, pointerInfo.windowXScale,
- pointerInfo.windowYScale);
+ std::string out = "\n";
+ if (useDefaultPointerTransform()) {
+ const ui::Transform& transform = getDefaultPointerTransform();
+ transform.dump(out, "default", " ");
+ return out;
}
- std::string out;
for (uint32_t i = pointerIds.firstMarkedBit(); i <= pointerIds.lastMarkedBit(); i++) {
if (!pointerIds.hasBit(i)) {
continue;
}
- out += StringPrintf("\n pointerId %d: xOffset=%.1f, yOffset=%.1f "
- "windowScaleFactor=(%.1f, %.1f)",
- i, pointerInfos[i].xOffset, pointerInfos[i].yOffset,
- pointerInfos[i].windowXScale, pointerInfos[i].windowYScale);
+
+ const std::string name = "pointerId " + std::to_string(i) + ":";
+ pointerTransforms[i].dump(out, name.c_str(), " ");
}
return out;
}
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 499a75f..debf805 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -18,28 +18,13 @@
#define _UI_INPUT_INPUTDISPATCHER_INPUTTARGET_H
#include <input/InputTransport.h>
+#include <ui/Transform.h>
#include <utils/BitSet.h>
#include <utils/RefBase.h>
namespace android::inputdispatcher {
/*
- * Information about each pointer for an InputTarget. This includes offset and scale so
- * all pointers can be normalized to a single offset and scale.
- *
- * These values are ignored for KeyEvents
- */
-struct PointerInfo {
- // The x and y offset to add to a MotionEvent as it is delivered.
- float xOffset = 0.0f;
- float yOffset = 0.0f;
-
- // Scaling factor to apply to MotionEvent as it is delivered.
- float windowXScale = 1.0f;
- float windowYScale = 1.0f;
-};
-
-/*
* An input target specifies how an input event is to be dispatched to a particular window
* including the window's input channel, control flags, a timeout, and an X / Y offset to
* be added to input event coordinates to compensate for the absolute position of the
@@ -106,7 +91,7 @@
};
// The input channel to be targeted.
- sp<InputChannel> inputChannel;
+ std::shared_ptr<InputChannel> inputChannel;
// Flags for the input target.
int32_t flags = 0;
@@ -119,13 +104,11 @@
// if FLAG_SPLIT is set.
BitSet32 pointerIds;
// The data is stored by the pointerId. Use the bit position of pointerIds to look up
- // PointerInfo per pointerId.
- PointerInfo pointerInfos[MAX_POINTERS];
+ // Transform per pointerId.
+ ui::Transform pointerTransforms[MAX_POINTERS];
- void addPointers(BitSet32 pointerIds, float xOffset, float yOffset, float windowXScale,
- float windowYScale);
- void setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale,
- float windowYScale);
+ void addPointers(BitSet32 pointerIds, const ui::Transform& transform);
+ void setDefaultPointerTransform(const ui::Transform& transform);
/**
* Returns whether the default pointer information should be used. This will be true when the
@@ -133,13 +116,13 @@
* and non splittable windows since we want all pointers for the EventEntry to go to this
* target.
*/
- bool useDefaultPointerInfo() const;
+ bool useDefaultPointerTransform() const;
/**
- * Returns the default PointerInfo object. This should be used when useDefaultPointerInfo is
+ * Returns the default Transform object. This should be used when useDefaultPointerTransform is
* true.
*/
- const PointerInfo& getDefaultPointerInfo() const;
+ const ui::Transform& getDefaultPointerTransform() const;
std::string getPointerInfoString() const;
};
diff --git a/services/inputflinger/dispatcher/Monitor.cpp b/services/inputflinger/dispatcher/Monitor.cpp
index 289b084..bbce759 100644
--- a/services/inputflinger/dispatcher/Monitor.cpp
+++ b/services/inputflinger/dispatcher/Monitor.cpp
@@ -19,7 +19,8 @@
namespace android::inputdispatcher {
// --- Monitor ---
-Monitor::Monitor(const sp<InputChannel>& inputChannel) : inputChannel(inputChannel) {}
+Monitor::Monitor(const std::shared_ptr<InputChannel>& inputChannel, int32_t pid)
+ : inputChannel(inputChannel), pid(pid) {}
// --- TouchedMonitor ---
TouchedMonitor::TouchedMonitor(const Monitor& monitor, float xOffset, float yOffset)
diff --git a/services/inputflinger/dispatcher/Monitor.h b/services/inputflinger/dispatcher/Monitor.h
index b67c9eb..7be0760 100644
--- a/services/inputflinger/dispatcher/Monitor.h
+++ b/services/inputflinger/dispatcher/Monitor.h
@@ -22,9 +22,11 @@
namespace android::inputdispatcher {
struct Monitor {
- sp<InputChannel> inputChannel; // never null
+ std::shared_ptr<InputChannel> inputChannel; // never null
- explicit Monitor(const sp<InputChannel>& inputChannel);
+ int32_t pid;
+
+ explicit Monitor(const std::shared_ptr<InputChannel>& inputChannel, int32_t pid);
};
// For tracking the offsets we need to apply when adding gesture monitor targets.
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 2baceba..81b3cf0 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -137,8 +137,7 @@
for (const TouchedWindow& window : windows) {
if (window.targetFlags & InputTarget::FLAG_FOREGROUND) {
if (haveSlipperyForegroundWindow ||
- !(window.windowHandle->getInfo()->layoutParamsFlags &
- InputWindowInfo::FLAG_SLIPPERY)) {
+ !window.windowHandle->getInfo()->flags.test(InputWindowInfo::Flag::SLIPPERY)) {
return false;
}
haveSlipperyForegroundWindow = true;
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 9b002f4..b601dfc 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -18,36 +18,21 @@
#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERINTERFACE_H
#include <InputListener.h>
-#include <input/ISetInputWindowsListener.h>
+#include <android-base/result.h>
+#include <android/FocusRequest.h>
+#include <android/os/BlockUntrustedTouchesMode.h>
+#include <android/os/ISetInputWindowsListener.h>
+#include <android/os/InputEventInjectionResult.h>
+#include <android/os/InputEventInjectionSync.h>
+#include <input/InputApplication.h>
+#include <input/InputDevice.h>
+#include <input/InputTransport.h>
+#include <input/InputWindow.h>
#include <unordered_map>
+
namespace android {
-class InputApplicationHandle;
-class InputChannel;
-class InputWindowHandle;
-
-/*
- * Constants used to report the outcome of input event injection.
- */
-enum {
- /* (INTERNAL USE ONLY) Specifies that injection is pending and its outcome is unknown. */
- INPUT_EVENT_INJECTION_PENDING = -1,
-
- /* Injection succeeded. */
- INPUT_EVENT_INJECTION_SUCCEEDED = 0,
-
- /* Injection failed because the injector did not have permission to inject
- * into the application with input focus. */
- INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1,
-
- /* Injection failed because there were no available input targets. */
- INPUT_EVENT_INJECTION_FAILED = 2,
-
- /* Injection failed due to a timeout. */
- INPUT_EVENT_INJECTION_TIMED_OUT = 3
-};
-
/* Notifies the system about input events generated by the input reader.
* The dispatcher is expected to be mostly asynchronous. */
class InputDispatcherInterface : public virtual RefBase, public InputListenerInterface {
@@ -89,9 +74,10 @@
*
* This method may be called on any thread (usually by the input manager).
*/
- virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid,
- int32_t injectorUid, int32_t syncMode,
- std::chrono::milliseconds timeout, uint32_t policyFlags) = 0;
+ virtual android::os::InputEventInjectionResult injectInputEvent(
+ const InputEvent* event, int32_t injectorPid, int32_t injectorUid,
+ android::os::InputEventInjectionSync syncMode, std::chrono::milliseconds timeout,
+ uint32_t policyFlags) = 0;
/*
* Check whether InputEvent actually happened by checking the signature of the event.
@@ -113,7 +99,8 @@
* This method may be called on any thread (usually by the input manager).
*/
virtual void setFocusedApplication(
- int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) = 0;
+ int32_t displayId,
+ const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) = 0;
/* Sets the focused display.
*
@@ -143,19 +130,42 @@
*/
virtual void setInTouchMode(bool inTouchMode) = 0;
+ /**
+ * Sets the maximum allowed obscuring opacity by UID to propagate touches.
+ * For certain window types (eg. SAWs), the decision of honoring
+ * FLAG_NOT_TOUCHABLE or not depends on the combined obscuring opacity of
+ * the windows above the touch-consuming window.
+ */
+ virtual void setMaximumObscuringOpacityForTouch(float opacity) = 0;
+
+ /**
+ * Sets the mode of the block untrusted touches feature.
+ *
+ * TODO(b/169067926): Clean-up feature modes.
+ */
+ virtual void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) = 0;
+
/* Transfers touch focus from one window to another window.
*
* Returns true on success. False if the window did not actually have touch focus.
*/
- virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) = 0;
+ virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
+ bool isDragDrop) = 0;
+ /**
+ * Sets focus on the specified window.
+ */
+ virtual void setFocusedWindow(const FocusRequest&) = 0;
- /* Registers input channels that may be used as targets for input events.
+ /**
+ * Creates an input channel that may be used as targets for input events.
*
* This method may be called on any thread (usually by the input manager).
*/
- virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel) = 0;
+ virtual base::Result<std::unique_ptr<InputChannel>> createInputChannel(
+ const std::string& name) = 0;
- /* Registers input channels to be used to monitor input events.
+ /**
+ * Creates an input channel to be used to monitor input events.
*
* Each monitor must target a specific display and will only receive input events sent to that
* display. If the monitor is a gesture monitor, it will only receive pointer events on the
@@ -163,20 +173,34 @@
*
* This method may be called on any thread (usually by the input manager).
*/
- virtual status_t registerInputMonitor(const sp<InputChannel>& inputChannel, int32_t displayId,
- bool gestureMonitor) = 0;
+ virtual base::Result<std::unique_ptr<InputChannel>> createInputMonitor(int32_t displayId,
+ bool gestureMonitor,
+ const std::string& name,
+ int32_t pid) = 0;
- /* Unregister input channels that will no longer receive input events.
+ /* Removes input channels that will no longer receive input events.
*
* This method may be called on any thread (usually by the input manager).
*/
- virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;
+ virtual status_t removeInputChannel(const sp<IBinder>& connectionToken) = 0;
/* Allows an input monitor steal the current pointer stream away from normal input windows.
*
* This method may be called on any thread (usually by the input manager).
*/
virtual status_t pilferPointers(const sp<IBinder>& token) = 0;
+
+ /**
+ * Enables Pointer Capture on the specified window if the window has focus.
+ *
+ * InputDispatcher is the source of truth of Pointer Capture.
+ */
+ virtual void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) = 0;
+ /* Flush input device motion sensor.
+ *
+ * Returns true on success.
+ */
+ virtual bool flushSensor(int deviceId, InputDeviceSensorType sensorType) = 0;
};
} // namespace android
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 667af9b..219f45a 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -21,11 +21,11 @@
#include <binder/IBinder.h>
#include <input/Input.h>
+#include <input/InputApplication.h>
#include <utils/RefBase.h>
namespace android {
-class InputApplicationHandle;
/*
* Input dispatcher policy interface.
@@ -45,14 +45,49 @@
/* Notifies the system that a configuration change has occurred. */
virtual void notifyConfigurationChanged(nsecs_t when) = 0;
- /* Notifies the system that an application is not responding.
- * Returns a new timeout to continue waiting, or 0 to abort dispatch. */
- virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>& inputApplicationHandle,
- const sp<IBinder>& token, const std::string& reason) = 0;
+ /* Notifies the system that an application does not have a focused window.
+ */
+ virtual void notifyNoFocusedWindowAnr(
+ const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) = 0;
+
+ /* Notifies the system that a window just became unresponsive. This indicates that ANR
+ * should be raised for this window. The window is identified via token.
+ * The string reason contains information about the input event that we haven't received
+ * a response for.
+ */
+ virtual void notifyWindowUnresponsive(const sp<IBinder>& token, const std::string& reason) = 0;
+ /* Notifies the system that a monitor just became unresponsive. This indicates that ANR
+ * should be raised for this monitor. The monitor is identified via its pid.
+ * The string reason contains information about the input event that we haven't received
+ * a response for.
+ */
+ virtual void notifyMonitorUnresponsive(int32_t pid, const std::string& reason) = 0;
+
+ /* Notifies the system that a window just became responsive. This is only called after the
+ * window was first marked "unresponsive". This indicates that ANR dialog (if any) should
+ * no longer should be shown to the user. The window is eligible to cause a new ANR in the
+ * future.
+ */
+ virtual void notifyWindowResponsive(const sp<IBinder>& token) = 0;
+ /* Notifies the system that a monitor just became responsive. This is only called after the
+ * monitor was first marked "unresponsive". This indicates that ANR dialog (if any) should
+ * no longer should be shown to the user. The monitor is eligible to cause a new ANR in the
+ * future.
+ */
+ virtual void notifyMonitorResponsive(int32_t pid) = 0;
/* Notifies the system that an input channel is unrecoverably broken. */
virtual void notifyInputChannelBroken(const sp<IBinder>& token) = 0;
virtual void notifyFocusChanged(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) = 0;
+ virtual void notifySensorEvent(int32_t deviceId, InputDeviceSensorType sensorType,
+ InputDeviceSensorAccuracy accuracy, nsecs_t timestamp,
+ const std::vector<float>& values) = 0;
+ virtual void notifySensorAccuracy(int32_t deviceId, InputDeviceSensorType sensorType,
+ InputDeviceSensorAccuracy accuracy) = 0;
+ virtual void notifyVibratorState(int32_t deviceId, bool isOn) = 0;
+
+ /* Notifies the system that an untrusted touch occurred. */
+ virtual void notifyUntrustedTouch(const std::string& obscuringPackage) = 0;
/* Gets the input dispatcher configuration. */
virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) = 0;
@@ -99,7 +134,7 @@
uint32_t policyFlags) = 0;
/* Poke user activity for an event dispatched to a window. */
- virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) = 0;
+ virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) = 0;
/* Checks whether a given application pid/uid has permission to inject input events
* into other applications.
@@ -116,6 +151,15 @@
* The touchedToken passed as an argument is the window that received the input event.
*/
virtual void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) = 0;
+
+ /* Change the Pointer Capture state in InputReader.
+ *
+ * InputDispatcher is solely responsible for updating the Pointer Capture state.
+ */
+ virtual void setPointerCapture(bool enabled) = 0;
+
+ /* Notifies the policy that the drag window has moved over to another window */
+ virtual void notifyDropWindow(const sp<IBinder>& token, float x, float y) = 0;
};
} // namespace android
diff --git a/services/inputflinger/docs/anr.md b/services/inputflinger/docs/anr.md
new file mode 100644
index 0000000..5b931d6
--- /dev/null
+++ b/services/inputflinger/docs/anr.md
@@ -0,0 +1,73 @@
+# ANR detection in InputDispatcher #
+
+'ANR' means 'application not responding'. This is an event that gets triggered when the system thinks that an application is too slow to respond. A dialog may pop up because of an ANR event. ANRs can be triggered by multiple systems within Android.
+
+In InputDispatcher, ANRs are raised in 2 cases:
+
+1. An event was sent to a connection, and the response was not received within a certain timeout.
+2. The application did not have a focused window, and an input event that requires focus was generated by the user.
+
+Let's consider each of these cases.
+
+## 1. Application does not respond to an input event that was sent to it. ##
+
+The most common case is when an application does not respond to input that dispatcher sent to it. Typically, it means an application is performing a long operation on its UI thread.
+
+When the event is being dispatched to an application, the normal flow is: `mPendingEvent` → `connection.outboundQueue` → `connection.waitQueue`.
+
+Every dispatch cycle, InputDispatcher will check all connections to see if any are unresponsive. To determine whether an app is not responding, we look at the oldest entry in the `waitQueue`. If the entry sits in the `waitQueue` past `entry.timeoutTime`, we trigger an ANR.
+
+
+### Checking if a connection is unresponsive ###
+
+When a dispatch entry is sent to the app, its `deliveryTime` and `timeoutTime` fields are populated. The `deliveryTime` is the time that the event is delivered to the app. This is simply the current time inside `publishMotionEvent`. The `timeoutTime` is the time when this entry would be considered overdue. At that time, the ANR process would start for this connection.
+
+Most connections are associated with a window, and each window may have a custom timeout time. To calculate the timeout time of a specific event, simply add the `window.dispatchingTimeout` to the current time. In case where there is no associated window, such as gesture monitors, use the default dispatching timeout which is defined in `android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS`.
+
+The `timeoutTime` field of the `DispatchEntry` is needed because the window associated with a specific connection may change its timeout time. Therefore, entries sent prior to the timeout change would need to follow the previous timeout value. If a window timeout changes, it only affects new events being dispatched, and does not alter the timeout times of events already sent to the app.
+
+For example, if an application is being debugged, the ActivityManager may want to increase the timeout time for a window to prevent the ANR dialog from appearing or the app from getting killed.
+
+Looping through `waitQueue`s of all connections on every dispatch cycle could be costly. To improve this, we introduced the `AnrTracker` class.
+
+`AnrTracker` uses a multiset (a set that allows duplicate entries) to keep track of the next time a dispatch entry would become out of date. Duplicate entries are allowed because there may be two events with an identical timeout time. This is unlikely to happen in practice today, but is possible if the window timeouts are different or if the device has a high input report rate or a low clock resolution.
+
+On each dispatch cycle, InputDispatcher checks `AnrTracker` for the nearest timeout value. If the nearest timeout value is in the past, InputDispatcher will trigger the ANR for the corresponding connection.
+
+When an application sends a response for a particular dispatch entry, that entry is removed from the connection's `waitQueue`, and it is also removed from the `AnrTracker`. During normal operation, the entries are removed from `AnrTracker` quickly.
+
+
+### How to test ###
+
+In order to test this behaviour, you can create an application that calls `SystemClock.sleep` while handling a click event.
+
+When this happens, the expectation is that the ANR dialog will come up within a short period of sending an input event (typically 5 seconds). While the app is not responding, it is expected that touches on other applications and gesture monitors still continue to work.
+
+
+## 2. Application does not have a focused window, and a focused event comes in ##
+
+This is a legacy behaviour that we are maintaining inside InputDispatcher. When an application is launched, WindowManager calls `setFocusedApplication` to tell InputDispatcher that there is a focused application. This is used by InputDispatcher purely for ANR and debugging purposes.
+
+After launching, an application may not add a focused window. This could be either due to a bug in WindowManager or in the app.
+
+The legacy behaviour in this situation is as follows: touches will continue to function normally, without causing an ANR. If there is a focused event, however, it would require a focused window to be dispatched. InputDispatcher will keep this focused event inside mPendingEvent until:
+
+* A focused window is added
+* Timeout occurs
+* User touches another application
+
+To keep track of this timeout, when this situation is detected initially, `mInputTargetWaitTimeoutTime` and `mAwaitedFocusedApplication` are set. When the `mInputTargetWaitTimeoutTime` expires, an ANR will be raised.
+
+
+### How to test ###
+
+Create an empty application that sets `FLAG_NOT_FOCUSABLE` on its window in `onCreate`. Touching the application's window should not cause an ANR. Sending a key event to the application, however, should cause ANR. One easy way to do this is by pressing or gesturing the BACK key. In this scenario, `adb shell dumpsys input` will reveal that there's no focused window in the current display.
+
+
+## Extending the timeout based on the response from policy ##
+
+When the policy processes the ANR notification and responds with a positive timeout, InputDispatcher marks the connection as "responsive" by setting `inputPublisherBlocked = false`. All of the entries for this connection inside AnrTracker will be modified to expire at `time = (current time) + (timeout extension returned by policy)`.
+
+If the policy wants to abort dispatch, it returns a timeout value of 0. In this case, InputDispatcher will synthesize cancel events for the connection.
+
+When an app is unresponsive, new touches do not go to the app. They get dropped with a warning log. This is done to prevent overwhelming the app with events in case it later becomes responsive.
diff --git a/services/inputflinger/docs/pointer_capture.md b/services/inputflinger/docs/pointer_capture.md
new file mode 100644
index 0000000..8da699d
--- /dev/null
+++ b/services/inputflinger/docs/pointer_capture.md
@@ -0,0 +1,44 @@
+# Pointer Capture in InputFlinger
+
+## Introduction
+
+[Pointer Capture](https://developer.android.com/training/gestures/movement#pointer-capture) is a feature that was introduced to the Android input pipeline in Android 8.0 (Oreo). Pointer Capture can be enabled or disabled for an `InputWindow` through requests to `InputManagerService`. Enabling Pointer Capture performs the following changes related to the mouse cursor and the devices that control it:
+
+- The position of the mouse cursor is fixed to its location before Pointer Capture was enabled.
+- The mouse cursor is hidden.
+- Events from a mouse will be delivered with the source `SOURCE_MOUSE_RELATIVE`, and their `AXIS_X` and `AXIS_Y` will report relative position changes.
+- Events from a touchpad will be delivered with the source `SOURCE_TOUCHPAD`, and their `AXIS_X` and `AXIS_Y` will report the absolute position of each of the pointers on the touchpad.
+- Events from mouse and touchpad devices are dispatched to the focused `InputWindow`.
+- Events from devices that do not normally control the mouse cursor are not affected.
+
+`InputWindow`s can only gain Pointer Capture if they have window focus. If a window with Pointer Capture loses focus, Pointer Capture is disabled.
+
+## Pointer Capture pipeline in InputFlinger
+
+`InputDispatcher` is responsible for controlling the state of Pointer Capture. Since the feature requires changes to how events are generated, Pointer Capture is configured in `InputReader`.
+
+### Enabling Pointer Capture
+
+There are four key steps that take place when Pointer Capture is enabled:
+
+1. Requests to enable Pointer Capture are forwarded from `InputManagerService` to `InputDispatcher`.
+2. If the window that makes the request has focus, `InputDispatcher` enables the Pointer Capture state in `InputReader` through the `InputDispatcherPolicy`.
+3. When `InputReader` is successfully configured, it notifies `InputDispatcher` through the `InputListener` interface.
+4. `InputDispatcher` then notifies the `InputWindow` that Pointer Capture has been enabled by sending a special `CAPTURE` event through the `InputChannel`.
+
+### Disabling Pointer Capture
+
+Pointer Capture can be disabled in two ways: by a request through `InputManagerService`, and as a result of the `InputWindow` losing focus.
+
+When Pointer Capture is disabled by a request from the application, it follows the same pipeline as when Pointer Capture is enabled.
+
+#### Window loses Pointer Capture when it loses focus
+
+When an `InputWindow` with Pointer Capture loses focus, Pointer Capture is disabled immediately. The `InputWindow` receives a `CAPTURE` event through the `InputChannel`, followed by a `FOCUS` event to notify loss of focus.
+
+## Pointer Capture in `InputDispatcher`
+
+`InputDispatcher` tracks two pieces of state information regarding Pointer Capture:
+
+- `mFocusedWindowRequestedPointerCapture`: Whether or not the focused window has requested Pointer Capture. This is updated whenever the Dispatcher receives requests from `InputManagerService`.
+- `mWindowTokenWithPointerCapture`: The Binder token of the `InputWindow` that currently has Pointer Capture. This is only updated during the dispatch cycle. If it is not `nullptr`, it signifies that the window was notified that it has Pointer Capture.
diff --git a/services/inputflinger/host/Android.bp b/services/inputflinger/host/Android.bp
index f0151c9..743587c 100644
--- a/services/inputflinger/host/Android.bp
+++ b/services/inputflinger/host/Android.bp
@@ -32,6 +32,7 @@
header_libs: ["jni_headers"],
shared_libs: [
+ "libbase",
"libbinder",
"libcrypto",
"libcutils",
@@ -66,11 +67,14 @@
cflags: ["-Wall", "-Werror"],
shared_libs: [
+ "libbase",
"libbinder",
"libinputflingerhost",
- "libutils"
+ "libutils",
+ "libinput"
],
static_libs: [
"libarect",
+ "libui-types",
],
}
diff --git a/services/inputflinger/host/InputDriver.cpp b/services/inputflinger/host/InputDriver.cpp
index c9001b0..2ebdbcf 100644
--- a/services/inputflinger/host/InputDriver.cpp
+++ b/services/inputflinger/host/InputDriver.cpp
@@ -36,7 +36,7 @@
#define INDENT2 " "
struct input_property_map {
- android::PropertyMap* propertyMap;
+ std::unique_ptr<android::PropertyMap> propertyMap;
};
struct input_property {
@@ -217,22 +217,25 @@
idi.product = id->productId;
idi.version = id->version;
- std::string configFile = getInputDeviceConfigurationFilePathByDeviceIdentifier(
- idi, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION);
+ std::string configFile =
+ getInputDeviceConfigurationFilePathByDeviceIdentifier(idi,
+ InputDeviceConfigurationFileType::
+ CONFIGURATION);
if (configFile.empty()) {
ALOGD("No input device configuration file found for device '%s'.",
idi.name.c_str());
} else {
- auto propMap = new input_property_map_t();
- status_t status = PropertyMap::load(String8(configFile.c_str()), &propMap->propertyMap);
- if (status) {
+ std::unique_ptr<input_property_map_t> propMap = std::make_unique<input_property_map_t>();
+ android::base::Result<std::unique_ptr<PropertyMap>> result =
+ PropertyMap::load(configFile.c_str());
+ if (!result.ok()) {
ALOGE("Error loading input device configuration file for device '%s'. "
"Using default configuration.",
idi.name.c_str());
- delete propMap;
return nullptr;
}
- return propMap;
+ propMap->propertyMap = std::move(*result);
+ return propMap.release();
}
return nullptr;
}
@@ -276,7 +279,6 @@
void InputDriver::inputFreeDevicePropertyMap(input_property_map_t* map) {
if (map != nullptr) {
- delete map->propertyMap;
delete map;
}
}
diff --git a/services/inputflinger/host/InputFlinger.h b/services/inputflinger/host/InputFlinger.h
index 973b4f9..8112038 100644
--- a/services/inputflinger/host/InputFlinger.h
+++ b/services/inputflinger/host/InputFlinger.h
@@ -22,13 +22,17 @@
#include "InputHost.h"
+#include <android/os/BnInputFlinger.h>
+#include <android/os/ISetInputWindowsListener.h>
+#include <binder/Binder.h>
#include <cutils/compiler.h>
-#include <input/IInputFlinger.h>
-#include <input/ISetInputWindowsListener.h>
-#include <utils/String8.h>
#include <utils/String16.h>
+#include <utils/String8.h>
#include <utils/StrongPointer.h>
+using android::os::BnInputFlinger;
+using android::os::ISetInputWindowsListener;
+
namespace android {
class InputFlinger : public BnInputFlinger {
@@ -39,14 +43,19 @@
InputFlinger() ANDROID_API;
- virtual status_t dump(int fd, const Vector<String16>& args);
- void setInputWindows(const std::vector<InputWindowInfo>&,
- const sp<ISetInputWindowsListener>&) {}
- void registerInputChannel(const sp<InputChannel>&) {}
- void unregisterInputChannel(const sp<InputChannel>&) {}
+ status_t dump(int fd, const Vector<String16>& args) override;
+ binder::Status setInputWindows(const std::vector<InputWindowInfo>&,
+ const sp<ISetInputWindowsListener>&) override {
+ return binder::Status::ok();
+ }
+ binder::Status createInputChannel(const std::string&, InputChannel*) override {
+ return binder::Status::ok();
+ }
+ binder::Status removeInputChannel(const sp<IBinder>&) override { return binder::Status::ok(); }
+ binder::Status setFocusedWindow(const FocusRequest&) override { return binder::Status::ok(); }
private:
- virtual ~InputFlinger();
+ ~InputFlinger() override;
void dumpInternal(String8& result);
diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h
index 8317b05..4b7d26d 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -20,6 +20,7 @@
#include <vector>
#include <input/Input.h>
+#include <input/InputDevice.h>
#include <input/TouchVideoFrame.h>
#include <utils/RefBase.h>
@@ -72,12 +73,14 @@
int32_t scanCode;
int32_t metaState;
nsecs_t downTime;
+ nsecs_t readTime;
inline NotifyKeyArgs() { }
- NotifyKeyArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
- int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags,
- int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime);
+ NotifyKeyArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId,
+ uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action,
+ int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
+ nsecs_t downTime);
bool operator==(const NotifyKeyArgs& rhs) const;
@@ -119,13 +122,14 @@
float xCursorPosition;
float yCursorPosition;
nsecs_t downTime;
+ nsecs_t readTime;
std::vector<TouchVideoFrame> videoFrames;
inline NotifyMotionArgs() { }
- NotifyMotionArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
- int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton,
- int32_t flags, int32_t metaState, int32_t buttonState,
+ NotifyMotionArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId,
+ uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action,
+ int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState,
MotionClassification classification, int32_t edgeFlags, uint32_t pointerCount,
const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
float xPrecision, float yPrecision, float xCursorPosition,
@@ -141,6 +145,30 @@
virtual void notify(const sp<InputListenerInterface>& listener) const;
};
+/* Describes a sensor event. */
+struct NotifySensorArgs : public NotifyArgs {
+ int32_t deviceId;
+ uint32_t source;
+ InputDeviceSensorType sensorType;
+ InputDeviceSensorAccuracy accuracy;
+ bool accuracyChanged;
+ nsecs_t hwTimestamp;
+ std::vector<float> values;
+
+ inline NotifySensorArgs() {}
+
+ NotifySensorArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
+ InputDeviceSensorType sensorType, InputDeviceSensorAccuracy accuracy,
+ bool accuracyChanged, nsecs_t hwTimestamp, std::vector<float> values);
+
+ NotifySensorArgs(const NotifySensorArgs& other);
+
+ bool operator==(const NotifySensorArgs rhs) const;
+
+ ~NotifySensorArgs() override {}
+
+ void notify(const sp<InputListenerInterface>& listener) const override;
+};
/* Describes a switch event. */
struct NotifySwitchArgs : public NotifyArgs {
@@ -181,6 +209,40 @@
virtual void notify(const sp<InputListenerInterface>& listener) const;
};
+/* Describes a change in the state of Pointer Capture. */
+struct NotifyPointerCaptureChangedArgs : public NotifyArgs {
+ bool enabled;
+
+ inline NotifyPointerCaptureChangedArgs() {}
+
+ NotifyPointerCaptureChangedArgs(int32_t id, nsecs_t eventTime, bool enabled);
+
+ NotifyPointerCaptureChangedArgs(const NotifyPointerCaptureChangedArgs& other);
+
+ bool operator==(const NotifyPointerCaptureChangedArgs& rhs) const;
+
+ virtual ~NotifyPointerCaptureChangedArgs() {}
+
+ virtual void notify(const sp<InputListenerInterface>& listener) const;
+};
+
+/* Describes a vibrator state event. */
+struct NotifyVibratorStateArgs : public NotifyArgs {
+ int32_t deviceId;
+ bool isOn;
+
+ inline NotifyVibratorStateArgs() {}
+
+ NotifyVibratorStateArgs(int32_t id, nsecs_t eventTIme, int32_t deviceId, bool isOn);
+
+ NotifyVibratorStateArgs(const NotifyVibratorStateArgs& other);
+
+ bool operator==(const NotifyVibratorStateArgs rhs) const;
+
+ virtual ~NotifyVibratorStateArgs() {}
+
+ virtual void notify(const sp<InputListenerInterface>& listener) const;
+};
/*
* The interface used by the InputReader to notify the InputListener about input events.
@@ -195,7 +257,10 @@
virtual void notifyKey(const NotifyKeyArgs* args) = 0;
virtual void notifyMotion(const NotifyMotionArgs* args) = 0;
virtual void notifySwitch(const NotifySwitchArgs* args) = 0;
+ virtual void notifySensor(const NotifySensorArgs* args) = 0;
+ virtual void notifyVibratorState(const NotifyVibratorStateArgs* args) = 0;
virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) = 0;
+ virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) = 0;
};
@@ -210,11 +275,14 @@
public:
explicit QueuedInputListener(const sp<InputListenerInterface>& innerListener);
- virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args);
- virtual void notifyKey(const NotifyKeyArgs* args);
- virtual void notifyMotion(const NotifyMotionArgs* args);
- virtual void notifySwitch(const NotifySwitchArgs* args);
- virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args);
+ virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
+ virtual void notifyKey(const NotifyKeyArgs* args) override;
+ virtual void notifyMotion(const NotifyMotionArgs* args) override;
+ virtual void notifySwitch(const NotifySwitchArgs* args) override;
+ virtual void notifySensor(const NotifySensorArgs* args) override;
+ virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
+ void notifyVibratorState(const NotifyVibratorStateArgs* args) override;
+ void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
void flush();
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 0fa8787..19abfd9 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -17,31 +17,28 @@
#ifndef _UI_INPUT_READER_BASE_H
#define _UI_INPUT_READER_BASE_H
-#include "PointerControllerInterface.h"
-
#include <input/DisplayViewport.h>
#include <input/Input.h>
#include <input/InputDevice.h>
#include <input/VelocityControl.h>
#include <input/VelocityTracker.h>
+#include <stddef.h>
+#include <unistd.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
-#include <stddef.h>
-#include <unistd.h>
#include <optional>
#include <set>
#include <unordered_map>
#include <vector>
+#include "PointerControllerInterface.h"
+#include "VibrationElement.h"
+
// Maximum supported size of a vibration pattern.
// Must be at least 2.
#define MAX_VIBRATE_PATTERN_SIZE 100
-// Maximum allowable delay value in a vibration pattern before
-// which the delay will be truncated.
-#define MAX_VIBRATE_PATTERN_DELAY_NSECS (1000000 * 1000000000LL)
-
namespace android {
// --- InputReaderInterface ---
@@ -81,7 +78,7 @@
*
* This method may be called on any thread (usually by the input manager).
*/
- virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) = 0;
+ virtual std::vector<InputDeviceInfo> getInputDevices() const = 0;
/* Query current input state. */
virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
@@ -104,12 +101,44 @@
virtual void requestRefreshConfiguration(uint32_t changes) = 0;
/* Controls the vibrator of a particular input device. */
- virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
- ssize_t repeat, int32_t token) = 0;
+ virtual void vibrate(int32_t deviceId, const VibrationSequence& sequence, ssize_t repeat,
+ int32_t token) = 0;
virtual void cancelVibrate(int32_t deviceId, int32_t token) = 0;
+ virtual bool isVibrating(int32_t deviceId) = 0;
+
+ virtual std::vector<int32_t> getVibratorIds(int32_t deviceId) = 0;
+ /* Get battery level of a particular input device. */
+ virtual std::optional<int32_t> getBatteryCapacity(int32_t deviceId) = 0;
+ /* Get battery status of a particular input device. */
+ virtual std::optional<int32_t> getBatteryStatus(int32_t deviceId) = 0;
+
+ virtual std::vector<int32_t> getLightIds(int32_t deviceId) = 0;
+
+ virtual const InputDeviceLightInfo* getLightInfo(int32_t deviceId, int32_t lightId) = 0;
+
/* Return true if the device can send input events to the specified display. */
virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) = 0;
+
+ /* Enable sensor in input reader mapper. */
+ virtual bool enableSensor(int32_t deviceId, InputDeviceSensorType sensorType,
+ std::chrono::microseconds samplingPeriod,
+ std::chrono::microseconds maxBatchReportLatency) = 0;
+
+ /* Disable sensor in input reader mapper. */
+ virtual void disableSensor(int32_t deviceId, InputDeviceSensorType sensorType) = 0;
+
+ /* Flush sensor data in input reader mapper. */
+ virtual void flushSensor(int32_t deviceId, InputDeviceSensorType sensorType) = 0;
+
+ /* Set color for the light */
+ virtual bool setLightColor(int32_t deviceId, int32_t lightId, int32_t color) = 0;
+ /* Set player ID for the light */
+ virtual bool setLightPlayerId(int32_t deviceId, int32_t lightId, int32_t playerId) = 0;
+ /* Get light color */
+ virtual std::optional<int32_t> getLightColor(int32_t deviceId, int32_t lightId) = 0;
+ /* Get light player ID */
+ virtual std::optional<int32_t> getLightPlayerId(int32_t deviceId, int32_t lightId) = 0;
};
// --- InputReaderConfiguration ---
@@ -169,6 +198,10 @@
// Used to determine which DisplayViewport should be tied to which InputDevice.
std::unordered_map<std::string, uint8_t> portAssociations;
+ // The associations between input device names and display unique ids.
+ // Used to determine which DisplayViewport should be tied to which InputDevice.
+ std::unordered_map<std::string, std::string> uniqueIdAssociations;
+
// The suggested display ID to show the cursor.
int32_t defaultPointerDisplayId;
@@ -344,7 +377,7 @@
virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) = 0;
/* Gets the keyboard layout for a particular input device. */
- virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(
+ virtual std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
const InputDeviceIdentifier& identifier) = 0;
/* Gets a user-supplied alias for a particular input device, or an empty string if none. */
diff --git a/services/inputflinger/include/VibrationElement.h b/services/inputflinger/include/VibrationElement.h
new file mode 100644
index 0000000..736041e
--- /dev/null
+++ b/services/inputflinger/include/VibrationElement.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef _VIBRATION_ELEMENT_H
+#define _VIBRATION_ELEMENT_H
+
+#include <array>
+#include <chrono>
+#include <cstdint>
+#include <string>
+#include <vector>
+
+namespace android {
+
+// evdev FF_RUMBLE effect only supports two channels of vibration.
+constexpr size_t CHANNEL_SIZE = 2;
+/*
+ * Describes a rumble effect
+ */
+struct VibrationElement {
+ std::chrono::milliseconds duration;
+ // Channel amplitude range 0-255.
+ std::vector<std::pair<int32_t /*vibratorId*/, uint8_t /*amplitude*/>> channels;
+
+ explicit VibrationElement(size_t channelNum);
+
+ VibrationElement(const VibrationElement& other);
+
+ bool operator==(const VibrationElement& other) const;
+
+ bool operator!=(const VibrationElement& other) const;
+
+ void addChannel(int32_t vibratorId, uint8_t amplitude);
+
+ const std::string toString() const;
+
+ uint16_t getMagnitude(int32_t vibratorId) const;
+
+ bool isOn() const;
+};
+
+/*
+ * Describes a sequence of rumble effect
+ */
+struct VibrationSequence {
+ // Pattern of vibration elements
+ std::vector<VibrationElement> pattern;
+
+ explicit VibrationSequence(size_t length);
+
+ void operator=(const VibrationSequence& other);
+
+ bool operator==(const VibrationSequence& other) const;
+
+ void addElement(VibrationElement element);
+
+ const std::string toString() const;
+};
+
+} // namespace android
+
+#endif // _VIBRATION_ELEMENT_H
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index dadbd0e..7db32e3 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -24,6 +24,7 @@
cc_library_headers {
name: "libinputreader_headers",
export_include_dirs: [
+ "controller",
"include",
"mapper",
"mapper/accumulator",
@@ -35,6 +36,7 @@
srcs: [
"EventHub.cpp",
"InputDevice.cpp",
+ "controller/PeripheralController.cpp",
"mapper/accumulator/CursorButtonAccumulator.cpp",
"mapper/accumulator/CursorScrollAccumulator.cpp",
"mapper/accumulator/SingleTouchMotionAccumulator.cpp",
@@ -46,6 +48,7 @@
"mapper/KeyboardInputMapper.cpp",
"mapper/MultiTouchInputMapper.cpp",
"mapper/RotaryEncoderInputMapper.cpp",
+ "mapper/SensorInputMapper.cpp",
"mapper/SingleTouchInputMapper.cpp",
"mapper/SwitchInputMapper.cpp",
"mapper/TouchInputMapper.cpp",
@@ -65,10 +68,15 @@
"libcutils",
"libinput",
"liblog",
+ "libstatslog",
"libui",
"libutils",
],
+ static_libs: [
+ "libc++fs",
+ ],
header_libs: [
+ "libbatteryservice_headers",
"libinputreader_headers",
],
}
@@ -90,4 +98,7 @@
export_header_lib_headers: [
"libinputreader_headers",
],
+ static_libs: [
+ "libc++fs"
+ ],
}
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index a1514af..e3e6c12 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -29,50 +29,102 @@
#include <sys/inotify.h>
#include <sys/ioctl.h>
#include <sys/limits.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
#include <unistd.h>
#define LOG_TAG "EventHub"
// #define LOG_NDEBUG 0
-
-#include "EventHub.h"
-
+#include <android-base/file.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <cutils/properties.h>
-#include <openssl/sha.h>
-#include <utils/Errors.h>
-#include <utils/Log.h>
-#include <utils/Timers.h>
-#include <utils/threads.h>
-
#include <input/KeyCharacterMap.h>
#include <input/KeyLayoutMap.h>
#include <input/VirtualKeyMap.h>
+#include <openssl/sha.h>
+#include <statslog.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <utils/Timers.h>
-/* this macro is used to tell if "bit" is set in "array"
- * it selects a byte from the array, and does a boolean AND
- * operation with a byte that only has the relevant bit set.
- * eg. to check for the 12th bit, we do (array[1] & 1<<4)
- */
-#define test_bit(bit, array) ((array)[(bit) / 8] & (1 << ((bit) % 8)))
+#include <filesystem>
+#include <regex>
-/* this macro computes the number of bytes needed to represent a bit array of the specified size */
-#define sizeof_bit_array(bits) (((bits) + 7) / 8)
+#include "EventHub.h"
#define INDENT " "
#define INDENT2 " "
#define INDENT3 " "
using android::base::StringPrintf;
+using namespace android::flag_operators;
namespace android {
-static constexpr bool DEBUG = false;
-
static const char* DEVICE_PATH = "/dev/input";
// v4l2 devices go directly into /dev
static const char* VIDEO_DEVICE_PATH = "/dev";
+static constexpr int32_t FF_STRONG_MAGNITUDE_CHANNEL_IDX = 0;
+static constexpr int32_t FF_WEAK_MAGNITUDE_CHANNEL_IDX = 1;
+
+// Mapping for input battery class node IDs lookup.
+// https://www.kernel.org/doc/Documentation/power/power_supply_class.txt
+static const std::unordered_map<std::string, InputBatteryClass> BATTERY_CLASSES =
+ {{"capacity", InputBatteryClass::CAPACITY},
+ {"capacity_level", InputBatteryClass::CAPACITY_LEVEL},
+ {"status", InputBatteryClass::STATUS}};
+
+// Mapping for input battery class node names lookup.
+// https://www.kernel.org/doc/Documentation/power/power_supply_class.txt
+static const std::unordered_map<InputBatteryClass, std::string> BATTERY_NODES =
+ {{InputBatteryClass::CAPACITY, "capacity"},
+ {InputBatteryClass::CAPACITY_LEVEL, "capacity_level"},
+ {InputBatteryClass::STATUS, "status"}};
+
+// must be kept in sync with definitions in kernel /drivers/power/supply/power_supply_sysfs.c
+static const std::unordered_map<std::string, int32_t> BATTERY_STATUS =
+ {{"Unknown", BATTERY_STATUS_UNKNOWN},
+ {"Charging", BATTERY_STATUS_CHARGING},
+ {"Discharging", BATTERY_STATUS_DISCHARGING},
+ {"Not charging", BATTERY_STATUS_NOT_CHARGING},
+ {"Full", BATTERY_STATUS_FULL}};
+
+// Mapping taken from
+// https://gitlab.freedesktop.org/upower/upower/-/blob/master/src/linux/up-device-supply.c#L484
+static const std::unordered_map<std::string, int32_t> BATTERY_LEVEL = {{"Critical", 5},
+ {"Low", 10},
+ {"Normal", 55},
+ {"High", 70},
+ {"Full", 100},
+ {"Unknown", 50}};
+
+// Mapping for input led class node names lookup.
+// https://www.kernel.org/doc/html/latest/leds/leds-class.html
+static const std::unordered_map<std::string, InputLightClass> LIGHT_CLASSES =
+ {{"red", InputLightClass::RED},
+ {"green", InputLightClass::GREEN},
+ {"blue", InputLightClass::BLUE},
+ {"global", InputLightClass::GLOBAL},
+ {"brightness", InputLightClass::BRIGHTNESS},
+ {"multi_index", InputLightClass::MULTI_INDEX},
+ {"multi_intensity", InputLightClass::MULTI_INTENSITY},
+ {"max_brightness", InputLightClass::MAX_BRIGHTNESS}};
+
+// Mapping for input multicolor led class node names.
+// https://www.kernel.org/doc/html/latest/leds/leds-class-multicolor.html
+static const std::unordered_map<InputLightClass, std::string> LIGHT_NODES =
+ {{InputLightClass::BRIGHTNESS, "brightness"},
+ {InputLightClass::MULTI_INDEX, "multi_index"},
+ {InputLightClass::MULTI_INTENSITY, "multi_intensity"}};
+
+// Mapping for light color name and the light color
+const std::unordered_map<std::string, LightColor> LIGHT_COLORS = {{"red", LightColor::RED},
+ {"green", LightColor::GREEN},
+ {"blue", LightColor::BLUE}};
+
static inline const char* toString(bool value) {
return value ? "true" : "false";
}
@@ -94,8 +146,8 @@
/**
* Return true if name matches "v4l-touch*"
*/
-static bool isV4lTouchNode(const char* name) {
- return strstr(name, "v4l-touch") == name;
+static bool isV4lTouchNode(std::string name) {
+ return name.find("v4l-touch") != std::string::npos;
}
/**
@@ -136,11 +188,120 @@
return inputEventTime;
}
+/**
+ * Returns the sysfs root path of the input device
+ *
+ */
+static std::optional<std::filesystem::path> getSysfsRootPath(const char* devicePath) {
+ std::error_code errorCode;
+
+ // Stat the device path to get the major and minor number of the character file
+ struct stat statbuf;
+ if (stat(devicePath, &statbuf) == -1) {
+ ALOGE("Could not stat device %s due to error: %s.", devicePath, std::strerror(errno));
+ return std::nullopt;
+ }
+
+ unsigned int major_num = major(statbuf.st_rdev);
+ unsigned int minor_num = minor(statbuf.st_rdev);
+
+ // Realpath "/sys/dev/char/{major}:{minor}" to get the sysfs path to the input event
+ auto sysfsPath = std::filesystem::path("/sys/dev/char/");
+ sysfsPath /= std::to_string(major_num) + ":" + std::to_string(minor_num);
+ sysfsPath = std::filesystem::canonical(sysfsPath, errorCode);
+
+ // Make sure nothing went wrong in call to canonical()
+ if (errorCode) {
+ ALOGW("Could not run filesystem::canonical() due to error %d : %s.", errorCode.value(),
+ errorCode.message().c_str());
+ return std::nullopt;
+ }
+
+ // Continue to go up a directory until we reach a directory named "input"
+ while (sysfsPath != "/" && sysfsPath.filename() != "input") {
+ sysfsPath = sysfsPath.parent_path();
+ }
+
+ // Then go up one more and you will be at the sysfs root of the device
+ sysfsPath = sysfsPath.parent_path();
+
+ // Make sure we didn't reach root path and that directory actually exists
+ if (sysfsPath == "/" || !std::filesystem::exists(sysfsPath, errorCode)) {
+ if (errorCode) {
+ ALOGW("Could not run filesystem::exists() due to error %d : %s.", errorCode.value(),
+ errorCode.message().c_str());
+ }
+
+ // Not found
+ return std::nullopt;
+ }
+
+ return sysfsPath;
+}
+
+/**
+ * Returns the list of files under a specified path.
+ */
+static std::vector<std::filesystem::path> allFilesInPath(const std::filesystem::path& path) {
+ std::vector<std::filesystem::path> nodes;
+ std::error_code errorCode;
+ auto iter = std::filesystem::directory_iterator(path, errorCode);
+ while (!errorCode && iter != std::filesystem::directory_iterator()) {
+ nodes.push_back(iter->path());
+ iter++;
+ }
+ return nodes;
+}
+
+/**
+ * Returns the list of files under a specified directory in a sysfs path.
+ * Example:
+ * findSysfsNodes(sysfsRootPath, SysfsClass::LEDS) will return all led nodes under "leds" directory
+ * in the sysfs path.
+ */
+static std::vector<std::filesystem::path> findSysfsNodes(const std::filesystem::path& sysfsRoot,
+ SysfsClass clazz) {
+ std::string nodeStr = NamedEnum::string(clazz);
+ std::for_each(nodeStr.begin(), nodeStr.end(),
+ [](char& c) { c = std::tolower(static_cast<unsigned char>(c)); });
+ std::vector<std::filesystem::path> nodes;
+ for (auto path = sysfsRoot; path != "/" && nodes.empty(); path = path.parent_path()) {
+ nodes = allFilesInPath(path / nodeStr);
+ }
+ return nodes;
+}
+
+static std::optional<std::array<LightColor, COLOR_NUM>> getColorIndexArray(
+ std::filesystem::path path) {
+ std::string indexStr;
+ if (!base::ReadFileToString(path, &indexStr)) {
+ return std::nullopt;
+ }
+
+ // Parse the multi color LED index file, refer to kernel docs
+ // leds/leds-class-multicolor.html
+ std::regex indexPattern("(red|green|blue)\\s(red|green|blue)\\s(red|green|blue)[\\n]");
+ std::smatch results;
+ std::array<LightColor, COLOR_NUM> colors;
+ if (!std::regex_match(indexStr, results, indexPattern)) {
+ return std::nullopt;
+ }
+
+ for (size_t i = 1; i < results.size(); i++) {
+ const auto it = LIGHT_COLORS.find(results[i].str());
+ if (it != LIGHT_COLORS.end()) {
+ // intensities.emplace(it->second, 0);
+ colors[i - 1] = it->second;
+ }
+ }
+ return colors;
+}
+
// --- Global Functions ---
-uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses) {
+Flags<InputDeviceClass> getAbsAxisUsage(int32_t axis, Flags<InputDeviceClass> deviceClasses) {
// Touch devices get dibs on touch-related axes.
- if (deviceClasses & INPUT_DEVICE_CLASS_TOUCH) {
+ if (deviceClasses.test(InputDeviceClass::TOUCH)) {
switch (axis) {
case ABS_X:
case ABS_Y:
@@ -162,27 +323,38 @@
case ABS_MT_TRACKING_ID:
case ABS_MT_PRESSURE:
case ABS_MT_DISTANCE:
- return INPUT_DEVICE_CLASS_TOUCH;
+ return InputDeviceClass::TOUCH;
+ }
+ }
+
+ if (deviceClasses.test(InputDeviceClass::SENSOR)) {
+ switch (axis) {
+ case ABS_X:
+ case ABS_Y:
+ case ABS_Z:
+ case ABS_RX:
+ case ABS_RY:
+ case ABS_RZ:
+ return InputDeviceClass::SENSOR;
}
}
// External stylus gets the pressure axis
- if (deviceClasses & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
+ if (deviceClasses.test(InputDeviceClass::EXTERNAL_STYLUS)) {
if (axis == ABS_PRESSURE) {
- return INPUT_DEVICE_CLASS_EXTERNAL_STYLUS;
+ return InputDeviceClass::EXTERNAL_STYLUS;
}
}
// Joystick devices get the rest.
- return deviceClasses & INPUT_DEVICE_CLASS_JOYSTICK;
+ return deviceClasses & InputDeviceClass::JOYSTICK;
}
// --- EventHub::Device ---
EventHub::Device::Device(int fd, int32_t id, const std::string& path,
const InputDeviceIdentifier& identifier)
- : next(nullptr),
- fd(fd),
+ : fd(fd),
id(id),
path(path),
identifier(identifier),
@@ -191,21 +363,13 @@
virtualKeyMap(nullptr),
ffEffectPlaying(false),
ffEffectId(-1),
+ associatedDevice(nullptr),
controllerNumber(0),
enabled(true),
- isVirtual(fd < 0) {
- memset(keyBitmask, 0, sizeof(keyBitmask));
- memset(absBitmask, 0, sizeof(absBitmask));
- memset(relBitmask, 0, sizeof(relBitmask));
- memset(swBitmask, 0, sizeof(swBitmask));
- memset(ledBitmask, 0, sizeof(ledBitmask));
- memset(ffBitmask, 0, sizeof(ffBitmask));
- memset(propBitmask, 0, sizeof(propBitmask));
-}
+ isVirtual(fd < 0) {}
EventHub::Device::~Device() {
close();
- delete configuration;
}
void EventHub::Device::close() {
@@ -231,10 +395,233 @@
return OK;
}
-bool EventHub::Device::hasValidFd() {
+bool EventHub::Device::hasValidFd() const {
return !isVirtual && enabled;
}
+const std::shared_ptr<KeyCharacterMap> EventHub::Device::getKeyCharacterMap() const {
+ return keyMap.keyCharacterMap;
+}
+
+template <std::size_t N>
+status_t EventHub::Device::readDeviceBitMask(unsigned long ioctlCode, BitArray<N>& bitArray) {
+ if (!hasValidFd()) {
+ return BAD_VALUE;
+ }
+ if ((_IOC_SIZE(ioctlCode) == 0)) {
+ ioctlCode |= _IOC(0, 0, 0, bitArray.bytes());
+ }
+
+ typename BitArray<N>::Buffer buffer;
+ status_t ret = ioctl(fd, ioctlCode, buffer.data());
+ bitArray.loadFromBuffer(buffer);
+ return ret;
+}
+
+void EventHub::Device::configureFd() {
+ // Set fd parameters with ioctl, such as key repeat, suspend block, and clock type
+ if (classes.test(InputDeviceClass::KEYBOARD)) {
+ // Disable kernel key repeat since we handle it ourselves
+ unsigned int repeatRate[] = {0, 0};
+ if (ioctl(fd, EVIOCSREP, repeatRate)) {
+ ALOGW("Unable to disable kernel key repeat for %s: %s", path.c_str(), strerror(errno));
+ }
+ }
+
+ // Tell the kernel that we want to use the monotonic clock for reporting timestamps
+ // associated with input events. This is important because the input system
+ // uses the timestamps extensively and assumes they were recorded using the monotonic
+ // clock.
+ int clockId = CLOCK_MONOTONIC;
+ if (classes.test(InputDeviceClass::SENSOR)) {
+ // Each new sensor event should use the same time base as
+ // SystemClock.elapsedRealtimeNanos().
+ clockId = CLOCK_BOOTTIME;
+ }
+ bool usingClockIoctl = !ioctl(fd, EVIOCSCLOCKID, &clockId);
+ ALOGI("usingClockIoctl=%s", toString(usingClockIoctl));
+}
+
+bool EventHub::Device::hasKeycodeLocked(int keycode) const {
+ if (!keyMap.haveKeyLayout()) {
+ return false;
+ }
+
+ std::vector<int32_t> scanCodes;
+ keyMap.keyLayoutMap->findScanCodesForKey(keycode, &scanCodes);
+ const size_t N = scanCodes.size();
+ for (size_t i = 0; i < N && i <= KEY_MAX; i++) {
+ int32_t sc = scanCodes[i];
+ if (sc >= 0 && sc <= KEY_MAX && keyBitmask.test(sc)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void EventHub::Device::loadConfigurationLocked() {
+ configurationFile =
+ getInputDeviceConfigurationFilePathByDeviceIdentifier(identifier,
+ InputDeviceConfigurationFileType::
+ CONFIGURATION);
+ if (configurationFile.empty()) {
+ ALOGD("No input device configuration file found for device '%s'.", identifier.name.c_str());
+ } else {
+ android::base::Result<std::unique_ptr<PropertyMap>> propertyMap =
+ PropertyMap::load(configurationFile.c_str());
+ if (!propertyMap.ok()) {
+ ALOGE("Error loading input device configuration file for device '%s'. "
+ "Using default configuration.",
+ identifier.name.c_str());
+ } else {
+ configuration = std::move(*propertyMap);
+ }
+ }
+}
+
+bool EventHub::Device::loadVirtualKeyMapLocked() {
+ // The virtual key map is supplied by the kernel as a system board property file.
+ std::string propPath = "/sys/board_properties/virtualkeys.";
+ propPath += identifier.getCanonicalName();
+ if (access(propPath.c_str(), R_OK)) {
+ return false;
+ }
+ virtualKeyMap = VirtualKeyMap::load(propPath);
+ return virtualKeyMap != nullptr;
+}
+
+status_t EventHub::Device::loadKeyMapLocked() {
+ return keyMap.load(identifier, configuration.get());
+}
+
+bool EventHub::Device::isExternalDeviceLocked() {
+ if (configuration) {
+ bool value;
+ if (configuration->tryGetProperty(String8("device.internal"), value)) {
+ return !value;
+ }
+ }
+ return identifier.bus == BUS_USB || identifier.bus == BUS_BLUETOOTH;
+}
+
+bool EventHub::Device::deviceHasMicLocked() {
+ if (configuration) {
+ bool value;
+ if (configuration->tryGetProperty(String8("audio.mic"), value)) {
+ return value;
+ }
+ }
+ return false;
+}
+
+void EventHub::Device::setLedStateLocked(int32_t led, bool on) {
+ int32_t sc;
+ if (hasValidFd() && mapLed(led, &sc) != NAME_NOT_FOUND) {
+ struct input_event ev;
+ ev.time.tv_sec = 0;
+ ev.time.tv_usec = 0;
+ ev.type = EV_LED;
+ ev.code = sc;
+ ev.value = on ? 1 : 0;
+
+ ssize_t nWrite;
+ do {
+ nWrite = write(fd, &ev, sizeof(struct input_event));
+ } while (nWrite == -1 && errno == EINTR);
+ }
+}
+
+void EventHub::Device::setLedForControllerLocked() {
+ for (int i = 0; i < MAX_CONTROLLER_LEDS; i++) {
+ setLedStateLocked(ALED_CONTROLLER_1 + i, controllerNumber == i + 1);
+ }
+}
+
+status_t EventHub::Device::mapLed(int32_t led, int32_t* outScanCode) const {
+ if (!keyMap.haveKeyLayout()) {
+ return NAME_NOT_FOUND;
+ }
+
+ int32_t scanCode;
+ if (keyMap.keyLayoutMap->findScanCodeForLed(led, &scanCode) != NAME_NOT_FOUND) {
+ if (scanCode >= 0 && scanCode <= LED_MAX && ledBitmask.test(scanCode)) {
+ *outScanCode = scanCode;
+ return NO_ERROR;
+ }
+ }
+ return NAME_NOT_FOUND;
+}
+
+// Check the sysfs path for any input device batteries, returns true if battery found.
+bool EventHub::AssociatedDevice::configureBatteryLocked() {
+ nextBatteryId = 0;
+ // Check if device has any battery.
+ const auto& paths = findSysfsNodes(sysfsRootPath, SysfsClass::POWER_SUPPLY);
+ for (const auto& nodePath : paths) {
+ RawBatteryInfo info;
+ info.id = ++nextBatteryId;
+ info.path = nodePath;
+ info.name = nodePath.filename();
+
+ // Scan the path for all the files
+ // Refer to https://www.kernel.org/doc/Documentation/leds/leds-class.txt
+ const auto& files = allFilesInPath(nodePath);
+ for (const auto& file : files) {
+ const auto it = BATTERY_CLASSES.find(file.filename().string());
+ if (it != BATTERY_CLASSES.end()) {
+ info.flags |= it->second;
+ }
+ }
+ batteryInfos.insert_or_assign(info.id, info);
+ ALOGD("configureBatteryLocked rawBatteryId %d name %s", info.id, info.name.c_str());
+ }
+ return !batteryInfos.empty();
+}
+
+// Check the sysfs path for any input device lights, returns true if lights found.
+bool EventHub::AssociatedDevice::configureLightsLocked() {
+ nextLightId = 0;
+ // Check if device has any lights.
+ const auto& paths = findSysfsNodes(sysfsRootPath, SysfsClass::LEDS);
+ for (const auto& nodePath : paths) {
+ RawLightInfo info;
+ info.id = ++nextLightId;
+ info.path = nodePath;
+ info.name = nodePath.filename();
+ info.maxBrightness = std::nullopt;
+ size_t nameStart = info.name.rfind(":");
+ if (nameStart != std::string::npos) {
+ // Trim the name to color name
+ info.name = info.name.substr(nameStart + 1);
+ // Set InputLightClass flag for colors
+ const auto it = LIGHT_CLASSES.find(info.name);
+ if (it != LIGHT_CLASSES.end()) {
+ info.flags |= it->second;
+ }
+ }
+ // Scan the path for all the files
+ // Refer to https://www.kernel.org/doc/Documentation/leds/leds-class.txt
+ const auto& files = allFilesInPath(nodePath);
+ for (const auto& file : files) {
+ const auto it = LIGHT_CLASSES.find(file.filename().string());
+ if (it != LIGHT_CLASSES.end()) {
+ info.flags |= it->second;
+ // If the node has maximum brightness, read it
+ if (it->second == InputLightClass::MAX_BRIGHTNESS) {
+ std::string str;
+ if (base::ReadFileToString(file, &str)) {
+ info.maxBrightness = std::stoi(str);
+ }
+ }
+ }
+ }
+ lightInfos.insert_or_assign(info.id, info);
+ ALOGD("configureLightsLocked rawLightId %d name %s", info.id, info.name.c_str());
+ }
+ return !lightInfos.empty();
+}
+
/**
* Get the capabilities for the current process.
* Crashes the system if unable to create / check / destroy the capabilities object.
@@ -284,8 +671,6 @@
: mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD),
mNextDeviceId(1),
mControllerNumbers(),
- mOpeningDevices(nullptr),
- mClosingDevices(nullptr),
mNeedToSendFinishedDeviceScan(false),
mNeedToReopenDevices(false),
mNeedToScanDevices(true),
@@ -340,12 +725,6 @@
EventHub::~EventHub(void) {
closeAllDevicesLocked();
- while (mClosingDevices) {
- Device* device = mClosingDevices;
- mClosingDevices = device->next;
- delete device;
- }
-
::close(mEpollFd);
::close(mINotifyFd);
::close(mWakeReadPipeFd);
@@ -353,30 +732,27 @@
}
InputDeviceIdentifier EventHub::getDeviceIdentifier(int32_t deviceId) const {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device == nullptr) return InputDeviceIdentifier();
- return device->identifier;
+ return device != nullptr ? device->identifier : InputDeviceIdentifier();
}
-uint32_t EventHub::getDeviceClasses(int32_t deviceId) const {
- AutoMutex _l(mLock);
+Flags<InputDeviceClass> EventHub::getDeviceClasses(int32_t deviceId) const {
+ std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device == nullptr) return 0;
- return device->classes;
+ return device != nullptr ? device->classes : Flags<InputDeviceClass>(0);
}
int32_t EventHub::getDeviceControllerNumber(int32_t deviceId) const {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device == nullptr) return 0;
- return device->controllerNumber;
+ return device != nullptr ? device->controllerNumber : 0;
}
void EventHub::getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->configuration) {
+ if (device != nullptr && device->configuration) {
*outConfiguration = *device->configuration;
} else {
outConfiguration->clear();
@@ -388,10 +764,10 @@
outAxisInfo->clear();
if (axis >= 0 && axis <= ABS_MAX) {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->hasValidFd() && test_bit(axis, device->absBitmask)) {
+ if (device != nullptr && device->hasValidFd() && device->absBitmask.test(axis)) {
struct input_absinfo info;
if (ioctl(device->fd, EVIOCGABS(axis), &info)) {
ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", axis,
@@ -415,38 +791,39 @@
bool EventHub::hasRelativeAxis(int32_t deviceId, int axis) const {
if (axis >= 0 && axis <= REL_MAX) {
- AutoMutex _l(mLock);
-
+ std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device) {
- return test_bit(axis, device->relBitmask);
- }
+ return device != nullptr ? device->relBitmask.test(axis) : false;
}
return false;
}
bool EventHub::hasInputProperty(int32_t deviceId, int property) const {
- if (property >= 0 && property <= INPUT_PROP_MAX) {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
- Device* device = getDeviceLocked(deviceId);
- if (device) {
- return test_bit(property, device->propBitmask);
- }
- }
- return false;
+ Device* device = getDeviceLocked(deviceId);
+ return property >= 0 && property <= INPUT_PROP_MAX && device != nullptr
+ ? device->propBitmask.test(property)
+ : false;
+}
+
+bool EventHub::hasMscEvent(int32_t deviceId, int mscEvent) const {
+ std::scoped_lock _l(mLock);
+
+ Device* device = getDeviceLocked(deviceId);
+ return mscEvent >= 0 && mscEvent <= MSC_MAX && device != nullptr
+ ? device->mscBitmask.test(mscEvent)
+ : false;
}
int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const {
if (scanCode >= 0 && scanCode <= KEY_MAX) {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->hasValidFd() && test_bit(scanCode, device->keyBitmask)) {
- uint8_t keyState[sizeof_bit_array(KEY_MAX + 1)];
- memset(keyState, 0, sizeof(keyState));
- if (ioctl(device->fd, EVIOCGKEY(sizeof(keyState)), keyState) >= 0) {
- return test_bit(scanCode, keyState) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
+ if (device != nullptr && device->hasValidFd() && device->keyBitmask.test(scanCode)) {
+ if (device->readDeviceBitMask(EVIOCGKEY(0), device->keyState) >= 0) {
+ return device->keyState.test(scanCode) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
}
}
}
@@ -454,19 +831,17 @@
}
int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->hasValidFd() && device->keyMap.haveKeyLayout()) {
+ if (device != nullptr && device->hasValidFd() && device->keyMap.haveKeyLayout()) {
std::vector<int32_t> scanCodes;
device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode, &scanCodes);
if (scanCodes.size() != 0) {
- uint8_t keyState[sizeof_bit_array(KEY_MAX + 1)];
- memset(keyState, 0, sizeof(keyState));
- if (ioctl(device->fd, EVIOCGKEY(sizeof(keyState)), keyState) >= 0) {
+ if (device->readDeviceBitMask(EVIOCGKEY(0), device->keyState) >= 0) {
for (size_t i = 0; i < scanCodes.size(); i++) {
int32_t sc = scanCodes[i];
- if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, keyState)) {
+ if (sc >= 0 && sc <= KEY_MAX && device->keyState.test(sc)) {
return AKEY_STATE_DOWN;
}
}
@@ -479,14 +854,12 @@
int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const {
if (sw >= 0 && sw <= SW_MAX) {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->hasValidFd() && test_bit(sw, device->swBitmask)) {
- uint8_t swState[sizeof_bit_array(SW_MAX + 1)];
- memset(swState, 0, sizeof(swState));
- if (ioctl(device->fd, EVIOCGSW(sizeof(swState)), swState) >= 0) {
- return test_bit(sw, swState) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
+ if (device != nullptr && device->hasValidFd() && device->swBitmask.test(sw)) {
+ if (device->readDeviceBitMask(EVIOCGSW(0), device->swState) >= 0) {
+ return device->swState.test(sw) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
}
}
}
@@ -497,10 +870,10 @@
*outValue = 0;
if (axis >= 0 && axis <= ABS_MAX) {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->hasValidFd() && test_bit(axis, device->absBitmask)) {
+ if (device != nullptr && device->hasValidFd() && device->absBitmask.test(axis)) {
struct input_absinfo info;
if (ioctl(device->fd, EVIOCGABS(axis), &info)) {
ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", axis,
@@ -517,10 +890,10 @@
bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
uint8_t* outFlags) const {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->keyMap.haveKeyLayout()) {
+ if (device != nullptr && device->keyMap.haveKeyLayout()) {
std::vector<int32_t> scanCodes;
for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) {
scanCodes.clear();
@@ -531,7 +904,7 @@
// check the possible scan codes identified by the layout map against the
// map of codes actually emitted by the driver
for (size_t sc = 0; sc < scanCodes.size(); sc++) {
- if (test_bit(scanCodes[sc], device->keyBitmask)) {
+ if (device->keyBitmask.test(scanCodes[sc])) {
outFlags[codeIndex] = 1;
break;
}
@@ -545,14 +918,14 @@
status_t EventHub::mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
status_t status = NAME_NOT_FOUND;
- if (device) {
+ if (device != nullptr) {
// Check the key character map first.
- sp<KeyCharacterMap> kcm = device->getKeyCharacterMap();
- if (kcm != nullptr) {
+ const std::shared_ptr<KeyCharacterMap> kcm = device->getKeyCharacterMap();
+ if (kcm) {
if (!kcm->mapKey(scanCode, usageCode, outKeycode)) {
*outFlags = 0;
status = NO_ERROR;
@@ -567,7 +940,7 @@
}
if (status == NO_ERROR) {
- if (kcm != nullptr) {
+ if (kcm) {
kcm->tryRemapKey(*outKeycode, metaState, outKeycode, outMetaState);
} else {
*outMetaState = metaState;
@@ -585,10 +958,10 @@
}
status_t EventHub::mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxisInfo) const {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->keyMap.haveKeyLayout()) {
+ if (device != nullptr && device->keyMap.haveKeyLayout()) {
status_t err = device->keyMap.keyLayoutMap->mapAxis(scanCode, outAxisInfo);
if (err == NO_ERROR) {
return NO_ERROR;
@@ -598,55 +971,232 @@
return NAME_NOT_FOUND;
}
+base::Result<std::pair<InputDeviceSensorType, int32_t>> EventHub::mapSensor(int32_t deviceId,
+ int32_t absCode) {
+ std::scoped_lock _l(mLock);
+ Device* device = getDeviceLocked(deviceId);
+
+ if (device != nullptr && device->keyMap.haveKeyLayout()) {
+ return device->keyMap.keyLayoutMap->mapSensor(absCode);
+ }
+ return Errorf("Device not found or device has no key layout.");
+}
+
+// Gets the battery info map from battery ID to RawBatteryInfo of the miscellaneous device
+// associated with the device ID. Returns an empty map if no miscellaneous device found.
+const std::unordered_map<int32_t, RawBatteryInfo>& EventHub::getBatteryInfoLocked(
+ int32_t deviceId) const {
+ static const std::unordered_map<int32_t, RawBatteryInfo> EMPTY_BATTERY_INFO = {};
+ Device* device = getDeviceLocked(deviceId);
+ if (device == nullptr || !device->associatedDevice) {
+ return EMPTY_BATTERY_INFO;
+ }
+ return device->associatedDevice->batteryInfos;
+}
+
+const std::vector<int32_t> EventHub::getRawBatteryIds(int32_t deviceId) {
+ std::scoped_lock _l(mLock);
+ std::vector<int32_t> batteryIds;
+
+ for (const auto [id, info] : getBatteryInfoLocked(deviceId)) {
+ batteryIds.push_back(id);
+ }
+
+ return batteryIds;
+}
+
+std::optional<RawBatteryInfo> EventHub::getRawBatteryInfo(int32_t deviceId, int32_t batteryId) {
+ std::scoped_lock _l(mLock);
+
+ const auto infos = getBatteryInfoLocked(deviceId);
+
+ auto it = infos.find(batteryId);
+ if (it != infos.end()) {
+ return it->second;
+ }
+
+ return std::nullopt;
+}
+
+// Gets the light info map from light ID to RawLightInfo of the miscellaneous device associated
+// with the deivice ID. Returns an empty map if no miscellaneous device found.
+const std::unordered_map<int32_t, RawLightInfo>& EventHub::getLightInfoLocked(
+ int32_t deviceId) const {
+ static const std::unordered_map<int32_t, RawLightInfo> EMPTY_LIGHT_INFO = {};
+ Device* device = getDeviceLocked(deviceId);
+ if (device == nullptr || !device->associatedDevice) {
+ return EMPTY_LIGHT_INFO;
+ }
+ return device->associatedDevice->lightInfos;
+}
+
+const std::vector<int32_t> EventHub::getRawLightIds(int32_t deviceId) {
+ std::scoped_lock _l(mLock);
+ std::vector<int32_t> lightIds;
+
+ for (const auto [id, info] : getLightInfoLocked(deviceId)) {
+ lightIds.push_back(id);
+ }
+
+ return lightIds;
+}
+
+std::optional<RawLightInfo> EventHub::getRawLightInfo(int32_t deviceId, int32_t lightId) {
+ std::scoped_lock _l(mLock);
+
+ const auto infos = getLightInfoLocked(deviceId);
+
+ auto it = infos.find(lightId);
+ if (it != infos.end()) {
+ return it->second;
+ }
+
+ return std::nullopt;
+}
+
+std::optional<int32_t> EventHub::getLightBrightness(int32_t deviceId, int32_t lightId) {
+ std::scoped_lock _l(mLock);
+
+ const auto infos = getLightInfoLocked(deviceId);
+ auto it = infos.find(lightId);
+ if (it == infos.end()) {
+ return std::nullopt;
+ }
+ std::string buffer;
+ if (!base::ReadFileToString(it->second.path / LIGHT_NODES.at(InputLightClass::BRIGHTNESS),
+ &buffer)) {
+ return std::nullopt;
+ }
+ return std::stoi(buffer);
+}
+
+std::optional<std::unordered_map<LightColor, int32_t>> EventHub::getLightIntensities(
+ int32_t deviceId, int32_t lightId) {
+ std::scoped_lock _l(mLock);
+
+ const auto infos = getLightInfoLocked(deviceId);
+ auto lightIt = infos.find(lightId);
+ if (lightIt == infos.end()) {
+ return std::nullopt;
+ }
+
+ auto ret =
+ getColorIndexArray(lightIt->second.path / LIGHT_NODES.at(InputLightClass::MULTI_INDEX));
+
+ if (!ret.has_value()) {
+ return std::nullopt;
+ }
+ std::array<LightColor, COLOR_NUM> colors = ret.value();
+
+ std::string intensityStr;
+ if (!base::ReadFileToString(lightIt->second.path /
+ LIGHT_NODES.at(InputLightClass::MULTI_INTENSITY),
+ &intensityStr)) {
+ return std::nullopt;
+ }
+
+ // Intensity node outputs 3 color values
+ std::regex intensityPattern("([0-9]+)\\s([0-9]+)\\s([0-9]+)[\\n]");
+ std::smatch results;
+
+ if (!std::regex_match(intensityStr, results, intensityPattern)) {
+ return std::nullopt;
+ }
+ std::unordered_map<LightColor, int32_t> intensities;
+ for (size_t i = 1; i < results.size(); i++) {
+ int value = std::stoi(results[i].str());
+ intensities.emplace(colors[i - 1], value);
+ }
+ return intensities;
+}
+
+void EventHub::setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) {
+ std::scoped_lock _l(mLock);
+
+ const auto infos = getLightInfoLocked(deviceId);
+ auto lightIt = infos.find(lightId);
+ if (lightIt == infos.end()) {
+ ALOGE("%s lightId %d not found ", __func__, lightId);
+ return;
+ }
+
+ if (!base::WriteStringToFile(std::to_string(brightness),
+ lightIt->second.path /
+ LIGHT_NODES.at(InputLightClass::BRIGHTNESS))) {
+ ALOGE("Can not write to file, error: %s", strerror(errno));
+ }
+}
+
+void EventHub::setLightIntensities(int32_t deviceId, int32_t lightId,
+ std::unordered_map<LightColor, int32_t> intensities) {
+ std::scoped_lock _l(mLock);
+
+ const auto infos = getLightInfoLocked(deviceId);
+ auto lightIt = infos.find(lightId);
+ if (lightIt == infos.end()) {
+ ALOGE("Light Id %d does not exist.", lightId);
+ return;
+ }
+
+ auto ret =
+ getColorIndexArray(lightIt->second.path / LIGHT_NODES.at(InputLightClass::MULTI_INDEX));
+
+ if (!ret.has_value()) {
+ return;
+ }
+ std::array<LightColor, COLOR_NUM> colors = ret.value();
+
+ std::string rgbStr;
+ for (size_t i = 0; i < COLOR_NUM; i++) {
+ auto it = intensities.find(colors[i]);
+ if (it != intensities.end()) {
+ rgbStr += std::to_string(it->second);
+ // Insert space between colors
+ if (i < COLOR_NUM - 1) {
+ rgbStr += " ";
+ }
+ }
+ }
+ // Append new line
+ rgbStr += "\n";
+
+ if (!base::WriteStringToFile(rgbStr,
+ lightIt->second.path /
+ LIGHT_NODES.at(InputLightClass::MULTI_INTENSITY))) {
+ ALOGE("Can not write to file, error: %s", strerror(errno));
+ }
+}
+
void EventHub::setExcludedDevices(const std::vector<std::string>& devices) {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
mExcludedDevices = devices;
}
bool EventHub::hasScanCode(int32_t deviceId, int32_t scanCode) const {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && scanCode >= 0 && scanCode <= KEY_MAX) {
- if (test_bit(scanCode, device->keyBitmask)) {
- return true;
- }
+ if (device != nullptr && scanCode >= 0 && scanCode <= KEY_MAX) {
+ return device->keyBitmask.test(scanCode);
}
return false;
}
bool EventHub::hasLed(int32_t deviceId, int32_t led) const {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
int32_t sc;
- if (device && mapLed(device, led, &sc) == NO_ERROR) {
- if (test_bit(sc, device->ledBitmask)) {
- return true;
- }
+ if (device != nullptr && device->mapLed(led, &sc) == NO_ERROR) {
+ return device->ledBitmask.test(sc);
}
return false;
}
void EventHub::setLedState(int32_t deviceId, int32_t led, bool on) {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
- setLedStateLocked(device, led, on);
-}
-
-void EventHub::setLedStateLocked(Device* device, int32_t led, bool on) {
- int32_t sc;
- if (device && device->hasValidFd() && mapLed(device, led, &sc) != NAME_NOT_FOUND) {
- struct input_event ev;
- ev.time.tv_sec = 0;
- ev.time.tv_usec = 0;
- ev.type = EV_LED;
- ev.code = sc;
- ev.value = on ? 1 : 0;
-
- ssize_t nWrite;
- do {
- nWrite = write(device->fd, &ev, sizeof(struct input_event));
- } while (nWrite == -1 && errno == EINTR);
+ if (device != nullptr && device->hasValidFd()) {
+ device->setLedStateLocked(led, on);
}
}
@@ -654,33 +1204,31 @@
std::vector<VirtualKeyDefinition>& outVirtualKeys) const {
outVirtualKeys.clear();
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->virtualKeyMap) {
+ if (device != nullptr && device->virtualKeyMap) {
const std::vector<VirtualKeyDefinition> virtualKeys =
device->virtualKeyMap->getVirtualKeys();
outVirtualKeys.insert(outVirtualKeys.end(), virtualKeys.begin(), virtualKeys.end());
}
}
-sp<KeyCharacterMap> EventHub::getKeyCharacterMap(int32_t deviceId) const {
- AutoMutex _l(mLock);
+const std::shared_ptr<KeyCharacterMap> EventHub::getKeyCharacterMap(int32_t deviceId) const {
+ std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device) {
+ if (device != nullptr) {
return device->getKeyCharacterMap();
}
return nullptr;
}
-bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) {
- AutoMutex _l(mLock);
+bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId, std::shared_ptr<KeyCharacterMap> map) {
+ std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device) {
- if (map != device->overlayKeyMap) {
- device->overlayKeyMap = map;
- device->combinedKeyMap = KeyCharacterMap::combine(device->keyMap.keyCharacterMap, map);
- return true;
- }
+ if (device != nullptr && map != nullptr && device->keyMap.keyCharacterMap != nullptr) {
+ device->keyMap.keyCharacterMap->combine(*map);
+ device->keyMap.keyCharacterMapFile = device->keyMap.keyCharacterMap->getLoadFileName();
+ return true;
}
return false;
}
@@ -735,17 +1283,18 @@
identifier.descriptor.c_str());
}
-void EventHub::vibrate(int32_t deviceId, nsecs_t duration) {
- AutoMutex _l(mLock);
+void EventHub::vibrate(int32_t deviceId, const VibrationElement& element) {
+ std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->hasValidFd()) {
+ if (device != nullptr && device->hasValidFd()) {
ff_effect effect;
memset(&effect, 0, sizeof(effect));
effect.type = FF_RUMBLE;
effect.id = device->ffEffectId;
- effect.u.rumble.strong_magnitude = 0xc000;
- effect.u.rumble.weak_magnitude = 0xc000;
- effect.replay.length = (duration + 999999LL) / 1000000LL;
+ // evdev FF_RUMBLE effect only supports two channels of vibration.
+ effect.u.rumble.strong_magnitude = element.getMagnitude(FF_STRONG_MAGNITUDE_CHANNEL_IDX);
+ effect.u.rumble.weak_magnitude = element.getMagnitude(FF_WEAK_MAGNITUDE_CHANNEL_IDX);
+ effect.replay.length = element.duration.count();
effect.replay.delay = 0;
if (ioctl(device->fd, EVIOCSFF, &effect)) {
ALOGW("Could not upload force feedback effect to device %s due to error %d.",
@@ -770,9 +1319,9 @@
}
void EventHub::cancelVibrate(int32_t deviceId) {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->hasValidFd()) {
+ if (device != nullptr && device->hasValidFd()) {
if (device->ffEffectPlaying) {
device->ffEffectPlaying = false;
@@ -791,12 +1340,22 @@
}
}
+std::vector<int32_t> EventHub::getVibratorIds(int32_t deviceId) {
+ std::scoped_lock _l(mLock);
+ std::vector<int32_t> vibrators;
+ Device* device = getDeviceLocked(deviceId);
+ if (device != nullptr && device->hasValidFd() &&
+ device->classes.test(InputDeviceClass::VIBRATOR)) {
+ vibrators.push_back(FF_STRONG_MAGNITUDE_CHANNEL_IDX);
+ vibrators.push_back(FF_WEAK_MAGNITUDE_CHANNEL_IDX);
+ }
+ return vibrators;
+}
+
EventHub::Device* EventHub::getDeviceByDescriptorLocked(const std::string& descriptor) const {
- size_t size = mDevices.size();
- for (size_t i = 0; i < size; i++) {
- Device* device = mDevices.valueAt(i);
+ for (const auto& [id, device] : mDevices) {
if (descriptor == device->identifier.descriptor) {
- return device;
+ return device.get();
}
}
return nullptr;
@@ -806,15 +1365,14 @@
if (deviceId == ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID) {
deviceId = mBuiltInKeyboardId;
}
- ssize_t index = mDevices.indexOfKey(deviceId);
- return index >= 0 ? mDevices.valueAt(index) : NULL;
+ const auto& it = mDevices.find(deviceId);
+ return it != mDevices.end() ? it->second.get() : nullptr;
}
-EventHub::Device* EventHub::getDeviceByPathLocked(const char* devicePath) const {
- for (size_t i = 0; i < mDevices.size(); i++) {
- Device* device = mDevices.valueAt(i);
+EventHub::Device* EventHub::getDeviceByPathLocked(const std::string& devicePath) const {
+ for (const auto& [id, device] : mDevices) {
if (device->path == devicePath) {
- return device;
+ return device.get();
}
}
return nullptr;
@@ -828,15 +1386,14 @@
* devices are ignored.
*/
EventHub::Device* EventHub::getDeviceByFdLocked(int fd) const {
- for (size_t i = 0; i < mDevices.size(); i++) {
- Device* device = mDevices.valueAt(i);
+ for (const auto& [id, device] : mDevices) {
if (device->fd == fd) {
// This is an input device event
- return device;
+ return device.get();
}
if (device->videoDevice && device->videoDevice->getFd() == fd) {
// This is a video device event
- return device;
+ return device.get();
}
}
// We do not check mUnattachedVideoDevices here because they should not participate in epoll,
@@ -844,10 +1401,65 @@
return nullptr;
}
+std::optional<int32_t> EventHub::getBatteryCapacity(int32_t deviceId, int32_t batteryId) const {
+ std::scoped_lock _l(mLock);
+
+ const auto infos = getBatteryInfoLocked(deviceId);
+ auto it = infos.find(batteryId);
+ if (it == infos.end()) {
+ return std::nullopt;
+ }
+ std::string buffer;
+
+ // Some devices report battery capacity as an integer through the "capacity" file
+ if (base::ReadFileToString(it->second.path / BATTERY_NODES.at(InputBatteryClass::CAPACITY),
+ &buffer)) {
+ return std::stoi(base::Trim(buffer));
+ }
+
+ // Other devices report capacity as an enum value POWER_SUPPLY_CAPACITY_LEVEL_XXX
+ // These values are taken from kernel source code include/linux/power_supply.h
+ if (base::ReadFileToString(it->second.path /
+ BATTERY_NODES.at(InputBatteryClass::CAPACITY_LEVEL),
+ &buffer)) {
+ // Remove any white space such as trailing new line
+ const auto levelIt = BATTERY_LEVEL.find(base::Trim(buffer));
+ if (levelIt != BATTERY_LEVEL.end()) {
+ return levelIt->second;
+ }
+ }
+
+ return std::nullopt;
+}
+
+std::optional<int32_t> EventHub::getBatteryStatus(int32_t deviceId, int32_t batteryId) const {
+ std::scoped_lock _l(mLock);
+ const auto infos = getBatteryInfoLocked(deviceId);
+ auto it = infos.find(batteryId);
+ if (it == infos.end()) {
+ return std::nullopt;
+ }
+ std::string buffer;
+
+ if (!base::ReadFileToString(it->second.path / BATTERY_NODES.at(InputBatteryClass::STATUS),
+ &buffer)) {
+ ALOGE("Failed to read sysfs battery info: %s", strerror(errno));
+ return std::nullopt;
+ }
+
+ // Remove white space like trailing new line
+ const auto statusIt = BATTERY_STATUS.find(base::Trim(buffer));
+ if (statusIt != BATTERY_STATUS.end()) {
+ return statusIt->second;
+ }
+
+ return std::nullopt;
+}
+
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
ALOG_ASSERT(bufferSize >= 1);
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
struct input_event readBuffer[bufferSize];
@@ -869,17 +1481,16 @@
}
// Report any devices that had last been added/removed.
- while (mClosingDevices) {
- Device* device = mClosingDevices;
+ for (auto it = mClosingDevices.begin(); it != mClosingDevices.end();) {
+ std::unique_ptr<Device> device = std::move(*it);
ALOGV("Reporting device closed: id=%d, name=%s\n", device->id, device->path.c_str());
- mClosingDevices = device->next;
event->when = now;
event->deviceId = (device->id == mBuiltInKeyboardId)
? ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID
: device->id;
event->type = DEVICE_REMOVED;
event += 1;
- delete device;
+ it = mClosingDevices.erase(it);
mNeedToSendFinishedDeviceScan = true;
if (--capacity == 0) {
break;
@@ -892,14 +1503,30 @@
mNeedToSendFinishedDeviceScan = true;
}
- while (mOpeningDevices != nullptr) {
- Device* device = mOpeningDevices;
+ while (!mOpeningDevices.empty()) {
+ std::unique_ptr<Device> device = std::move(*mOpeningDevices.rbegin());
+ mOpeningDevices.pop_back();
ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str());
- mOpeningDevices = device->next;
event->when = now;
event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
event->type = DEVICE_ADDED;
event += 1;
+
+ // Try to find a matching video device by comparing device names
+ for (auto it = mUnattachedVideoDevices.begin(); it != mUnattachedVideoDevices.end();
+ it++) {
+ std::unique_ptr<TouchVideoDevice>& videoDevice = *it;
+ if (tryAddVideoDeviceLocked(*device, videoDevice)) {
+ // videoDevice was transferred to 'device'
+ it = mUnattachedVideoDevices.erase(it);
+ break;
+ }
+ }
+
+ auto [dev_it, inserted] = mDevices.insert_or_assign(device->id, std::move(device));
+ if (!inserted) {
+ ALOGW("Device id %d exists, replaced.", device->id);
+ }
mNeedToSendFinishedDeviceScan = true;
if (--capacity == 0) {
break;
@@ -933,11 +1560,11 @@
if (eventItem.events & EPOLLIN) {
ALOGV("awoken after wake()");
awoken = true;
- char buffer[16];
+ char wakeReadBuffer[16];
ssize_t nRead;
do {
- nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
- } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
+ nRead = read(mWakeReadPipeFd, wakeReadBuffer, sizeof(wakeReadBuffer));
+ } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(wakeReadBuffer));
} else {
ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.",
eventItem.events);
@@ -946,7 +1573,7 @@
}
Device* device = getDeviceByFdLocked(eventItem.data.fd);
- if (!device) {
+ if (device == nullptr) {
ALOGE("Received unexpected epoll event 0x%08x for unknown fd %d.", eventItem.events,
eventItem.data.fd);
ALOG_ASSERT(!DEBUG);
@@ -982,7 +1609,7 @@
" bufferSize: %zu capacity: %zu errno: %d)\n",
device->fd, readSize, bufferSize, capacity, errno);
deviceChanged = true;
- closeDeviceLocked(device);
+ closeDeviceLocked(*device);
} else if (readSize < 0) {
if (errno != EAGAIN && errno != EINTR) {
ALOGW("could not get event (errno=%d)", errno);
@@ -996,6 +1623,7 @@
for (size_t i = 0; i < count; i++) {
struct input_event& iev = readBuffer[i];
event->when = processEventTimestamp(iev);
+ event->readTime = systemTime(SYSTEM_TIME_MONOTONIC);
event->deviceId = deviceId;
event->type = iev.type;
event->code = iev.code;
@@ -1014,7 +1642,7 @@
ALOGI("Removing device %s due to epoll hang-up event.",
device->identifier.name.c_str());
deviceChanged = true;
- closeDeviceLocked(device);
+ closeDeviceLocked(*device);
} else {
ALOGW("Received unexpected epoll event 0x%08x for device %s.", eventItem.events,
device->identifier.name.c_str());
@@ -1086,10 +1714,10 @@
}
std::vector<TouchVideoFrame> EventHub::getVideoFrames(int32_t deviceId) {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (!device || !device->videoDevice) {
+ if (device == nullptr || !device->videoDevice) {
return {};
}
return device->videoDevice->consumeFrames();
@@ -1119,24 +1747,13 @@
ALOGE("scan video dir failed for %s", VIDEO_DEVICE_PATH);
}
}
- if (mDevices.indexOfKey(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID) < 0) {
+ if (mDevices.find(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID) == mDevices.end()) {
createVirtualKeyboardLocked();
}
}
// ----------------------------------------------------------------------------
-static bool containsNonZeroByte(const uint8_t* array, uint32_t startIndex, uint32_t endIndex) {
- const uint8_t* end = array + endIndex;
- array += startIndex;
- while (array != end) {
- if (*(array++) != 0) {
- return true;
- }
- }
- return false;
-}
-
static const int32_t GAMEPAD_KEYCODES[] = {
AKEYCODE_BUTTON_A, AKEYCODE_BUTTON_B, AKEYCODE_BUTTON_C, //
AKEYCODE_BUTTON_X, AKEYCODE_BUTTON_Y, AKEYCODE_BUTTON_Z, //
@@ -1166,20 +1783,14 @@
return OK;
}
-status_t EventHub::registerDeviceForEpollLocked(Device* device) {
- if (device == nullptr) {
- if (DEBUG) {
- LOG_ALWAYS_FATAL("Cannot call registerDeviceForEpollLocked with null Device");
- }
- return BAD_VALUE;
- }
- status_t result = registerFdForEpoll(device->fd);
+status_t EventHub::registerDeviceForEpollLocked(Device& device) {
+ status_t result = registerFdForEpoll(device.fd);
if (result != OK) {
- ALOGE("Could not add input device fd to epoll for device %" PRId32, device->id);
+ ALOGE("Could not add input device fd to epoll for device %" PRId32, device.id);
return result;
}
- if (device->videoDevice) {
- registerVideoDeviceForEpollLocked(*device->videoDevice);
+ if (device.videoDevice) {
+ registerVideoDeviceForEpollLocked(*device.videoDevice);
}
return result;
}
@@ -1191,16 +1802,16 @@
}
}
-status_t EventHub::unregisterDeviceFromEpollLocked(Device* device) {
- if (device->hasValidFd()) {
- status_t result = unregisterFdFromEpoll(device->fd);
+status_t EventHub::unregisterDeviceFromEpollLocked(Device& device) {
+ if (device.hasValidFd()) {
+ status_t result = unregisterFdFromEpoll(device.fd);
if (result != OK) {
- ALOGW("Could not remove input device fd from epoll for device %" PRId32, device->id);
+ ALOGW("Could not remove input device fd from epoll for device %" PRId32, device.id);
return result;
}
}
- if (device->videoDevice) {
- unregisterVideoDeviceFromEpollLocked(*device->videoDevice);
+ if (device.videoDevice) {
+ unregisterVideoDeviceFromEpollLocked(*device.videoDevice);
}
return OK;
}
@@ -1215,22 +1826,39 @@
}
}
-status_t EventHub::openDeviceLocked(const char* devicePath) {
+void EventHub::reportDeviceAddedForStatisticsLocked(const InputDeviceIdentifier& identifier,
+ Flags<InputDeviceClass> classes) {
+ android::util::stats_write(android::util::INPUTDEVICE_REGISTERED, identifier.name.c_str(),
+ identifier.vendor, identifier.product, identifier.version,
+ identifier.bus, identifier.uniqueId.c_str(), classes.get());
+}
+
+void EventHub::openDeviceLocked(const std::string& devicePath) {
+ // If an input device happens to register around the time when EventHub's constructor runs, it
+ // is possible that the same input event node (for example, /dev/input/event3) will be noticed
+ // in both 'inotify' callback and also in the 'scanDirLocked' pass. To prevent duplicate devices
+ // from getting registered, ensure that this path is not already covered by an existing device.
+ for (const auto& [deviceId, device] : mDevices) {
+ if (device->path == devicePath) {
+ return; // device was already registered
+ }
+ }
+
char buffer[80];
- ALOGV("Opening device: %s", devicePath);
+ ALOGV("Opening device: %s", devicePath.c_str());
- int fd = open(devicePath, O_RDWR | O_CLOEXEC | O_NONBLOCK);
+ int fd = open(devicePath.c_str(), O_RDWR | O_CLOEXEC | O_NONBLOCK);
if (fd < 0) {
- ALOGE("could not open %s, %s\n", devicePath, strerror(errno));
- return -1;
+ ALOGE("could not open %s, %s\n", devicePath.c_str(), strerror(errno));
+ return;
}
InputDeviceIdentifier identifier;
// Get device name.
if (ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {
- ALOGE("Could not get device name for %s: %s", devicePath, strerror(errno));
+ ALOGE("Could not get device name for %s: %s", devicePath.c_str(), strerror(errno));
} else {
buffer[sizeof(buffer) - 1] = '\0';
identifier.name = buffer;
@@ -1240,26 +1868,26 @@
for (size_t i = 0; i < mExcludedDevices.size(); i++) {
const std::string& item = mExcludedDevices[i];
if (identifier.name == item) {
- ALOGI("ignoring event id %s driver %s\n", devicePath, item.c_str());
+ ALOGI("ignoring event id %s driver %s\n", devicePath.c_str(), item.c_str());
close(fd);
- return -1;
+ return;
}
}
// Get device driver version.
int driverVersion;
if (ioctl(fd, EVIOCGVERSION, &driverVersion)) {
- ALOGE("could not get driver version for %s, %s\n", devicePath, strerror(errno));
+ ALOGE("could not get driver version for %s, %s\n", devicePath.c_str(), strerror(errno));
close(fd);
- return -1;
+ return;
}
// Get device identifier.
struct input_id inputId;
if (ioctl(fd, EVIOCGID, &inputId)) {
- ALOGE("could not get device input id for %s, %s\n", devicePath, strerror(errno));
+ ALOGE("could not get device input id for %s, %s\n", devicePath.c_str(), strerror(errno));
close(fd);
- return -1;
+ return;
}
identifier.bus = inputId.bustype;
identifier.product = inputId.product;
@@ -1287,9 +1915,9 @@
// Allocate device. (The device object takes ownership of the fd at this point.)
int32_t deviceId = mNextDeviceId++;
- Device* device = new Device(fd, deviceId, devicePath, identifier);
+ std::unique_ptr<Device> device = std::make_unique<Device>(fd, deviceId, devicePath, identifier);
- ALOGV("add device %d: %s\n", deviceId, devicePath);
+ ALOGV("add device %d: %s\n", deviceId, devicePath.c_str());
ALOGV(" bus: %04x\n"
" vendor %04x\n"
" product %04x\n"
@@ -1303,35 +1931,53 @@
driverVersion & 0xff);
// Load the configuration file for the device.
- loadConfigurationLocked(device);
+ device->loadConfigurationLocked();
+
+ bool hasBattery = false;
+ bool hasLights = false;
+ // Check the sysfs root path
+ std::optional<std::filesystem::path> sysfsRootPath = getSysfsRootPath(devicePath.c_str());
+ if (sysfsRootPath.has_value()) {
+ std::shared_ptr<AssociatedDevice> associatedDevice;
+ for (const auto& [id, dev] : mDevices) {
+ if (device->identifier.descriptor == dev->identifier.descriptor &&
+ !dev->associatedDevice) {
+ associatedDevice = dev->associatedDevice;
+ }
+ }
+ if (!associatedDevice) {
+ associatedDevice = std::make_shared<AssociatedDevice>(sysfsRootPath.value());
+ }
+ hasBattery = associatedDevice->configureBatteryLocked();
+ hasLights = associatedDevice->configureLightsLocked();
+
+ device->associatedDevice = associatedDevice;
+ }
// Figure out the kinds of events the device reports.
- ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)), device->keyBitmask);
- ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(device->absBitmask)), device->absBitmask);
- ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask);
- ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)), device->swBitmask);
- ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)), device->ledBitmask);
- ioctl(fd, EVIOCGBIT(EV_FF, sizeof(device->ffBitmask)), device->ffBitmask);
- ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask);
+ device->readDeviceBitMask(EVIOCGBIT(EV_KEY, 0), device->keyBitmask);
+ device->readDeviceBitMask(EVIOCGBIT(EV_ABS, 0), device->absBitmask);
+ device->readDeviceBitMask(EVIOCGBIT(EV_REL, 0), device->relBitmask);
+ device->readDeviceBitMask(EVIOCGBIT(EV_SW, 0), device->swBitmask);
+ device->readDeviceBitMask(EVIOCGBIT(EV_LED, 0), device->ledBitmask);
+ device->readDeviceBitMask(EVIOCGBIT(EV_FF, 0), device->ffBitmask);
+ device->readDeviceBitMask(EVIOCGBIT(EV_MSC, 0), device->mscBitmask);
+ device->readDeviceBitMask(EVIOCGPROP(0), device->propBitmask);
// See if this is a keyboard. Ignore everything in the button range except for
// joystick and gamepad buttons which are handled like keyboards for the most part.
bool haveKeyboardKeys =
- containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC)) ||
- containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_WHEEL),
- sizeof_bit_array(KEY_MAX + 1));
- bool haveGamepadButtons = containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_MISC),
- sizeof_bit_array(BTN_MOUSE)) ||
- containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_JOYSTICK),
- sizeof_bit_array(BTN_DIGI));
+ device->keyBitmask.any(0, BTN_MISC) || device->keyBitmask.any(BTN_WHEEL, KEY_MAX + 1);
+ bool haveGamepadButtons = device->keyBitmask.any(BTN_MISC, BTN_MOUSE) ||
+ device->keyBitmask.any(BTN_JOYSTICK, BTN_DIGI);
if (haveKeyboardKeys || haveGamepadButtons) {
- device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
+ device->classes |= InputDeviceClass::KEYBOARD;
}
// See if this is a cursor device such as a trackball or mouse.
- if (test_bit(BTN_MOUSE, device->keyBitmask) && test_bit(REL_X, device->relBitmask) &&
- test_bit(REL_Y, device->relBitmask)) {
- device->classes |= INPUT_DEVICE_CLASS_CURSOR;
+ if (device->keyBitmask.test(BTN_MOUSE) && device->relBitmask.test(REL_X) &&
+ device->relBitmask.test(REL_Y)) {
+ device->classes |= InputDeviceClass::CURSOR;
}
// See if this is a rotary encoder type device.
@@ -1339,185 +1985,164 @@
if (device->configuration &&
device->configuration->tryGetProperty(String8("device.type"), deviceType)) {
if (!deviceType.compare(String8("rotaryEncoder"))) {
- device->classes |= INPUT_DEVICE_CLASS_ROTARY_ENCODER;
+ device->classes |= InputDeviceClass::ROTARY_ENCODER;
}
}
// See if this is a touch pad.
// Is this a new modern multi-touch driver?
- if (test_bit(ABS_MT_POSITION_X, device->absBitmask) &&
- test_bit(ABS_MT_POSITION_Y, device->absBitmask)) {
+ if (device->absBitmask.test(ABS_MT_POSITION_X) && device->absBitmask.test(ABS_MT_POSITION_Y)) {
// Some joysticks such as the PS3 controller report axes that conflict
// with the ABS_MT range. Try to confirm that the device really is
// a touch screen.
- if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) {
- device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT;
+ if (device->keyBitmask.test(BTN_TOUCH) || !haveGamepadButtons) {
+ device->classes |= (InputDeviceClass::TOUCH | InputDeviceClass::TOUCH_MT);
}
// Is this an old style single-touch driver?
- } else if (test_bit(BTN_TOUCH, device->keyBitmask) && test_bit(ABS_X, device->absBitmask) &&
- test_bit(ABS_Y, device->absBitmask)) {
- device->classes |= INPUT_DEVICE_CLASS_TOUCH;
+ } else if (device->keyBitmask.test(BTN_TOUCH) && device->absBitmask.test(ABS_X) &&
+ device->absBitmask.test(ABS_Y)) {
+ device->classes |= InputDeviceClass::TOUCH;
// Is this a BT stylus?
- } else if ((test_bit(ABS_PRESSURE, device->absBitmask) ||
- test_bit(BTN_TOUCH, device->keyBitmask)) &&
- !test_bit(ABS_X, device->absBitmask) && !test_bit(ABS_Y, device->absBitmask)) {
- device->classes |= INPUT_DEVICE_CLASS_EXTERNAL_STYLUS;
+ } else if ((device->absBitmask.test(ABS_PRESSURE) || device->keyBitmask.test(BTN_TOUCH)) &&
+ !device->absBitmask.test(ABS_X) && !device->absBitmask.test(ABS_Y)) {
+ device->classes |= InputDeviceClass::EXTERNAL_STYLUS;
// Keyboard will try to claim some of the buttons but we really want to reserve those so we
// can fuse it with the touch screen data, so just take them back. Note this means an
// external stylus cannot also be a keyboard device.
- device->classes &= ~INPUT_DEVICE_CLASS_KEYBOARD;
+ device->classes &= ~InputDeviceClass::KEYBOARD;
}
// See if this device is a joystick.
// Assumes that joysticks always have gamepad buttons in order to distinguish them
// from other devices such as accelerometers that also have absolute axes.
if (haveGamepadButtons) {
- uint32_t assumedClasses = device->classes | INPUT_DEVICE_CLASS_JOYSTICK;
+ auto assumedClasses = device->classes | InputDeviceClass::JOYSTICK;
for (int i = 0; i <= ABS_MAX; i++) {
- if (test_bit(i, device->absBitmask) &&
- (getAbsAxisUsage(i, assumedClasses) & INPUT_DEVICE_CLASS_JOYSTICK)) {
+ if (device->absBitmask.test(i) &&
+ (getAbsAxisUsage(i, assumedClasses).test(InputDeviceClass::JOYSTICK))) {
device->classes = assumedClasses;
break;
}
}
}
+ // Check whether this device is an accelerometer.
+ if (device->propBitmask.test(INPUT_PROP_ACCELEROMETER)) {
+ device->classes |= InputDeviceClass::SENSOR;
+ }
+
// Check whether this device has switches.
for (int i = 0; i <= SW_MAX; i++) {
- if (test_bit(i, device->swBitmask)) {
- device->classes |= INPUT_DEVICE_CLASS_SWITCH;
+ if (device->swBitmask.test(i)) {
+ device->classes |= InputDeviceClass::SWITCH;
break;
}
}
// Check whether this device supports the vibrator.
- if (test_bit(FF_RUMBLE, device->ffBitmask)) {
- device->classes |= INPUT_DEVICE_CLASS_VIBRATOR;
+ if (device->ffBitmask.test(FF_RUMBLE)) {
+ device->classes |= InputDeviceClass::VIBRATOR;
}
// Configure virtual keys.
- if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) {
+ if ((device->classes.test(InputDeviceClass::TOUCH))) {
// Load the virtual keys for the touch screen, if any.
// We do this now so that we can make sure to load the keymap if necessary.
- bool success = loadVirtualKeyMapLocked(device);
+ bool success = device->loadVirtualKeyMapLocked();
if (success) {
- device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
+ device->classes |= InputDeviceClass::KEYBOARD;
}
}
// Load the key map.
- // We need to do this for joysticks too because the key layout may specify axes.
+ // We need to do this for joysticks too because the key layout may specify axes, and for
+ // sensor as well because the key layout may specify the axes to sensor data mapping.
status_t keyMapStatus = NAME_NOT_FOUND;
- if (device->classes & (INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_JOYSTICK)) {
+ if (device->classes.any(InputDeviceClass::KEYBOARD | InputDeviceClass::JOYSTICK |
+ InputDeviceClass::SENSOR)) {
// Load the keymap for the device.
- keyMapStatus = loadKeyMapLocked(device);
+ keyMapStatus = device->loadKeyMapLocked();
}
// Configure the keyboard, gamepad or virtual keyboard.
- if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) {
+ if (device->classes.test(InputDeviceClass::KEYBOARD)) {
// Register the keyboard as a built-in keyboard if it is eligible.
if (!keyMapStatus && mBuiltInKeyboardId == NO_BUILT_IN_KEYBOARD &&
- isEligibleBuiltInKeyboard(device->identifier, device->configuration, &device->keyMap)) {
+ isEligibleBuiltInKeyboard(device->identifier, device->configuration.get(),
+ &device->keyMap)) {
mBuiltInKeyboardId = device->id;
}
// 'Q' key support = cheap test of whether this is an alpha-capable kbd
- if (hasKeycodeLocked(device, AKEYCODE_Q)) {
- device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
+ if (device->hasKeycodeLocked(AKEYCODE_Q)) {
+ device->classes |= InputDeviceClass::ALPHAKEY;
}
// See if this device has a DPAD.
- if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) &&
- hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) &&
- hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) &&
- hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) &&
- hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) {
- device->classes |= INPUT_DEVICE_CLASS_DPAD;
+ if (device->hasKeycodeLocked(AKEYCODE_DPAD_UP) &&
+ device->hasKeycodeLocked(AKEYCODE_DPAD_DOWN) &&
+ device->hasKeycodeLocked(AKEYCODE_DPAD_LEFT) &&
+ device->hasKeycodeLocked(AKEYCODE_DPAD_RIGHT) &&
+ device->hasKeycodeLocked(AKEYCODE_DPAD_CENTER)) {
+ device->classes |= InputDeviceClass::DPAD;
}
// See if this device has a gamepad.
for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES) / sizeof(GAMEPAD_KEYCODES[0]); i++) {
- if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) {
- device->classes |= INPUT_DEVICE_CLASS_GAMEPAD;
+ if (device->hasKeycodeLocked(GAMEPAD_KEYCODES[i])) {
+ device->classes |= InputDeviceClass::GAMEPAD;
break;
}
}
}
// If the device isn't recognized as something we handle, don't monitor it.
- if (device->classes == 0) {
- ALOGV("Dropping device: id=%d, path='%s', name='%s'", deviceId, devicePath,
+ if (device->classes == Flags<InputDeviceClass>(0)) {
+ ALOGV("Dropping device: id=%d, path='%s', name='%s'", deviceId, devicePath.c_str(),
device->identifier.name.c_str());
- delete device;
- return -1;
+ return;
+ }
+
+ // Classify InputDeviceClass::BATTERY.
+ if (hasBattery) {
+ device->classes |= InputDeviceClass::BATTERY;
+ }
+
+ // Classify InputDeviceClass::LIGHT.
+ if (hasLights) {
+ device->classes |= InputDeviceClass::LIGHT;
}
// Determine whether the device has a mic.
- if (deviceHasMicLocked(device)) {
- device->classes |= INPUT_DEVICE_CLASS_MIC;
+ if (device->deviceHasMicLocked()) {
+ device->classes |= InputDeviceClass::MIC;
}
// Determine whether the device is external or internal.
- if (isExternalDeviceLocked(device)) {
- device->classes |= INPUT_DEVICE_CLASS_EXTERNAL;
+ if (device->isExternalDeviceLocked()) {
+ device->classes |= InputDeviceClass::EXTERNAL;
}
- if (device->classes & (INPUT_DEVICE_CLASS_JOYSTICK | INPUT_DEVICE_CLASS_DPAD) &&
- device->classes & INPUT_DEVICE_CLASS_GAMEPAD) {
- device->controllerNumber = getNextControllerNumberLocked(device);
- setLedForControllerLocked(device);
+ if (device->classes.any(InputDeviceClass::JOYSTICK | InputDeviceClass::DPAD) &&
+ device->classes.test(InputDeviceClass::GAMEPAD)) {
+ device->controllerNumber = getNextControllerNumberLocked(device->identifier.name);
+ device->setLedForControllerLocked();
}
- // Find a matching video device by comparing device names
- // This should be done before registerDeviceForEpollLocked, so that both fds are added to epoll
- for (std::unique_ptr<TouchVideoDevice>& videoDevice : mUnattachedVideoDevices) {
- if (device->identifier.name == videoDevice->getName()) {
- device->videoDevice = std::move(videoDevice);
- break;
- }
- }
- mUnattachedVideoDevices
- .erase(std::remove_if(mUnattachedVideoDevices.begin(), mUnattachedVideoDevices.end(),
- [](const std::unique_ptr<TouchVideoDevice>& videoDevice) {
- return videoDevice == nullptr;
- }),
- mUnattachedVideoDevices.end());
-
- if (registerDeviceForEpollLocked(device) != OK) {
- delete device;
- return -1;
+ if (registerDeviceForEpollLocked(*device) != OK) {
+ return;
}
- configureFd(device);
+ device->configureFd();
- ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, "
+ ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=%s, "
"configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, ",
- deviceId, fd, devicePath, device->identifier.name.c_str(), device->classes,
- device->configurationFile.c_str(), device->keyMap.keyLayoutFile.c_str(),
- device->keyMap.keyCharacterMapFile.c_str(), toString(mBuiltInKeyboardId == deviceId));
+ deviceId, fd, devicePath.c_str(), device->identifier.name.c_str(),
+ device->classes.string().c_str(), device->configurationFile.c_str(),
+ device->keyMap.keyLayoutFile.c_str(), device->keyMap.keyCharacterMapFile.c_str(),
+ toString(mBuiltInKeyboardId == deviceId));
- addDeviceLocked(device);
- return OK;
-}
-
-void EventHub::configureFd(Device* device) {
- // Set fd parameters with ioctl, such as key repeat, suspend block, and clock type
- if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) {
- // Disable kernel key repeat since we handle it ourselves
- unsigned int repeatRate[] = {0, 0};
- if (ioctl(device->fd, EVIOCSREP, repeatRate)) {
- ALOGW("Unable to disable kernel key repeat for %s: %s", device->path.c_str(),
- strerror(errno));
- }
- }
-
- // Tell the kernel that we want to use the monotonic clock for reporting timestamps
- // associated with input events. This is important because the input system
- // uses the timestamps extensively and assumes they were recorded using the monotonic
- // clock.
- int clockId = CLOCK_MONOTONIC;
- bool usingClockIoctl = !ioctl(device->fd, EVIOCSCLOCKID, &clockId);
- ALOGI("usingClockIoctl=%s", toString(usingClockIoctl));
+ addDeviceLocked(std::move(device));
}
void EventHub::openVideoDeviceLocked(const std::string& devicePath) {
@@ -1527,14 +2152,9 @@
return;
}
// Transfer ownership of this video device to a matching input device
- for (size_t i = 0; i < mDevices.size(); i++) {
- Device* device = mDevices.valueAt(i);
- if (videoDevice->getName() == device->identifier.name) {
- device->videoDevice = std::move(videoDevice);
- if (device->enabled) {
- registerVideoDeviceForEpollLocked(*device->videoDevice);
- }
- return;
+ for (const auto& [id, device] : mDevices) {
+ if (tryAddVideoDeviceLocked(*device, videoDevice)) {
+ return; // 'device' now owns 'videoDevice'
}
}
@@ -1545,8 +2165,20 @@
mUnattachedVideoDevices.push_back(std::move(videoDevice));
}
+bool EventHub::tryAddVideoDeviceLocked(EventHub::Device& device,
+ std::unique_ptr<TouchVideoDevice>& videoDevice) {
+ if (videoDevice->getName() != device.identifier.name) {
+ return false;
+ }
+ device.videoDevice = std::move(videoDevice);
+ if (device.enabled) {
+ registerVideoDeviceForEpollLocked(*device.videoDevice);
+ }
+ return true;
+}
+
bool EventHub::isDeviceEnabled(int32_t deviceId) {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
if (device == nullptr) {
ALOGE("Invalid device id=%" PRId32 " provided to %s", deviceId, __func__);
@@ -1556,7 +2188,7 @@
}
status_t EventHub::enableDevice(int32_t deviceId) {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
if (device == nullptr) {
ALOGE("Invalid device id=%" PRId32 " provided to %s", deviceId, __func__);
@@ -1572,13 +2204,13 @@
return result;
}
- configureFd(device);
+ device->configureFd();
- return registerDeviceForEpollLocked(device);
+ return registerDeviceForEpollLocked(*device);
}
status_t EventHub::disableDevice(int32_t deviceId) {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
if (device == nullptr) {
ALOGE("Invalid device id=%" PRId32 " provided to %s", deviceId, __func__);
@@ -1588,7 +2220,7 @@
ALOGW("Duplicate call to %s, input device already disabled", __func__);
return OK;
}
- unregisterDeviceFromEpollLocked(device);
+ unregisterDeviceFromEpollLocked(*device);
return device->disable();
}
@@ -1598,77 +2230,24 @@
identifier.uniqueId = "<virtual>";
assignDescriptorLocked(identifier);
- Device* device =
- new Device(-1, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, "<virtual>", identifier);
- device->classes = INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_ALPHAKEY |
- INPUT_DEVICE_CLASS_DPAD | INPUT_DEVICE_CLASS_VIRTUAL;
- loadKeyMapLocked(device);
- addDeviceLocked(device);
+ std::unique_ptr<Device> device =
+ std::make_unique<Device>(-1, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, "<virtual>",
+ identifier);
+ device->classes = InputDeviceClass::KEYBOARD | InputDeviceClass::ALPHAKEY |
+ InputDeviceClass::DPAD | InputDeviceClass::VIRTUAL;
+ device->loadKeyMapLocked();
+ addDeviceLocked(std::move(device));
}
-void EventHub::addDeviceLocked(Device* device) {
- mDevices.add(device->id, device);
- device->next = mOpeningDevices;
- mOpeningDevices = device;
+void EventHub::addDeviceLocked(std::unique_ptr<Device> device) {
+ reportDeviceAddedForStatisticsLocked(device->identifier, device->classes);
+ mOpeningDevices.push_back(std::move(device));
}
-void EventHub::loadConfigurationLocked(Device* device) {
- device->configurationFile = getInputDeviceConfigurationFilePathByDeviceIdentifier(
- device->identifier, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION);
- if (device->configurationFile.empty()) {
- ALOGD("No input device configuration file found for device '%s'.",
- device->identifier.name.c_str());
- } else {
- status_t status = PropertyMap::load(String8(device->configurationFile.c_str()),
- &device->configuration);
- if (status) {
- ALOGE("Error loading input device configuration file for device '%s'. "
- "Using default configuration.",
- device->identifier.name.c_str());
- }
- }
-}
-
-bool EventHub::loadVirtualKeyMapLocked(Device* device) {
- // The virtual key map is supplied by the kernel as a system board property file.
- std::string path;
- path += "/sys/board_properties/virtualkeys.";
- path += device->identifier.getCanonicalName();
- if (access(path.c_str(), R_OK)) {
- return false;
- }
- device->virtualKeyMap = VirtualKeyMap::load(path);
- return device->virtualKeyMap != nullptr;
-}
-
-status_t EventHub::loadKeyMapLocked(Device* device) {
- return device->keyMap.load(device->identifier, device->configuration);
-}
-
-bool EventHub::isExternalDeviceLocked(Device* device) {
- if (device->configuration) {
- bool value;
- if (device->configuration->tryGetProperty(String8("device.internal"), value)) {
- return !value;
- }
- }
- return device->identifier.bus == BUS_USB || device->identifier.bus == BUS_BLUETOOTH;
-}
-
-bool EventHub::deviceHasMicLocked(Device* device) {
- if (device->configuration) {
- bool value;
- if (device->configuration->tryGetProperty(String8("audio.mic"), value)) {
- return value;
- }
- }
- return false;
-}
-
-int32_t EventHub::getNextControllerNumberLocked(Device* device) {
+int32_t EventHub::getNextControllerNumberLocked(const std::string& name) {
if (mControllerNumbers.isFull()) {
ALOGI("Maximum number of controllers reached, assigning controller number 0 to device %s",
- device->identifier.name.c_str());
+ name.c_str());
return 0;
}
// Since the controller number 0 is reserved for non-controllers, translate all numbers up by
@@ -1676,61 +2255,19 @@
return static_cast<int32_t>(mControllerNumbers.markFirstUnmarkedBit() + 1);
}
-void EventHub::releaseControllerNumberLocked(Device* device) {
- int32_t num = device->controllerNumber;
- device->controllerNumber = 0;
- if (num == 0) {
- return;
- }
- mControllerNumbers.clearBit(static_cast<uint32_t>(num - 1));
-}
-
-void EventHub::setLedForControllerLocked(Device* device) {
- for (int i = 0; i < MAX_CONTROLLER_LEDS; i++) {
- setLedStateLocked(device, ALED_CONTROLLER_1 + i, device->controllerNumber == i + 1);
+void EventHub::releaseControllerNumberLocked(int32_t num) {
+ if (num > 0) {
+ mControllerNumbers.clearBit(static_cast<uint32_t>(num - 1));
}
}
-bool EventHub::hasKeycodeLocked(Device* device, int keycode) const {
- if (!device->keyMap.haveKeyLayout()) {
- return false;
- }
-
- std::vector<int32_t> scanCodes;
- device->keyMap.keyLayoutMap->findScanCodesForKey(keycode, &scanCodes);
- const size_t N = scanCodes.size();
- for (size_t i = 0; i < N && i <= KEY_MAX; i++) {
- int32_t sc = scanCodes[i];
- if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, device->keyBitmask)) {
- return true;
- }
- }
-
- return false;
-}
-
-status_t EventHub::mapLed(Device* device, int32_t led, int32_t* outScanCode) const {
- if (!device->keyMap.haveKeyLayout()) {
- return NAME_NOT_FOUND;
- }
-
- int32_t scanCode;
- if (device->keyMap.keyLayoutMap->findScanCodeForLed(led, &scanCode) != NAME_NOT_FOUND) {
- if (scanCode >= 0 && scanCode <= LED_MAX && test_bit(scanCode, device->ledBitmask)) {
- *outScanCode = scanCode;
- return NO_ERROR;
- }
- }
- return NAME_NOT_FOUND;
-}
-
-void EventHub::closeDeviceByPathLocked(const char* devicePath) {
+void EventHub::closeDeviceByPathLocked(const std::string& devicePath) {
Device* device = getDeviceByPathLocked(devicePath);
- if (device) {
- closeDeviceLocked(device);
+ if (device != nullptr) {
+ closeDeviceLocked(*device);
return;
}
- ALOGV("Remove device: %s not found, device may already have been removed.", devicePath);
+ ALOGV("Remove device: %s not found, device may already have been removed.", devicePath.c_str());
}
/**
@@ -1741,8 +2278,7 @@
void EventHub::closeVideoDeviceByPathLocked(const std::string& devicePath) {
// A video device may be owned by an existing input device, or it may be stored in
// the mUnattachedVideoDevices queue. Check both locations.
- for (size_t i = 0; i < mDevices.size(); i++) {
- Device* device = mDevices.valueAt(i);
+ for (const auto& [id, device] : mDevices) {
if (device->videoDevice && device->videoDevice->getPath() == devicePath) {
unregisterVideoDeviceFromEpollLocked(*device->videoDevice);
device->videoDevice = nullptr;
@@ -1760,60 +2296,33 @@
void EventHub::closeAllDevicesLocked() {
mUnattachedVideoDevices.clear();
- while (mDevices.size() > 0) {
- closeDeviceLocked(mDevices.valueAt(mDevices.size() - 1));
+ while (!mDevices.empty()) {
+ closeDeviceLocked(*(mDevices.begin()->second));
}
}
-void EventHub::closeDeviceLocked(Device* device) {
- ALOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x", device->path.c_str(),
- device->identifier.name.c_str(), device->id, device->fd, device->classes);
+void EventHub::closeDeviceLocked(Device& device) {
+ ALOGI("Removed device: path=%s name=%s id=%d fd=%d classes=%s", device.path.c_str(),
+ device.identifier.name.c_str(), device.id, device.fd, device.classes.string().c_str());
- if (device->id == mBuiltInKeyboardId) {
+ if (device.id == mBuiltInKeyboardId) {
ALOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this",
- device->path.c_str(), mBuiltInKeyboardId);
+ device.path.c_str(), mBuiltInKeyboardId);
mBuiltInKeyboardId = NO_BUILT_IN_KEYBOARD;
}
unregisterDeviceFromEpollLocked(device);
- if (device->videoDevice) {
+ if (device.videoDevice) {
// This must be done after the video device is removed from epoll
- mUnattachedVideoDevices.push_back(std::move(device->videoDevice));
+ mUnattachedVideoDevices.push_back(std::move(device.videoDevice));
}
- releaseControllerNumberLocked(device);
+ releaseControllerNumberLocked(device.controllerNumber);
+ device.controllerNumber = 0;
+ device.close();
+ mClosingDevices.push_back(std::move(mDevices[device.id]));
- mDevices.removeItem(device->id);
- device->close();
-
- // Unlink for opening devices list if it is present.
- Device* pred = nullptr;
- bool found = false;
- for (Device* entry = mOpeningDevices; entry != nullptr;) {
- if (entry == device) {
- found = true;
- break;
- }
- pred = entry;
- entry = entry->next;
- }
- if (found) {
- // Unlink the device from the opening devices list then delete it.
- // We don't need to tell the client that the device was closed because
- // it does not even know it was opened in the first place.
- ALOGI("Device %s was immediately closed after opening.", device->path.c_str());
- if (pred) {
- pred->next = device->next;
- } else {
- mOpeningDevices = device->next;
- }
- delete device;
- } else {
- // Link into closing devices list.
- // The device will be deleted later after we have informed the client.
- device->next = mClosingDevices;
- mClosingDevices = device;
- }
+ mDevices.erase(device.id);
}
status_t EventHub::readNotifyLocked() {
@@ -1835,16 +2344,16 @@
event = (struct inotify_event*)(event_buf + event_pos);
if (event->len) {
if (event->wd == mInputWd) {
- std::string filename = StringPrintf("%s/%s", DEVICE_PATH, event->name);
+ std::string filename = std::string(DEVICE_PATH) + "/" + event->name;
if (event->mask & IN_CREATE) {
- openDeviceLocked(filename.c_str());
+ openDeviceLocked(filename);
} else {
ALOGI("Removing device '%s' due to inotify event\n", filename.c_str());
- closeDeviceByPathLocked(filename.c_str());
+ closeDeviceByPathLocked(filename);
}
} else if (event->wd == mVideoWd) {
if (isV4lTouchNode(event->name)) {
- std::string filename = StringPrintf("%s/%s", VIDEO_DEVICE_PATH, event->name);
+ std::string filename = std::string(VIDEO_DEVICE_PATH) + "/" + event->name;
if (event->mask & IN_CREATE) {
openVideoDeviceLocked(filename);
} else {
@@ -1863,24 +2372,10 @@
return 0;
}
-status_t EventHub::scanDirLocked(const char* dirname) {
- char devname[PATH_MAX];
- char* filename;
- DIR* dir;
- struct dirent* de;
- dir = opendir(dirname);
- if (dir == nullptr) return -1;
- strcpy(devname, dirname);
- filename = devname + strlen(devname);
- *filename++ = '/';
- while ((de = readdir(dir))) {
- if (de->d_name[0] == '.' &&
- (de->d_name[1] == '\0' || (de->d_name[1] == '.' && de->d_name[2] == '\0')))
- continue;
- strcpy(filename, de->d_name);
- openDeviceLocked(devname);
+status_t EventHub::scanDirLocked(const std::string& dirname) {
+ for (const auto& entry : std::filesystem::directory_iterator(dirname)) {
+ openDeviceLocked(entry.path());
}
- closedir(dir);
return 0;
}
@@ -1888,29 +2383,19 @@
* Look for all dirname/v4l-touch* devices, and open them.
*/
status_t EventHub::scanVideoDirLocked(const std::string& dirname) {
- DIR* dir;
- struct dirent* de;
- dir = opendir(dirname.c_str());
- if (!dir) {
- ALOGE("Could not open video directory %s", dirname.c_str());
- return BAD_VALUE;
- }
-
- while ((de = readdir(dir))) {
- const char* name = de->d_name;
- if (isV4lTouchNode(name)) {
- ALOGI("Found touch video device %s", name);
- openVideoDeviceLocked(dirname + "/" + name);
+ for (const auto& entry : std::filesystem::directory_iterator(dirname)) {
+ if (isV4lTouchNode(entry.path())) {
+ ALOGI("Found touch video device %s", entry.path().c_str());
+ openVideoDeviceLocked(entry.path());
}
}
- closedir(dir);
return OK;
}
void EventHub::requestReopenDevices() {
ALOGV("requestReopenDevices() called");
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
mNeedToReopenDevices = true;
}
@@ -1918,14 +2403,13 @@
dump += "Event Hub State:\n";
{ // acquire lock
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
dump += StringPrintf(INDENT "BuiltInKeyboardId: %d\n", mBuiltInKeyboardId);
dump += INDENT "Devices:\n";
- for (size_t i = 0; i < mDevices.size(); i++) {
- const Device* device = mDevices.valueAt(i);
+ for (const auto& [id, device] : mDevices) {
if (mBuiltInKeyboardId == device->id) {
dump += StringPrintf(INDENT2 "%d: %s (aka device 0 - built-in keyboard)\n",
device->id, device->identifier.name.c_str());
@@ -1933,7 +2417,7 @@
dump += StringPrintf(INDENT2 "%d: %s\n", device->id,
device->identifier.name.c_str());
}
- dump += StringPrintf(INDENT3 "Classes: 0x%08x\n", device->classes);
+ dump += StringPrintf(INDENT3 "Classes: %s\n", device->classes.string().c_str());
dump += StringPrintf(INDENT3 "Path: %s\n", device->path.c_str());
dump += StringPrintf(INDENT3 "Enabled: %s\n", toString(device->enabled));
dump += StringPrintf(INDENT3 "Descriptor: %s\n", device->identifier.descriptor.c_str());
@@ -1950,8 +2434,6 @@
device->keyMap.keyCharacterMapFile.c_str());
dump += StringPrintf(INDENT3 "ConfigurationFile: %s\n",
device->configurationFile.c_str());
- dump += StringPrintf(INDENT3 "HaveKeyboardLayoutOverlay: %s\n",
- toString(device->overlayKeyMap != nullptr));
dump += INDENT3 "VideoDevice: ";
if (device->videoDevice) {
dump += device->videoDevice->dump() + "\n";
@@ -1972,8 +2454,7 @@
void EventHub::monitor() {
// Acquire and release the lock to ensure that the event hub has not deadlocked.
- mLock.lock();
- mLock.unlock();
+ std::unique_lock<std::mutex> lock(mLock);
}
}; // namespace android
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 3347ba6..ad503fd 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -18,6 +18,7 @@
#include "InputDevice.h"
+#include <input/Flags.h>
#include <algorithm>
#include "CursorInputMapper.h"
@@ -26,7 +27,9 @@
#include "JoystickInputMapper.h"
#include "KeyboardInputMapper.h"
#include "MultiTouchInputMapper.h"
+#include "PeripheralController.h"
#include "RotaryEncoderInputMapper.h"
+#include "SensorInputMapper.h"
#include "SingleTouchInputMapper.h"
#include "SwitchInputMapper.h"
#include "VibratorInputMapper.h"
@@ -52,10 +55,11 @@
if (!hasEventHubDevices()) {
return false;
}
- // devices are either all enabled or all disabled, so we only need to check the first
- auto& devicePair = mDevices.begin()->second;
- auto& contextPtr = devicePair.first;
- return contextPtr->isDeviceEnabled();
+ // An input device composed of sub devices can be individually enabled or disabled.
+ // If any of the sub device is enabled then the input device is considered as enabled.
+ bool enabled = false;
+ for_each_subdevice([&enabled](auto& context) { enabled |= context.isDeviceEnabled(); });
+ return enabled;
}
void InputDevice::setEnabled(bool enabled, nsecs_t when) {
@@ -99,6 +103,12 @@
} else {
dump += "<none>\n";
}
+ dump += StringPrintf(INDENT2 "AssociatedDisplayUniqueId: ");
+ if (mAssociatedDisplayUniqueId) {
+ dump += StringPrintf("%s\n", mAssociatedDisplayUniqueId->c_str());
+ } else {
+ dump += "<none>\n";
+ }
dump += StringPrintf(INDENT2 "HasMic: %s\n", toString(mHasMic));
dump += StringPrintf(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
dump += StringPrintf(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
@@ -109,7 +119,7 @@
dump += INDENT2 "Motion Ranges:\n";
for (size_t i = 0; i < ranges.size(); i++) {
const InputDeviceInfo::MotionRange& range = ranges[i];
- const char* label = getAxisLabel(range.axis);
+ const char* label = InputEventLookup::getAxisLabel(range.axis);
char name[32];
if (label) {
strncpy(name, label, sizeof(name));
@@ -126,6 +136,9 @@
}
for_each_mapper([&dump](InputMapper& mapper) { mapper.dump(dump); });
+ if (mController) {
+ mController->dump(dump);
+ }
}
void InputDevice::addEventHubDevice(int32_t eventHubId, bool populateMappers) {
@@ -133,7 +146,7 @@
return;
}
std::unique_ptr<InputDeviceContext> contextPtr(new InputDeviceContext(*this, eventHubId));
- uint32_t classes = contextPtr->getDeviceClasses();
+ Flags<InputDeviceClass> classes = contextPtr->getDeviceClasses();
std::vector<std::unique_ptr<InputMapper>> mappers;
// Check if we should skip population
@@ -143,33 +156,39 @@
}
// Switch-like devices.
- if (classes & INPUT_DEVICE_CLASS_SWITCH) {
+ if (classes.test(InputDeviceClass::SWITCH)) {
mappers.push_back(std::make_unique<SwitchInputMapper>(*contextPtr));
}
// Scroll wheel-like devices.
- if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) {
+ if (classes.test(InputDeviceClass::ROTARY_ENCODER)) {
mappers.push_back(std::make_unique<RotaryEncoderInputMapper>(*contextPtr));
}
// Vibrator-like devices.
- if (classes & INPUT_DEVICE_CLASS_VIBRATOR) {
+ if (classes.test(InputDeviceClass::VIBRATOR)) {
mappers.push_back(std::make_unique<VibratorInputMapper>(*contextPtr));
}
+ // Battery-like devices or light-containing devices.
+ // PeripheralController will be created with associated EventHub device.
+ if (classes.test(InputDeviceClass::BATTERY) || classes.test(InputDeviceClass::LIGHT)) {
+ mController = std::make_unique<PeripheralController>(*contextPtr);
+ }
+
// Keyboard-like devices.
uint32_t keyboardSource = 0;
int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
- if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
+ if (classes.test(InputDeviceClass::KEYBOARD)) {
keyboardSource |= AINPUT_SOURCE_KEYBOARD;
}
- if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
+ if (classes.test(InputDeviceClass::ALPHAKEY)) {
keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
}
- if (classes & INPUT_DEVICE_CLASS_DPAD) {
+ if (classes.test(InputDeviceClass::DPAD)) {
keyboardSource |= AINPUT_SOURCE_DPAD;
}
- if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
+ if (classes.test(InputDeviceClass::GAMEPAD)) {
keyboardSource |= AINPUT_SOURCE_GAMEPAD;
}
@@ -179,24 +198,29 @@
}
// Cursor-like devices.
- if (classes & INPUT_DEVICE_CLASS_CURSOR) {
+ if (classes.test(InputDeviceClass::CURSOR)) {
mappers.push_back(std::make_unique<CursorInputMapper>(*contextPtr));
}
// Touchscreens and touchpad devices.
- if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
+ if (classes.test(InputDeviceClass::TOUCH_MT)) {
mappers.push_back(std::make_unique<MultiTouchInputMapper>(*contextPtr));
- } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
+ } else if (classes.test(InputDeviceClass::TOUCH)) {
mappers.push_back(std::make_unique<SingleTouchInputMapper>(*contextPtr));
}
// Joystick-like devices.
- if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {
+ if (classes.test(InputDeviceClass::JOYSTICK)) {
mappers.push_back(std::make_unique<JoystickInputMapper>(*contextPtr));
}
+ // Motion sensor enabled devices.
+ if (classes.test(InputDeviceClass::SENSOR)) {
+ mappers.push_back(std::make_unique<SensorInputMapper>(*contextPtr));
+ }
+
// External stylus-like devices.
- if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
+ if (classes.test(InputDeviceClass::EXTERNAL_STYLUS)) {
mappers.push_back(std::make_unique<ExternalStylusInputMapper>(*contextPtr));
}
@@ -213,7 +237,7 @@
void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config,
uint32_t changes) {
mSources = 0;
- mClasses = 0;
+ mClasses = Flags<InputDeviceClass>(0);
mControllerNumber = 0;
for_each_subdevice([this](InputDeviceContext& context) {
@@ -228,8 +252,8 @@
}
});
- mIsExternal = !!(mClasses & INPUT_DEVICE_CLASS_EXTERNAL);
- mHasMic = !!(mClasses & INPUT_DEVICE_CLASS_MIC);
+ mIsExternal = mClasses.test(InputDeviceClass::EXTERNAL);
+ mHasMic = mClasses.test(InputDeviceClass::MIC);
if (!isIgnored()) {
if (!changes) { // first time only
@@ -242,8 +266,8 @@
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) {
- if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) {
- sp<KeyCharacterMap> keyboardLayout =
+ if (!mClasses.test(InputDeviceClass::VIRTUAL)) {
+ std::shared_ptr<KeyCharacterMap> keyboardLayout =
mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier);
bool shouldBumpGeneration = false;
for_each_subdevice(
@@ -259,7 +283,7 @@
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_ALIAS)) {
- if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) {
+ if (!(mClasses.test(InputDeviceClass::VIRTUAL))) {
std::string alias = mContext->getPolicy()->getDeviceAlias(mIdentifier);
if (mAlias != alias) {
mAlias = alias;
@@ -275,8 +299,9 @@
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
- // In most situations, no port will be specified.
+ // In most situations, no port or name will be specified.
mAssociatedDisplayPort = std::nullopt;
+ mAssociatedDisplayUniqueId = std::nullopt;
mAssociatedViewport = std::nullopt;
// Find the display port that corresponds to the current input port.
const std::string& inputPort = mIdentifier.location;
@@ -287,6 +312,13 @@
mAssociatedDisplayPort = std::make_optional(displayPort->second);
}
}
+ const std::string& inputDeviceName = mIdentifier.name;
+ const std::unordered_map<std::string, std::string>& names =
+ config->uniqueIdAssociations;
+ const auto& displayUniqueId = names.find(inputDeviceName);
+ if (displayUniqueId != names.end()) {
+ mAssociatedDisplayUniqueId = displayUniqueId->second;
+ }
// If the device was explicitly disabled by the user, it would be present in the
// "disabledDevices" list. If it is associated with a specific display, and it was not
@@ -301,6 +333,15 @@
getName().c_str(), *mAssociatedDisplayPort);
enabled = false;
}
+ } else if (mAssociatedDisplayUniqueId != std::nullopt) {
+ mAssociatedViewport =
+ config->getDisplayViewportByUniqueId(*mAssociatedDisplayUniqueId);
+ if (!mAssociatedViewport) {
+ ALOGW("Input device %s should be associated with display %s but the "
+ "corresponding viewport cannot be found",
+ inputDeviceName.c_str(), mAssociatedDisplayUniqueId->c_str());
+ enabled = false;
+ }
}
if (changes) {
@@ -381,6 +422,10 @@
mHasMic);
for_each_mapper(
[outDeviceInfo](InputMapper& mapper) { mapper.populateDeviceInfo(outDeviceInfo); });
+
+ if (mController) {
+ mController->populateDeviceInfo(outDeviceInfo);
+ }
}
int32_t InputDevice::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
@@ -428,10 +473,9 @@
return result;
}
-void InputDevice::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
- int32_t token) {
- for_each_mapper([pattern, patternSize, repeat, token](InputMapper& mapper) {
- mapper.vibrate(pattern, patternSize, repeat, token);
+void InputDevice::vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token) {
+ for_each_mapper([sequence, repeat, token](InputMapper& mapper) {
+ mapper.vibrate(sequence, repeat, token);
});
}
@@ -439,8 +483,72 @@
for_each_mapper([token](InputMapper& mapper) { mapper.cancelVibrate(token); });
}
-void InputDevice::cancelTouch(nsecs_t when) {
- for_each_mapper([when](InputMapper& mapper) { mapper.cancelTouch(when); });
+bool InputDevice::isVibrating() {
+ bool vibrating = false;
+ for_each_mapper([&vibrating](InputMapper& mapper) { vibrating |= mapper.isVibrating(); });
+ return vibrating;
+}
+
+/* There's no guarantee the IDs provided by the different mappers are unique, so if we have two
+ * different vibration mappers then we could have duplicate IDs.
+ * Alternatively, if we have a merged device that has multiple evdev nodes with FF_* capabilities,
+ * we would definitely have duplicate IDs.
+ */
+std::vector<int32_t> InputDevice::getVibratorIds() {
+ std::vector<int32_t> vibrators;
+ for_each_mapper([&vibrators](InputMapper& mapper) {
+ std::vector<int32_t> devVibs = mapper.getVibratorIds();
+ vibrators.reserve(vibrators.size() + devVibs.size());
+ vibrators.insert(vibrators.end(), devVibs.begin(), devVibs.end());
+ });
+ return vibrators;
+}
+
+bool InputDevice::enableSensor(InputDeviceSensorType sensorType,
+ std::chrono::microseconds samplingPeriod,
+ std::chrono::microseconds maxBatchReportLatency) {
+ bool success = true;
+ for_each_mapper(
+ [&success, sensorType, samplingPeriod, maxBatchReportLatency](InputMapper& mapper) {
+ success &= mapper.enableSensor(sensorType, samplingPeriod, maxBatchReportLatency);
+ });
+ return success;
+}
+
+void InputDevice::disableSensor(InputDeviceSensorType sensorType) {
+ for_each_mapper([sensorType](InputMapper& mapper) { mapper.disableSensor(sensorType); });
+}
+
+void InputDevice::flushSensor(InputDeviceSensorType sensorType) {
+ for_each_mapper([sensorType](InputMapper& mapper) { mapper.flushSensor(sensorType); });
+}
+
+void InputDevice::cancelTouch(nsecs_t when, nsecs_t readTime) {
+ for_each_mapper([when, readTime](InputMapper& mapper) { mapper.cancelTouch(when, readTime); });
+}
+
+std::optional<int32_t> InputDevice::getBatteryCapacity() {
+ return mController ? mController->getBatteryCapacity(DEFAULT_BATTERY_ID) : std::nullopt;
+}
+
+std::optional<int32_t> InputDevice::getBatteryStatus() {
+ return mController ? mController->getBatteryStatus(DEFAULT_BATTERY_ID) : std::nullopt;
+}
+
+bool InputDevice::setLightColor(int32_t lightId, int32_t color) {
+ return mController ? mController->setLightColor(lightId, color) : false;
+}
+
+bool InputDevice::setLightPlayerId(int32_t lightId, int32_t playerId) {
+ return mController ? mController->setLightPlayerId(lightId, playerId) : false;
+}
+
+std::optional<int32_t> InputDevice::getLightColor(int32_t lightId) {
+ return mController ? mController->getLightColor(lightId) : std::nullopt;
+}
+
+std::optional<int32_t> InputDevice::getLightPlayerId(int32_t lightId) {
+ return mController ? mController->getLightPlayerId(lightId) : std::nullopt;
}
int32_t InputDevice::getMetaState() {
@@ -484,6 +592,10 @@
return count;
}
+void InputDevice::updateLedState(bool reset) {
+ for_each_mapper([reset](InputMapper& mapper) { mapper.updateLedState(reset); });
+}
+
InputDeviceContext::InputDeviceContext(InputDevice& device, int32_t eventHubId)
: mDevice(device),
mContext(device.getContext()),
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index dff830c..2d0fdf7 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -47,6 +47,7 @@
mEventHub(eventHub),
mPolicy(policy),
mGlobalMetaState(0),
+ mLedMetaState(AMETA_NUM_LOCK_ON),
mGeneration(1),
mNextInputDeviceId(END_RESERVED_ID),
mDisableVirtualKeysTimeout(LLONG_MIN),
@@ -55,7 +56,7 @@
mQueuedListener = new QueuedInputListener(listener);
{ // acquire lock
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
refreshConfigurationLocked(0);
updateGlobalMetaStateLocked();
@@ -88,7 +89,7 @@
bool inputDevicesChanged = false;
std::vector<InputDeviceInfo> inputDevices;
{ // acquire lock
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
oldGeneration = mGeneration;
timeoutMillis = -1;
@@ -107,8 +108,8 @@
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
{ // acquire lock
- AutoMutex _l(mLock);
- mReaderIsAliveCondition.broadcast();
+ std::scoped_lock _l(mLock);
+ mReaderIsAliveCondition.notify_all();
if (count) {
processEventsLocked(mEventBuffer, count);
@@ -127,7 +128,7 @@
if (oldGeneration != mGeneration) {
inputDevicesChanged = true;
- getInputDevicesLocked(inputDevices);
+ inputDevices = getInputDevicesLocked();
}
} // release lock
@@ -216,8 +217,15 @@
}
bumpGenerationLocked();
- if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
- notifyExternalStylusPresenceChanged();
+ if (device->getClasses().test(InputDeviceClass::EXTERNAL_STYLUS)) {
+ notifyExternalStylusPresenceChangedLocked();
+ }
+
+ // Sensor input device is noisy, to save power disable it by default.
+ // Input device is classified as SENSOR when any sub device is a SENSOR device, check Eventhub
+ // device class to disable SENSOR sub device only.
+ if (mEventHub->getDeviceClasses(eventHubId).test(InputDeviceClass::SENSOR)) {
+ mEventHub->disableDevice(eventHubId);
}
}
@@ -256,8 +264,8 @@
device->removeEventHubDevice(eventHubId);
- if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
- notifyExternalStylusPresenceChanged();
+ if (device->getClasses().test(InputDeviceClass::EXTERNAL_STYLUS)) {
+ notifyExternalStylusPresenceChangedLocked();
}
if (device->hasEventHubDevices()) {
@@ -302,7 +310,7 @@
device->process(rawEvents, count);
}
-InputDevice* InputReader::findInputDevice(int32_t deviceId) {
+InputDevice* InputReader::findInputDeviceLocked(int32_t deviceId) {
auto deviceIt =
std::find_if(mDevices.begin(), mDevices.end(), [deviceId](const auto& devicePair) {
return devicePair.second->getId() == deviceId;
@@ -339,24 +347,30 @@
mPolicy->getReaderConfiguration(&mConfig);
mEventHub->setExcludedDevices(mConfig.excludedDeviceNames);
- if (changes) {
- ALOGI("Reconfiguring input devices, changes=%s",
- InputReaderConfiguration::changesToString(changes).c_str());
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ if (!changes) return;
- if (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO) {
- updatePointerDisplayLocked();
- }
+ ALOGI("Reconfiguring input devices, changes=%s",
+ InputReaderConfiguration::changesToString(changes).c_str());
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) {
- mEventHub->requestReopenDevices();
- } else {
- for (auto& devicePair : mDevices) {
- std::shared_ptr<InputDevice>& device = devicePair.second;
- device->configure(now, &mConfig, changes);
- }
+ if (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO) {
+ updatePointerDisplayLocked();
+ }
+
+ if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) {
+ mEventHub->requestReopenDevices();
+ } else {
+ for (auto& devicePair : mDevices) {
+ std::shared_ptr<InputDevice>& device = devicePair.second;
+ device->configure(now, &mConfig, changes);
}
}
+
+ if (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE) {
+ const NotifyPointerCaptureChangedArgs args(mContext.getNextId(), now,
+ mConfig.pointerCapture);
+ mQueuedListener->notifyPointerCaptureChanged(&args);
+ }
}
void InputReader::updateGlobalMetaStateLocked() {
@@ -372,14 +386,26 @@
return mGlobalMetaState;
}
-void InputReader::notifyExternalStylusPresenceChanged() {
+void InputReader::updateLedMetaStateLocked(int32_t metaState) {
+ mLedMetaState = metaState;
+ for (auto& devicePair : mDevices) {
+ std::shared_ptr<InputDevice>& device = devicePair.second;
+ device->updateLedState(false);
+ }
+}
+
+int32_t InputReader::getLedMetaStateLocked() {
+ return mLedMetaState;
+}
+
+void InputReader::notifyExternalStylusPresenceChangedLocked() {
refreshConfigurationLocked(InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE);
}
void InputReader::getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices) {
for (auto& devicePair : mDevices) {
std::shared_ptr<InputDevice>& device = devicePair.second;
- if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS && !device->isIgnored()) {
+ if (device->getClasses().test(InputDeviceClass::EXTERNAL_STYLUS) && !device->isIgnored()) {
InputDeviceInfo info;
device->getDeviceInfo(&info);
outDevices.push_back(info);
@@ -388,6 +414,7 @@
}
void InputReader::dispatchExternalStylusState(const StylusState& state) {
+ std::scoped_lock _l(mLock);
for (auto& devicePair : mDevices) {
std::shared_ptr<InputDevice>& device = devicePair.second;
device->updateExternalStylusState(state);
@@ -461,13 +488,14 @@
return ++mGeneration;
}
-void InputReader::getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) {
- AutoMutex _l(mLock);
- getInputDevicesLocked(outInputDevices);
+std::vector<InputDeviceInfo> InputReader::getInputDevices() const {
+ std::scoped_lock _l(mLock);
+ return getInputDevicesLocked();
}
-void InputReader::getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDevices) {
- outInputDevices.clear();
+std::vector<InputDeviceInfo> InputReader::getInputDevicesLocked() const {
+ std::vector<InputDeviceInfo> outInputDevices;
+ outInputDevices.reserve(mDeviceToEventHubIdsMap.size());
for (const auto& [device, eventHubIds] : mDeviceToEventHubIdsMap) {
if (!device->isIgnored()) {
@@ -476,22 +504,23 @@
outInputDevices.push_back(info);
}
}
+ return outInputDevices;
}
int32_t InputReader::getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
return getStateLocked(deviceId, sourceMask, keyCode, &InputDevice::getKeyCodeState);
}
int32_t InputReader::getScanCodeState(int32_t deviceId, uint32_t sourceMask, int32_t scanCode) {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
return getStateLocked(deviceId, sourceMask, scanCode, &InputDevice::getScanCodeState);
}
int32_t InputReader::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t switchCode) {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
return getStateLocked(deviceId, sourceMask, switchCode, &InputDevice::getSwitchState);
}
@@ -500,7 +529,7 @@
GetStateFunc getStateFunc) {
int32_t result = AKEY_STATE_UNKNOWN;
if (deviceId >= 0) {
- InputDevice* device = findInputDevice(deviceId);
+ InputDevice* device = findInputDeviceLocked(deviceId);
if (device && !device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
result = (device->*getStateFunc)(sourceMask, code);
}
@@ -523,7 +552,8 @@
}
void InputReader::toggleCapsLockState(int32_t deviceId) {
- InputDevice* device = findInputDevice(deviceId);
+ std::scoped_lock _l(mLock);
+ InputDevice* device = findInputDeviceLocked(deviceId);
if (!device) {
ALOGW("Ignoring toggleCapsLock for unknown deviceId %" PRId32 ".", deviceId);
return;
@@ -538,7 +568,7 @@
bool InputReader::hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
const int32_t* keyCodes, uint8_t* outFlags) {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
memset(outFlags, 0, numCodes);
return markSupportedKeyCodesLocked(deviceId, sourceMask, numCodes, keyCodes, outFlags);
@@ -549,7 +579,7 @@
uint8_t* outFlags) {
bool result = false;
if (deviceId >= 0) {
- InputDevice* device = findInputDevice(deviceId);
+ InputDevice* device = findInputDeviceLocked(deviceId);
if (device && !device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
result = device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
}
@@ -565,7 +595,7 @@
}
void InputReader::requestRefreshConfiguration(uint32_t changes) {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
if (changes) {
bool needWake = !mConfigurationChangesToRefresh;
@@ -577,28 +607,163 @@
}
}
-void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
- ssize_t repeat, int32_t token) {
- AutoMutex _l(mLock);
- InputDevice* device = findInputDevice(deviceId);
+void InputReader::vibrate(int32_t deviceId, const VibrationSequence& sequence, ssize_t repeat,
+ int32_t token) {
+ std::scoped_lock _l(mLock);
+
+ InputDevice* device = findInputDeviceLocked(deviceId);
if (device) {
- device->vibrate(pattern, patternSize, repeat, token);
+ device->vibrate(sequence, repeat, token);
}
}
void InputReader::cancelVibrate(int32_t deviceId, int32_t token) {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
- InputDevice* device = findInputDevice(deviceId);
+ InputDevice* device = findInputDeviceLocked(deviceId);
if (device) {
device->cancelVibrate(token);
}
}
-bool InputReader::isInputDeviceEnabled(int32_t deviceId) {
- AutoMutex _l(mLock);
+bool InputReader::isVibrating(int32_t deviceId) {
+ std::scoped_lock _l(mLock);
- InputDevice* device = findInputDevice(deviceId);
+ InputDevice* device = findInputDeviceLocked(deviceId);
+ if (device) {
+ return device->isVibrating();
+ }
+ return false;
+}
+
+std::vector<int32_t> InputReader::getVibratorIds(int32_t deviceId) {
+ std::scoped_lock _l(mLock);
+
+ InputDevice* device = findInputDeviceLocked(deviceId);
+ if (device) {
+ return device->getVibratorIds();
+ }
+ return {};
+}
+
+void InputReader::disableSensor(int32_t deviceId, InputDeviceSensorType sensorType) {
+ std::scoped_lock _l(mLock);
+
+ InputDevice* device = findInputDeviceLocked(deviceId);
+ if (device) {
+ device->disableSensor(sensorType);
+ }
+}
+
+bool InputReader::enableSensor(int32_t deviceId, InputDeviceSensorType sensorType,
+ std::chrono::microseconds samplingPeriod,
+ std::chrono::microseconds maxBatchReportLatency) {
+ std::scoped_lock _l(mLock);
+
+ InputDevice* device = findInputDeviceLocked(deviceId);
+ if (device) {
+ return device->enableSensor(sensorType, samplingPeriod, maxBatchReportLatency);
+ }
+ return false;
+}
+
+void InputReader::flushSensor(int32_t deviceId, InputDeviceSensorType sensorType) {
+ std::scoped_lock _l(mLock);
+
+ InputDevice* device = findInputDeviceLocked(deviceId);
+ if (device) {
+ device->flushSensor(sensorType);
+ }
+}
+
+std::optional<int32_t> InputReader::getBatteryCapacity(int32_t deviceId) {
+ std::scoped_lock _l(mLock);
+
+ InputDevice* device = findInputDeviceLocked(deviceId);
+ if (device) {
+ return device->getBatteryCapacity();
+ }
+ return std::nullopt;
+}
+
+std::optional<int32_t> InputReader::getBatteryStatus(int32_t deviceId) {
+ std::scoped_lock _l(mLock);
+
+ InputDevice* device = findInputDeviceLocked(deviceId);
+ if (device) {
+ return device->getBatteryStatus();
+ }
+ return std::nullopt;
+}
+
+std::vector<int32_t> InputReader::getLightIds(int32_t deviceId) {
+ std::scoped_lock _l(mLock);
+
+ InputDevice* device = findInputDeviceLocked(deviceId);
+ if (device) {
+ InputDeviceInfo info;
+ device->getDeviceInfo(&info);
+ return info.getLightIds();
+ }
+ return {};
+}
+
+const InputDeviceLightInfo* InputReader::getLightInfo(int32_t deviceId, int32_t lightId) {
+ std::scoped_lock _l(mLock);
+
+ InputDevice* device = findInputDeviceLocked(deviceId);
+ if (device) {
+ InputDeviceInfo info;
+ device->getDeviceInfo(&info);
+ return info.getLightInfo(lightId);
+ }
+ return nullptr;
+}
+
+bool InputReader::setLightColor(int32_t deviceId, int32_t lightId, int32_t color) {
+ std::scoped_lock _l(mLock);
+
+ InputDevice* device = findInputDeviceLocked(deviceId);
+ if (device) {
+ return device->setLightColor(lightId, color);
+ }
+ return false;
+}
+
+bool InputReader::setLightPlayerId(int32_t deviceId, int32_t lightId, int32_t playerId) {
+ std::scoped_lock _l(mLock);
+
+ InputDevice* device = findInputDeviceLocked(deviceId);
+ if (device) {
+ return device->setLightPlayerId(lightId, playerId);
+ }
+ return false;
+}
+
+std::optional<int32_t> InputReader::getLightColor(int32_t deviceId, int32_t lightId) {
+ std::scoped_lock _l(mLock);
+
+ InputDevice* device = findInputDeviceLocked(deviceId);
+ if (device) {
+ return device->getLightColor(lightId);
+ }
+ return std::nullopt;
+}
+
+std::optional<int32_t> InputReader::getLightPlayerId(int32_t deviceId, int32_t lightId) {
+ std::scoped_lock _l(mLock);
+
+ InputDevice* device = findInputDeviceLocked(deviceId);
+ if (device) {
+ return device->getLightPlayerId(lightId);
+ }
+ return std::nullopt;
+}
+
+bool InputReader::isInputDeviceEnabled(int32_t deviceId) {
+ std::scoped_lock _l(mLock);
+
+ InputDevice* device = findInputDeviceLocked(deviceId);
if (device) {
return device->isEnabled();
}
@@ -607,9 +772,9 @@
}
bool InputReader::canDispatchToDisplay(int32_t deviceId, int32_t displayId) {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
- InputDevice* device = findInputDevice(deviceId);
+ InputDevice* device = findInputDeviceLocked(deviceId);
if (!device) {
ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId);
return false;
@@ -635,7 +800,7 @@
}
void InputReader::dump(std::string& dump) {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
mEventHub->dump(dump);
dump += "\n";
@@ -710,11 +875,9 @@
void InputReader::monitor() {
// Acquire and release the lock to ensure that the reader has not deadlocked.
- mLock.lock();
+ std::unique_lock<std::mutex> lock(mLock);
mEventHub->wake();
- mReaderIsAliveCondition.wait(mLock);
- mLock.unlock();
-
+ mReaderIsAliveCondition.wait(lock);
// Check the EventHub
mEventHub->monitor();
}
@@ -734,6 +897,16 @@
return mReader->getGlobalMetaStateLocked();
}
+void InputReader::ContextImpl::updateLedMetaState(int32_t metaState) {
+ // lock is already held by the input loop
+ mReader->updateLedMetaStateLocked(metaState);
+}
+
+int32_t InputReader::ContextImpl::getLedMetaState() {
+ // lock is already held by the input loop
+ return mReader->getLedMetaStateLocked();
+}
+
void InputReader::ContextImpl::disableVirtualKeysUntil(nsecs_t time) {
// lock is already held by the input loop
mReader->disableVirtualKeysUntilLocked(time);
diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp
new file mode 100644
index 0000000..1a40d06
--- /dev/null
+++ b/services/inputflinger/reader/controller/PeripheralController.cpp
@@ -0,0 +1,528 @@
+/*
+ * Copyright (C) 2021 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 <locale>
+#include <regex>
+
+#include "../Macros.h"
+
+#include "PeripheralController.h"
+#include "input/NamedEnum.h"
+
+// Log detailed debug messages about input device lights.
+static constexpr bool DEBUG_LIGHT_DETAILS = false;
+
+namespace android {
+
+static inline int32_t getAlpha(int32_t color) {
+ return (color >> 24) & 0xff;
+}
+
+static inline int32_t getRed(int32_t color) {
+ return (color >> 16) & 0xff;
+}
+
+static inline int32_t getGreen(int32_t color) {
+ return (color >> 8) & 0xff;
+}
+
+static inline int32_t getBlue(int32_t color) {
+ return color & 0xff;
+}
+
+static inline int32_t toArgb(int32_t brightness, int32_t red, int32_t green, int32_t blue) {
+ return (brightness & 0xff) << 24 | (red & 0xff) << 16 | (green & 0xff) << 8 | (blue & 0xff);
+}
+
+/**
+ * Input controller owned by InputReader device, implements the native API for querying input
+ * lights, getting and setting the lights brightness and color, by interacting with EventHub
+ * devices.
+ */
+PeripheralController::PeripheralController(InputDeviceContext& deviceContext)
+ : mDeviceContext(deviceContext) {
+ configureBattries();
+ configureLights();
+}
+
+PeripheralController::~PeripheralController() {}
+
+std::optional<std::int32_t> PeripheralController::Light::getRawLightBrightness(int32_t rawLightId) {
+ std::optional<RawLightInfo> rawInfoOpt = context.getRawLightInfo(rawLightId);
+ if (!rawInfoOpt.has_value()) {
+ return std::nullopt;
+ }
+ std::optional<int32_t> brightnessOpt = context.getLightBrightness(rawLightId);
+ if (!brightnessOpt.has_value()) {
+ return std::nullopt;
+ }
+ int brightness = brightnessOpt.value();
+
+ // If the light node doesn't have max brightness, use the default max brightness.
+ int rawMaxBrightness = rawInfoOpt->maxBrightness.value_or(MAX_BRIGHTNESS);
+ float ratio = MAX_BRIGHTNESS / rawMaxBrightness;
+ // Scale the returned brightness in [0, rawMaxBrightness] to [0, 255]
+ if (rawMaxBrightness != MAX_BRIGHTNESS) {
+ brightness = brightness * ratio;
+ }
+ if (DEBUG_LIGHT_DETAILS) {
+ ALOGD("getRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f", rawLightId,
+ brightness, ratio);
+ }
+ return brightness;
+}
+
+void PeripheralController::Light::setRawLightBrightness(int32_t rawLightId, int32_t brightness) {
+ std::optional<RawLightInfo> rawInfo = context.getRawLightInfo(rawLightId);
+ if (!rawInfo.has_value()) {
+ return;
+ }
+ // If the light node doesn't have max brightness, use the default max brightness.
+ int rawMaxBrightness = rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS);
+ float ratio = MAX_BRIGHTNESS / rawMaxBrightness;
+ // Scale the requested brightness in [0, 255] to [0, rawMaxBrightness]
+ if (rawMaxBrightness != MAX_BRIGHTNESS) {
+ brightness = ceil(brightness / ratio);
+ }
+ if (DEBUG_LIGHT_DETAILS) {
+ ALOGD("setRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f", rawLightId,
+ brightness, ratio);
+ }
+ context.setLightBrightness(rawLightId, brightness);
+}
+
+bool PeripheralController::SingleLight::setLightColor(int32_t color) {
+ int32_t brightness = getAlpha(color);
+ setRawLightBrightness(rawId, brightness);
+
+ return true;
+}
+
+bool PeripheralController::RgbLight::setLightColor(int32_t color) {
+ // Compose color value as per:
+ // https://developer.android.com/reference/android/graphics/Color?hl=en
+ // int color = (A & 0xff) << 24 | (R & 0xff) << 16 | (G & 0xff) << 8 | (B & 0xff);
+ // The alpha component is used to scale the R,G,B leds brightness, with the ratio to
+ // MAX_BRIGHTNESS.
+ brightness = getAlpha(color);
+ int32_t red = 0;
+ int32_t green = 0;
+ int32_t blue = 0;
+ if (brightness > 0) {
+ float ratio = MAX_BRIGHTNESS / brightness;
+ red = ceil(getRed(color) / ratio);
+ green = ceil(getGreen(color) / ratio);
+ blue = ceil(getBlue(color) / ratio);
+ }
+ setRawLightBrightness(rawRgbIds.at(LightColor::RED), red);
+ setRawLightBrightness(rawRgbIds.at(LightColor::GREEN), green);
+ setRawLightBrightness(rawRgbIds.at(LightColor::BLUE), blue);
+ if (rawGlobalId.has_value()) {
+ setRawLightBrightness(rawGlobalId.value(), brightness);
+ }
+
+ return true;
+}
+
+bool PeripheralController::MultiColorLight::setLightColor(int32_t color) {
+ std::unordered_map<LightColor, int32_t> intensities;
+ intensities.emplace(LightColor::RED, getRed(color));
+ intensities.emplace(LightColor::GREEN, getGreen(color));
+ intensities.emplace(LightColor::BLUE, getBlue(color));
+
+ context.setLightIntensities(rawId, intensities);
+ setRawLightBrightness(rawId, getAlpha(color));
+ return true;
+}
+
+std::optional<int32_t> PeripheralController::SingleLight::getLightColor() {
+ std::optional<int32_t> brightness = getRawLightBrightness(rawId);
+ if (!brightness.has_value()) {
+ return std::nullopt;
+ }
+
+ return toArgb(brightness.value(), 0 /* red */, 0 /* green */, 0 /* blue */);
+}
+
+std::optional<int32_t> PeripheralController::RgbLight::getLightColor() {
+ // If the Alpha component is zero, then return color 0.
+ if (brightness == 0) {
+ return 0;
+ }
+ // Compose color value as per:
+ // https://developer.android.com/reference/android/graphics/Color?hl=en
+ // int color = (A & 0xff) << 24 | (R & 0xff) << 16 | (G & 0xff) << 8 | (B & 0xff);
+ std::optional<int32_t> redOr = getRawLightBrightness(rawRgbIds.at(LightColor::RED));
+ std::optional<int32_t> greenOr = getRawLightBrightness(rawRgbIds.at(LightColor::GREEN));
+ std::optional<int32_t> blueOr = getRawLightBrightness(rawRgbIds.at(LightColor::BLUE));
+ // If we can't get brightness for any of the RGB light
+ if (!redOr.has_value() || !greenOr.has_value() || !blueOr.has_value()) {
+ return std::nullopt;
+ }
+
+ // Compose the ARGB format color. As the R,G,B color led brightness is scaled by Alpha
+ // value, scale it back to return the nominal color value.
+ float ratio = MAX_BRIGHTNESS / brightness;
+ int32_t red = round(redOr.value() * ratio);
+ int32_t green = round(greenOr.value() * ratio);
+ int32_t blue = round(blueOr.value() * ratio);
+
+ if (red > MAX_BRIGHTNESS || green > MAX_BRIGHTNESS || blue > MAX_BRIGHTNESS) {
+ // Previously stored brightness isn't valid for current LED values, so just reset to max
+ // brightness since an app couldn't have provided these values in the first place.
+ red = redOr.value();
+ green = greenOr.value();
+ blue = blueOr.value();
+ brightness = MAX_BRIGHTNESS;
+ }
+
+ return toArgb(brightness, red, green, blue);
+}
+
+std::optional<int32_t> PeripheralController::MultiColorLight::getLightColor() {
+ auto ret = context.getLightIntensities(rawId);
+ if (!ret.has_value()) {
+ return std::nullopt;
+ }
+ std::unordered_map<LightColor, int32_t> intensities = ret.value();
+ // Get red, green, blue colors
+ int32_t color = toArgb(0 /* brightness */, intensities.at(LightColor::RED) /* red */,
+ intensities.at(LightColor::GREEN) /* green */,
+ intensities.at(LightColor::BLUE) /* blue */);
+ // Get brightness
+ std::optional<int32_t> brightness = getRawLightBrightness(rawId);
+ if (brightness.has_value()) {
+ return toArgb(brightness.value() /* A */, 0, 0, 0) | color;
+ }
+ return std::nullopt;
+}
+
+bool PeripheralController::PlayerIdLight::setLightPlayerId(int32_t playerId) {
+ if (rawLightIds.find(playerId) == rawLightIds.end()) {
+ return false;
+ }
+ for (const auto& [id, rawId] : rawLightIds) {
+ if (playerId == id) {
+ setRawLightBrightness(rawId, MAX_BRIGHTNESS);
+ } else {
+ setRawLightBrightness(rawId, 0);
+ }
+ }
+ return true;
+}
+
+std::optional<int32_t> PeripheralController::PlayerIdLight::getLightPlayerId() {
+ for (const auto& [id, rawId] : rawLightIds) {
+ std::optional<int32_t> brightness = getRawLightBrightness(rawId);
+ if (brightness.has_value() && brightness.value() > 0) {
+ return id;
+ }
+ }
+ return std::nullopt;
+}
+
+void PeripheralController::SingleLight::dump(std::string& dump) {
+ dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0));
+}
+
+void PeripheralController::PlayerIdLight::dump(std::string& dump) {
+ dump += StringPrintf(INDENT4 "PlayerId: %d\n", getLightPlayerId().value_or(-1));
+ dump += StringPrintf(INDENT4 "Raw Player ID LEDs:");
+ for (const auto& [id, rawId] : rawLightIds) {
+ dump += StringPrintf("id %d -> %d ", id, rawId);
+ }
+ dump += "\n";
+}
+
+void PeripheralController::RgbLight::dump(std::string& dump) {
+ dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0));
+ dump += StringPrintf(INDENT4 "Raw RGB LEDs: [%d, %d, %d] ", rawRgbIds.at(LightColor::RED),
+ rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE));
+ if (rawGlobalId.has_value()) {
+ dump += StringPrintf(INDENT4 "Raw Global LED: [%d] ", rawGlobalId.value());
+ }
+ dump += "\n";
+}
+
+void PeripheralController::MultiColorLight::dump(std::string& dump) {
+ dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0));
+}
+
+void PeripheralController::populateDeviceInfo(InputDeviceInfo* deviceInfo) {
+ // TODO: b/180733860 Remove this after enabling multi-battery
+ if (!mBatteries.empty()) {
+ deviceInfo->setHasBattery(true);
+ }
+
+ for (const auto& [batteryId, battery] : mBatteries) {
+ InputDeviceBatteryInfo batteryInfo(battery->name, battery->id);
+ deviceInfo->addBatteryInfo(batteryInfo);
+ }
+
+ for (const auto& [lightId, light] : mLights) {
+ // Input device light doesn't support ordinal, always pass 1.
+ InputDeviceLightInfo lightInfo(light->name, light->id, light->type, 1 /* ordinal */);
+ deviceInfo->addLightInfo(lightInfo);
+ }
+}
+
+void PeripheralController::dump(std::string& dump) {
+ dump += INDENT2 "Input Controller:\n";
+ if (!mLights.empty()) {
+ dump += INDENT3 "Lights:\n";
+ for (const auto& [lightId, light] : mLights) {
+ dump += StringPrintf(INDENT4 "Id: %d", lightId);
+ dump += StringPrintf(INDENT4 "Name: %s", light->name.c_str());
+ dump += StringPrintf(INDENT4 "Type: %s", NamedEnum::string(light->type).c_str());
+ light->dump(dump);
+ }
+ }
+ // Dump raw lights
+ dump += INDENT3 "RawLights:\n";
+ dump += INDENT4 "Id:\t Name:\t Flags:\t Max brightness:\t Brightness\n";
+ const std::vector<int32_t> rawLightIds = getDeviceContext().getRawLightIds();
+ // Map from raw light id to raw light info
+ std::unordered_map<int32_t, RawLightInfo> rawInfos;
+ for (const auto& rawId : rawLightIds) {
+ std::optional<RawLightInfo> rawInfo = getDeviceContext().getRawLightInfo(rawId);
+ if (!rawInfo.has_value()) {
+ continue;
+ }
+ dump += StringPrintf(INDENT4 "%d", rawId);
+ dump += StringPrintf(INDENT4 "%s", rawInfo->name.c_str());
+ dump += StringPrintf(INDENT4 "%s", rawInfo->flags.string().c_str());
+ dump += StringPrintf(INDENT4 "%d", rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS));
+ dump += StringPrintf(INDENT4 "%d\n",
+ getDeviceContext().getLightBrightness(rawId).value_or(-1));
+ }
+
+ if (!mBatteries.empty()) {
+ dump += INDENT3 "Batteries:\n";
+ for (const auto& [batteryId, battery] : mBatteries) {
+ dump += StringPrintf(INDENT4 "Id: %d", batteryId);
+ dump += StringPrintf(INDENT4 "Name: %s", battery->name.c_str());
+ dump += getBatteryCapacity(batteryId).has_value()
+ ? StringPrintf(INDENT3 "Capacity: %d\n", getBatteryCapacity(batteryId).value())
+ : StringPrintf(INDENT3 "Capacity: Unknown");
+
+ std::string status;
+ switch (getBatteryStatus(batteryId).value_or(BATTERY_STATUS_UNKNOWN)) {
+ case BATTERY_STATUS_CHARGING:
+ status = "Charging";
+ break;
+ case BATTERY_STATUS_DISCHARGING:
+ status = "Discharging";
+ break;
+ case BATTERY_STATUS_NOT_CHARGING:
+ status = "Not charging";
+ break;
+ case BATTERY_STATUS_FULL:
+ status = "Full";
+ break;
+ default:
+ status = "Unknown";
+ }
+ dump += StringPrintf(INDENT3 "Status: %s\n", status.c_str());
+ }
+ }
+}
+
+void PeripheralController::configureBattries() {
+ // Check raw batteries
+ const std::vector<int32_t> rawBatteryIds = getDeviceContext().getRawBatteryIds();
+
+ for (const auto& rawId : rawBatteryIds) {
+ std::optional<RawBatteryInfo> rawInfo = getDeviceContext().getRawBatteryInfo(rawId);
+ if (!rawInfo.has_value()) {
+ continue;
+ }
+ std::unique_ptr<Battery> battery =
+ std::make_unique<Battery>(getDeviceContext(), rawInfo->name, rawInfo->id);
+ mBatteries.insert_or_assign(rawId, std::move(battery));
+ }
+}
+
+void PeripheralController::configureLights() {
+ bool hasRedLed = false;
+ bool hasGreenLed = false;
+ bool hasBlueLed = false;
+ std::optional<int32_t> rawGlobalId = std::nullopt;
+ // Player ID light common name string
+ std::string playerIdName;
+ // Raw RGB color to raw light ID
+ std::unordered_map<LightColor, int32_t /* rawLightId */> rawRgbIds;
+ // Map from player Id to raw light Id
+ std::unordered_map<int32_t, int32_t> playerIdLightIds;
+
+ // Check raw lights
+ const std::vector<int32_t> rawLightIds = getDeviceContext().getRawLightIds();
+ // Map from raw light id to raw light info
+ std::unordered_map<int32_t, RawLightInfo> rawInfos;
+ for (const auto& rawId : rawLightIds) {
+ std::optional<RawLightInfo> rawInfo = getDeviceContext().getRawLightInfo(rawId);
+ if (!rawInfo.has_value()) {
+ continue;
+ }
+ rawInfos.insert_or_assign(rawId, rawInfo.value());
+ // Check if this is a group LEDs for player ID
+ std::regex lightPattern("([a-z]+)([0-9]+)");
+ std::smatch results;
+ if (std::regex_match(rawInfo->name, results, lightPattern)) {
+ std::string commonName = results[1].str();
+ int32_t playerId = std::stoi(results[2]);
+ if (playerIdLightIds.empty()) {
+ playerIdName = commonName;
+ playerIdLightIds.insert_or_assign(playerId, rawId);
+ } else {
+ // Make sure the player ID leds have common string name
+ if (playerIdName.compare(commonName) == 0 &&
+ playerIdLightIds.find(playerId) == playerIdLightIds.end()) {
+ playerIdLightIds.insert_or_assign(playerId, rawId);
+ }
+ }
+ }
+ // Check if this is an LED of RGB light
+ if (rawInfo->flags.test(InputLightClass::RED)) {
+ hasRedLed = true;
+ rawRgbIds.emplace(LightColor::RED, rawId);
+ }
+ if (rawInfo->flags.test(InputLightClass::GREEN)) {
+ hasGreenLed = true;
+ rawRgbIds.emplace(LightColor::GREEN, rawId);
+ }
+ if (rawInfo->flags.test(InputLightClass::BLUE)) {
+ hasBlueLed = true;
+ rawRgbIds.emplace(LightColor::BLUE, rawId);
+ }
+ if (rawInfo->flags.test(InputLightClass::GLOBAL)) {
+ rawGlobalId = rawId;
+ }
+ if (DEBUG_LIGHT_DETAILS) {
+ ALOGD("Light rawId %d name %s max %d flags %s \n", rawInfo->id, rawInfo->name.c_str(),
+ rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS), rawInfo->flags.string().c_str());
+ }
+ }
+
+ // Construct a player ID light
+ if (playerIdLightIds.size() > 1) {
+ std::unique_ptr<Light> light =
+ std::make_unique<PlayerIdLight>(getDeviceContext(), playerIdName, ++mNextId,
+ playerIdLightIds);
+ mLights.insert_or_assign(light->id, std::move(light));
+ // Remove these raw lights from raw light info as they've been used to compose a
+ // Player ID light, so we do not expose these raw lights as single lights.
+ for (const auto& [playerId, rawId] : playerIdLightIds) {
+ rawInfos.erase(rawId);
+ }
+ }
+ // Construct a RGB light for composed RGB light
+ if (hasRedLed && hasGreenLed && hasBlueLed) {
+ if (DEBUG_LIGHT_DETAILS) {
+ ALOGD("Rgb light ids [%d, %d, %d] \n", rawRgbIds.at(LightColor::RED),
+ rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE));
+ }
+ std::unique_ptr<Light> light =
+ std::make_unique<RgbLight>(getDeviceContext(), ++mNextId, rawRgbIds, rawGlobalId);
+ mLights.insert_or_assign(light->id, std::move(light));
+ // Remove from raw light info as they've been composed a RBG light.
+ rawInfos.erase(rawRgbIds.at(LightColor::RED));
+ rawInfos.erase(rawRgbIds.at(LightColor::GREEN));
+ rawInfos.erase(rawRgbIds.at(LightColor::BLUE));
+ if (rawGlobalId.has_value()) {
+ rawInfos.erase(rawGlobalId.value());
+ }
+ }
+
+ // Check the rest of raw light infos
+ for (const auto& [rawId, rawInfo] : rawInfos) {
+ // If the node is multi-color led, construct a MULTI_COLOR light
+ if (rawInfo.flags.test(InputLightClass::MULTI_INDEX) &&
+ rawInfo.flags.test(InputLightClass::MULTI_INTENSITY)) {
+ if (DEBUG_LIGHT_DETAILS) {
+ ALOGD("Multicolor light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str());
+ }
+ std::unique_ptr<Light> light =
+ std::make_unique<MultiColorLight>(getDeviceContext(), rawInfo.name, ++mNextId,
+ rawInfo.id);
+ mLights.insert_or_assign(light->id, std::move(light));
+ continue;
+ }
+ // Construct a single LED light
+ if (DEBUG_LIGHT_DETAILS) {
+ ALOGD("Single light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str());
+ }
+ std::unique_ptr<Light> light =
+ std::make_unique<SingleLight>(getDeviceContext(), rawInfo.name, ++mNextId,
+ rawInfo.id);
+
+ mLights.insert_or_assign(light->id, std::move(light));
+ }
+}
+
+std::optional<int32_t> PeripheralController::getBatteryCapacity(int batteryId) {
+ return getDeviceContext().getBatteryCapacity(batteryId);
+}
+
+std::optional<int32_t> PeripheralController::getBatteryStatus(int batteryId) {
+ return getDeviceContext().getBatteryStatus(batteryId);
+}
+
+bool PeripheralController::setLightColor(int32_t lightId, int32_t color) {
+ auto it = mLights.find(lightId);
+ if (it == mLights.end()) {
+ return false;
+ }
+ auto& light = it->second;
+ if (DEBUG_LIGHT_DETAILS) {
+ ALOGD("setLightColor lightId %d type %s color 0x%x", lightId,
+ NamedEnum::string(light->type).c_str(), color);
+ }
+ return light->setLightColor(color);
+}
+
+std::optional<int32_t> PeripheralController::getLightColor(int32_t lightId) {
+ auto it = mLights.find(lightId);
+ if (it == mLights.end()) {
+ return std::nullopt;
+ }
+ auto& light = it->second;
+ std::optional<int32_t> color = light->getLightColor();
+ if (DEBUG_LIGHT_DETAILS) {
+ ALOGD("getLightColor lightId %d type %s color 0x%x", lightId,
+ NamedEnum::string(light->type).c_str(), color.value_or(0));
+ }
+ return color;
+}
+
+bool PeripheralController::setLightPlayerId(int32_t lightId, int32_t playerId) {
+ auto it = mLights.find(lightId);
+ if (it == mLights.end()) {
+ return false;
+ }
+ auto& light = it->second;
+ return light->setLightPlayerId(playerId);
+}
+
+std::optional<int32_t> PeripheralController::getLightPlayerId(int32_t lightId) {
+ auto it = mLights.find(lightId);
+ if (it == mLights.end()) {
+ return std::nullopt;
+ }
+ auto& light = it->second;
+ return light->getLightPlayerId();
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/controller/PeripheralController.h b/services/inputflinger/reader/controller/PeripheralController.h
new file mode 100644
index 0000000..ff3607f
--- /dev/null
+++ b/services/inputflinger/reader/controller/PeripheralController.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#ifndef _UI_INPUTREADER_LIGHT_CONTROLLER_H
+#define _UI_INPUTREADER_LIGHT_CONTROLLER_H
+
+#include "PeripheralControllerInterface.h"
+
+namespace android {
+
+class PeripheralController : public PeripheralControllerInterface {
+ // Refer to https://developer.android.com/reference/kotlin/android/graphics/Color
+ /* Number of colors : {red, green, blue} */
+ static constexpr size_t COLOR_NUM = 3;
+ static constexpr int32_t MAX_BRIGHTNESS = 0xff;
+
+public:
+ explicit PeripheralController(InputDeviceContext& deviceContext);
+ ~PeripheralController() override;
+
+ void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+ void dump(std::string& dump) override;
+ bool setLightColor(int32_t lightId, int32_t color) override;
+ bool setLightPlayerId(int32_t lightId, int32_t playerId) override;
+ std::optional<int32_t> getLightColor(int32_t lightId) override;
+ std::optional<int32_t> getLightPlayerId(int32_t lightId) override;
+ std::optional<int32_t> getBatteryCapacity(int32_t batteryId) override;
+ std::optional<int32_t> getBatteryStatus(int32_t batteryId) override;
+
+private:
+ inline int32_t getDeviceId() { return mDeviceContext.getId(); }
+ inline InputDeviceContext& getDeviceContext() { return mDeviceContext; }
+
+ InputDeviceContext& mDeviceContext;
+ void configureLights();
+ void configureBattries();
+
+ struct Battery {
+ explicit Battery(InputDeviceContext& context, const std::string& name, int32_t id)
+ : context(context), name(name), id(id) {}
+ virtual ~Battery() {}
+ InputDeviceContext& context;
+ std::string name;
+ int32_t id;
+ };
+
+ struct Light {
+ explicit Light(InputDeviceContext& context, const std::string& name, int32_t id,
+ InputDeviceLightType type)
+ : context(context), name(name), id(id), type(type) {}
+ virtual ~Light() {}
+ InputDeviceContext& context;
+ std::string name;
+ int32_t id;
+ InputDeviceLightType type;
+
+ virtual bool setLightColor(int32_t color) { return false; }
+ virtual std::optional<int32_t> getLightColor() { return std::nullopt; }
+ virtual bool setLightPlayerId(int32_t playerId) { return false; }
+ virtual std::optional<int32_t> getLightPlayerId() { return std::nullopt; }
+
+ virtual void dump(std::string& dump) {}
+
+ std::optional<std::int32_t> getRawLightBrightness(int32_t rawLightId);
+ void setRawLightBrightness(int32_t rawLightId, int32_t brightness);
+ };
+
+ struct SingleLight : public Light {
+ explicit SingleLight(InputDeviceContext& context, const std::string& name, int32_t id,
+ int32_t rawId)
+ : Light(context, name, id, InputDeviceLightType::SINGLE), rawId(rawId) {}
+ int32_t rawId;
+
+ bool setLightColor(int32_t color) override;
+ std::optional<int32_t> getLightColor() override;
+ void dump(std::string& dump) override;
+ };
+
+ struct RgbLight : public Light {
+ explicit RgbLight(InputDeviceContext& context, int32_t id,
+ const std::unordered_map<LightColor, int32_t>& rawRgbIds,
+ std::optional<int32_t> rawGlobalId)
+ : Light(context, "RGB", id, InputDeviceLightType::RGB),
+ rawRgbIds(rawRgbIds),
+ rawGlobalId(rawGlobalId) {
+ brightness = rawGlobalId.has_value()
+ ? getRawLightBrightness(rawGlobalId.value()).value_or(MAX_BRIGHTNESS)
+ : MAX_BRIGHTNESS;
+ }
+ // Map from color to raw light id.
+ std::unordered_map<LightColor, int32_t /* rawLightId */> rawRgbIds;
+ // Optional global control raw light id.
+ std::optional<int32_t> rawGlobalId;
+ int32_t brightness;
+
+ bool setLightColor(int32_t color) override;
+ std::optional<int32_t> getLightColor() override;
+ void dump(std::string& dump) override;
+ };
+
+ struct MultiColorLight : public Light {
+ explicit MultiColorLight(InputDeviceContext& context, const std::string& name, int32_t id,
+ int32_t rawId)
+ : Light(context, name, id, InputDeviceLightType::MULTI_COLOR), rawId(rawId) {}
+ int32_t rawId;
+
+ bool setLightColor(int32_t color) override;
+ std::optional<int32_t> getLightColor() override;
+ void dump(std::string& dump) override;
+ };
+
+ struct PlayerIdLight : public Light {
+ explicit PlayerIdLight(InputDeviceContext& context, const std::string& name, int32_t id,
+ const std::unordered_map<int32_t, int32_t>& rawLightIds)
+ : Light(context, name, id, InputDeviceLightType::PLAYER_ID),
+ rawLightIds(rawLightIds) {}
+ // Map from player Id to raw light Id
+ std::unordered_map<int32_t, int32_t> rawLightIds;
+
+ bool setLightPlayerId(int32_t palyerId) override;
+ std::optional<int32_t> getLightPlayerId() override;
+ void dump(std::string& dump) override;
+ };
+
+ int32_t mNextId = 0;
+
+ // Light color map from light color to the color index.
+ static const std::unordered_map<std::string, size_t> LIGHT_COLORS;
+
+ // Light map from light ID to Light
+ std::unordered_map<int32_t, std::unique_ptr<Light>> mLights;
+
+ // Battery map from battery ID to battery
+ std::unordered_map<int32_t, std::unique_ptr<Battery>> mBatteries;
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_LIGHT_CONTROLLER_H
diff --git a/services/inputflinger/reader/controller/PeripheralControllerInterface.h b/services/inputflinger/reader/controller/PeripheralControllerInterface.h
new file mode 100644
index 0000000..7688a43
--- /dev/null
+++ b/services/inputflinger/reader/controller/PeripheralControllerInterface.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#ifndef _UI_INPUTREADER_INPUT_CONTROLLER_H
+#define _UI_INPUTREADER_INPUT_CONTROLLER_H
+
+#include "EventHub.h"
+#include "InputDevice.h"
+#include "InputListener.h"
+#include "InputReaderContext.h"
+
+namespace android {
+
+/* A peripheral controller manages the input device peripherals associated with the input device,
+ * like the sysfs based battery and light class devices.
+ *
+ */
+class PeripheralControllerInterface {
+public:
+ PeripheralControllerInterface() {}
+ virtual ~PeripheralControllerInterface() {}
+
+ // Interface methods for Battery
+ virtual std::optional<int32_t> getBatteryCapacity(int32_t batteryId) = 0;
+ virtual std::optional<int32_t> getBatteryStatus(int32_t batteryId) = 0;
+
+ // Interface methods for Light
+ virtual bool setLightColor(int32_t lightId, int32_t color) = 0;
+ virtual bool setLightPlayerId(int32_t lightId, int32_t playerId) = 0;
+ virtual std::optional<int32_t> getLightColor(int32_t lightId) = 0;
+ virtual std::optional<int32_t> getLightPlayerId(int32_t lightId) = 0;
+
+ virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) = 0;
+ virtual void dump(std::string& dump) = 0;
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_INPUT_CONTROLLER_H
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index f5451d7..410a706 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -17,8 +17,15 @@
#ifndef _RUNTIME_EVENT_HUB_H
#define _RUNTIME_EVENT_HUB_H
+#include <bitset>
+#include <climits>
+#include <unordered_map>
#include <vector>
+#include <input/Flags.h>
+#include <filesystem>
+
+#include <batteryservice/BatteryService.h>
#include <input/Input.h>
#include <input/InputDevice.h>
#include <input/KeyCharacterMap.h>
@@ -26,6 +33,8 @@
#include <input/Keyboard.h>
#include <input/PropertyMap.h>
#include <input/VirtualKeyMap.h>
+#include <linux/input.h>
+#include <sys/epoll.h>
#include <utils/BitSet.h>
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
@@ -33,23 +42,22 @@
#include <utils/Log.h>
#include <utils/Mutex.h>
-#include <linux/input.h>
-#include <sys/epoll.h>
-
#include "TouchVideoDevice.h"
-
-/* Convenience constants. */
-
-#define BTN_FIRST 0x100 // first button code
-#define BTN_LAST 0x15f // last button code
+#include "VibrationElement.h"
namespace android {
+/* Number of colors : {red, green, blue} */
+static constexpr size_t COLOR_NUM = 3;
/*
* A raw event as retrieved from the EventHub.
*/
struct RawEvent {
+ // Time when the event happened
nsecs_t when;
+ // Time when the event was read by EventHub. Only populated for input events.
+ // For other events (device added/removed/etc), this value is undefined and should not be read.
+ nsecs_t readTime;
int32_t deviceId;
int32_t type;
int32_t code;
@@ -79,58 +87,124 @@
/*
* Input device classes.
*/
-enum {
+enum class InputDeviceClass : uint32_t {
/* The input device is a keyboard or has buttons. */
- INPUT_DEVICE_CLASS_KEYBOARD = 0x00000001,
+ KEYBOARD = 0x00000001,
/* The input device is an alpha-numeric keyboard (not just a dial pad). */
- INPUT_DEVICE_CLASS_ALPHAKEY = 0x00000002,
+ ALPHAKEY = 0x00000002,
/* The input device is a touchscreen or a touchpad (either single-touch or multi-touch). */
- INPUT_DEVICE_CLASS_TOUCH = 0x00000004,
+ TOUCH = 0x00000004,
/* The input device is a cursor device such as a trackball or mouse. */
- INPUT_DEVICE_CLASS_CURSOR = 0x00000008,
+ CURSOR = 0x00000008,
/* The input device is a multi-touch touchscreen. */
- INPUT_DEVICE_CLASS_TOUCH_MT = 0x00000010,
+ TOUCH_MT = 0x00000010,
/* The input device is a directional pad (implies keyboard, has DPAD keys). */
- INPUT_DEVICE_CLASS_DPAD = 0x00000020,
+ DPAD = 0x00000020,
/* The input device is a gamepad (implies keyboard, has BUTTON keys). */
- INPUT_DEVICE_CLASS_GAMEPAD = 0x00000040,
+ GAMEPAD = 0x00000040,
/* The input device has switches. */
- INPUT_DEVICE_CLASS_SWITCH = 0x00000080,
+ SWITCH = 0x00000080,
/* The input device is a joystick (implies gamepad, has joystick absolute axes). */
- INPUT_DEVICE_CLASS_JOYSTICK = 0x00000100,
+ JOYSTICK = 0x00000100,
/* The input device has a vibrator (supports FF_RUMBLE). */
- INPUT_DEVICE_CLASS_VIBRATOR = 0x00000200,
+ VIBRATOR = 0x00000200,
/* The input device has a microphone. */
- INPUT_DEVICE_CLASS_MIC = 0x00000400,
+ MIC = 0x00000400,
/* The input device is an external stylus (has data we want to fuse with touch data). */
- INPUT_DEVICE_CLASS_EXTERNAL_STYLUS = 0x00000800,
+ EXTERNAL_STYLUS = 0x00000800,
/* The input device has a rotary encoder */
- INPUT_DEVICE_CLASS_ROTARY_ENCODER = 0x00001000,
+ ROTARY_ENCODER = 0x00001000,
+
+ /* The input device has a sensor like accelerometer, gyro, etc */
+ SENSOR = 0x00002000,
+
+ /* The input device has a battery */
+ BATTERY = 0x00004000,
+
+ /* The input device has sysfs controllable lights */
+ LIGHT = 0x00008000,
/* The input device is virtual (not a real device, not part of UI configuration). */
- INPUT_DEVICE_CLASS_VIRTUAL = 0x40000000,
+ VIRTUAL = 0x40000000,
/* The input device is external (not built-in). */
- INPUT_DEVICE_CLASS_EXTERNAL = 0x80000000,
+ EXTERNAL = 0x80000000,
+};
+
+enum class SysfsClass : uint32_t {
+ POWER_SUPPLY = 0,
+ LEDS = 1,
+};
+
+enum class LightColor : uint32_t {
+ RED = 0,
+ GREEN = 1,
+ BLUE = 2,
+};
+
+enum class InputLightClass : uint32_t {
+ /* The input light has brightness node. */
+ BRIGHTNESS = 0x00000001,
+ /* The input light has red name. */
+ RED = 0x00000002,
+ /* The input light has green name. */
+ GREEN = 0x00000004,
+ /* The input light has blue name. */
+ BLUE = 0x00000008,
+ /* The input light has global name. */
+ GLOBAL = 0x00000010,
+ /* The input light has multi index node. */
+ MULTI_INDEX = 0x00000020,
+ /* The input light has multi intensity node. */
+ MULTI_INTENSITY = 0x00000040,
+ /* The input light has max brightness node. */
+ MAX_BRIGHTNESS = 0x00000080,
+};
+
+enum class InputBatteryClass : uint32_t {
+ /* The input device battery has capacity node. */
+ CAPACITY = 0x00000001,
+ /* The input device battery has capacity_level node. */
+ CAPACITY_LEVEL = 0x00000002,
+ /* The input device battery has status node. */
+ STATUS = 0x00000004,
+};
+
+/* Describes a raw light. */
+struct RawLightInfo {
+ int32_t id;
+ std::string name;
+ std::optional<int32_t> maxBrightness;
+ Flags<InputLightClass> flags;
+ std::array<int32_t, COLOR_NUM> rgbIndex;
+ std::filesystem::path path;
+};
+
+/* Describes a raw battery. */
+struct RawBatteryInfo {
+ int32_t id;
+ std::string name;
+ Flags<InputBatteryClass> flags;
+ std::filesystem::path path;
};
/*
* Gets the class that owns an axis, in cases where multiple classes might claim
* the same axis for different purposes.
*/
-extern uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses);
+extern Flags<InputDeviceClass> getAbsAxisUsage(int32_t axis, Flags<InputDeviceClass> deviceClasses);
/*
* Grand Central Station for events.
@@ -163,7 +237,7 @@
FIRST_SYNTHETIC_EVENT = DEVICE_ADDED,
};
- virtual uint32_t getDeviceClasses(int32_t deviceId) const = 0;
+ virtual Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const = 0;
virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const = 0;
@@ -178,6 +252,8 @@
virtual bool hasInputProperty(int32_t deviceId, int property) const = 0;
+ virtual bool hasMscEvent(int32_t deviceId, int mscEvent) const = 0;
+
virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
int32_t metaState, int32_t* outKeycode, int32_t* outMetaState,
uint32_t* outFlags) const = 0;
@@ -202,7 +278,24 @@
*/
virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) = 0;
virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) = 0;
+ virtual base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(int32_t deviceId,
+ int32_t absCode) = 0;
+ // Raw batteries are sysfs power_supply nodes we found from the EventHub device sysfs node,
+ // containing the raw info of the sysfs node structure.
+ virtual const std::vector<int32_t> getRawBatteryIds(int32_t deviceId) = 0;
+ virtual std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId,
+ int32_t BatteryId) = 0;
+ // Raw lights are sysfs led light nodes we found from the EventHub device sysfs node,
+ // containing the raw info of the sysfs node structure.
+ virtual const std::vector<int32_t> getRawLightIds(int32_t deviceId) = 0;
+ virtual std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId, int32_t lightId) = 0;
+ virtual std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) = 0;
+ virtual void setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) = 0;
+ virtual std::optional<std::unordered_map<LightColor, int32_t>> getLightIntensities(
+ int32_t deviceId, int32_t lightId) = 0;
+ virtual void setLightIntensities(int32_t deviceId, int32_t lightId,
+ std::unordered_map<LightColor, int32_t> intensities) = 0;
/*
* Query current input state.
*/
@@ -227,12 +320,21 @@
virtual void getVirtualKeyDefinitions(
int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const = 0;
- virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const = 0;
- virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) = 0;
+ virtual const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const = 0;
+ virtual bool setKeyboardLayoutOverlay(int32_t deviceId,
+ std::shared_ptr<KeyCharacterMap> map) = 0;
/* Control the vibrator. */
- virtual void vibrate(int32_t deviceId, nsecs_t duration) = 0;
+ virtual void vibrate(int32_t deviceId, const VibrationElement& effect) = 0;
virtual void cancelVibrate(int32_t deviceId) = 0;
+ virtual std::vector<int32_t> getVibratorIds(int32_t deviceId) = 0;
+
+ /* Query battery level. */
+ virtual std::optional<int32_t> getBatteryCapacity(int32_t deviceId,
+ int32_t batteryId) const = 0;
+
+ /* Query battery status. */
+ virtual std::optional<int32_t> getBatteryStatus(int32_t deviceId, int32_t batteryId) const = 0;
/* Requests the EventHub to reopen all input devices on the next call to getEvents(). */
virtual void requestReopenDevices() = 0;
@@ -256,73 +358,193 @@
virtual status_t disableDevice(int32_t deviceId) = 0;
};
+template <std::size_t BITS>
+class BitArray {
+ /* Array element type and vector of element type. */
+ using Element = std::uint32_t;
+ /* Number of bits in each BitArray element. */
+ static constexpr size_t WIDTH = sizeof(Element) * CHAR_BIT;
+ /* Number of elements to represent a bit array of the specified size of bits. */
+ static constexpr size_t COUNT = (BITS + WIDTH - 1) / WIDTH;
+
+public:
+ /* BUFFER type declaration for BitArray */
+ using Buffer = std::array<Element, COUNT>;
+ /* To tell if a bit is set in array, it selects an element from the array, and test
+ * if the relevant bit set.
+ * Note the parameter "bit" is an index to the bit, 0 <= bit < BITS.
+ */
+ inline bool test(size_t bit) const {
+ return (bit < BITS) ? mData[bit / WIDTH].test(bit % WIDTH) : false;
+ }
+ /* Returns total number of bytes needed for the array */
+ inline size_t bytes() { return (BITS + CHAR_BIT - 1) / CHAR_BIT; }
+ /* Returns true if array contains any non-zero bit from the range defined by start and end
+ * bit index [startIndex, endIndex).
+ */
+ bool any(size_t startIndex, size_t endIndex) {
+ if (startIndex >= endIndex || startIndex > BITS || endIndex > BITS + 1) {
+ ALOGE("Invalid start/end index. start = %zu, end = %zu, total bits = %zu", startIndex,
+ endIndex, BITS);
+ return false;
+ }
+ size_t se = startIndex / WIDTH; // Start of element
+ size_t ee = endIndex / WIDTH; // End of element
+ size_t si = startIndex % WIDTH; // Start index in start element
+ size_t ei = endIndex % WIDTH; // End index in end element
+ // Need to check first unaligned bitset for any non zero bit
+ if (si > 0) {
+ size_t nBits = se == ee ? ei - si : WIDTH - si;
+ // Generate the mask of interested bit range
+ Element mask = ((1 << nBits) - 1) << si;
+ if (mData[se++].to_ulong() & mask) {
+ return true;
+ }
+ }
+ // Check whole bitset for any bit set
+ for (; se < ee; se++) {
+ if (mData[se].any()) {
+ return true;
+ }
+ }
+ // Need to check last unaligned bitset for any non zero bit
+ if (ei > 0 && se <= ee) {
+ // Generate the mask of interested bit range
+ Element mask = (1 << ei) - 1;
+ if (mData[se].to_ulong() & mask) {
+ return true;
+ }
+ }
+ return false;
+ }
+ /* Load bit array values from buffer */
+ void loadFromBuffer(const Buffer& buffer) {
+ for (size_t i = 0; i < COUNT; i++) {
+ mData[i] = std::bitset<WIDTH>(buffer[i]);
+ }
+ }
+
+private:
+ std::array<std::bitset<WIDTH>, COUNT> mData;
+};
+
class EventHub : public EventHubInterface {
public:
EventHub();
- virtual uint32_t getDeviceClasses(int32_t deviceId) const override;
+ Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const override final;
- virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override;
+ InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override final;
- virtual int32_t getDeviceControllerNumber(int32_t deviceId) const override;
+ int32_t getDeviceControllerNumber(int32_t deviceId) const override final;
- virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override;
+ void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override final;
- virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
- RawAbsoluteAxisInfo* outAxisInfo) const override;
+ status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
+ RawAbsoluteAxisInfo* outAxisInfo) const override final;
- virtual bool hasRelativeAxis(int32_t deviceId, int axis) const override;
+ bool hasRelativeAxis(int32_t deviceId, int axis) const override final;
- virtual bool hasInputProperty(int32_t deviceId, int property) const override;
+ bool hasInputProperty(int32_t deviceId, int property) const override final;
- virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
- int32_t metaState, int32_t* outKeycode, int32_t* outMetaState,
- uint32_t* outFlags) const override;
+ bool hasMscEvent(int32_t deviceId, int mscEvent) const override final;
- virtual status_t mapAxis(int32_t deviceId, int32_t scanCode,
- AxisInfo* outAxisInfo) const override;
+ status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
+ int32_t* outKeycode, int32_t* outMetaState,
+ uint32_t* outFlags) const override final;
- virtual void setExcludedDevices(const std::vector<std::string>& devices) override;
+ status_t mapAxis(int32_t deviceId, int32_t scanCode,
+ AxisInfo* outAxisInfo) const override final;
- virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override;
- virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override;
- virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const override;
- virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
- int32_t* outValue) const override;
+ base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(
+ int32_t deviceId, int32_t absCode) override final;
- virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
- uint8_t* outFlags) const override;
+ const std::vector<int32_t> getRawBatteryIds(int32_t deviceId) override final;
+ std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId,
+ int32_t BatteryId) override final;
- virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) override;
- virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override;
+ const std::vector<int32_t> getRawLightIds(int32_t deviceId) override final;
- virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const override;
- virtual bool hasLed(int32_t deviceId, int32_t led) const override;
- virtual void setLedState(int32_t deviceId, int32_t led, bool on) override;
+ std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId, int32_t lightId) override final;
- virtual void getVirtualKeyDefinitions(
- int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const override;
+ std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) override final;
+ void setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) override final;
+ std::optional<std::unordered_map<LightColor, int32_t>> getLightIntensities(
+ int32_t deviceId, int32_t lightId) override final;
+ void setLightIntensities(int32_t deviceId, int32_t lightId,
+ std::unordered_map<LightColor, int32_t> intensities) override final;
- virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const override;
- virtual bool setKeyboardLayoutOverlay(int32_t deviceId,
- const sp<KeyCharacterMap>& map) override;
+ void setExcludedDevices(const std::vector<std::string>& devices) override final;
- virtual void vibrate(int32_t deviceId, nsecs_t duration) override;
- virtual void cancelVibrate(int32_t deviceId) override;
+ int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override final;
+ int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override final;
+ int32_t getSwitchState(int32_t deviceId, int32_t sw) const override final;
+ status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
+ int32_t* outValue) const override final;
- virtual void requestReopenDevices() override;
+ bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
+ uint8_t* outFlags) const override final;
- virtual void wake() override;
+ size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) override final;
+ std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override final;
- virtual void dump(std::string& dump) override;
- virtual void monitor() override;
+ bool hasScanCode(int32_t deviceId, int32_t scanCode) const override final;
+ bool hasLed(int32_t deviceId, int32_t led) const override final;
+ void setLedState(int32_t deviceId, int32_t led, bool on) override final;
- virtual ~EventHub() override;
+ void getVirtualKeyDefinitions(
+ int32_t deviceId,
+ std::vector<VirtualKeyDefinition>& outVirtualKeys) const override final;
+
+ const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap(
+ int32_t deviceId) const override final;
+ bool setKeyboardLayoutOverlay(int32_t deviceId,
+ std::shared_ptr<KeyCharacterMap> map) override final;
+
+ void vibrate(int32_t deviceId, const VibrationElement& effect) override final;
+ void cancelVibrate(int32_t deviceId) override final;
+ std::vector<int32_t> getVibratorIds(int32_t deviceId) override final;
+
+ void requestReopenDevices() override final;
+
+ void wake() override final;
+
+ void dump(std::string& dump) override final;
+
+ void monitor() override final;
+
+ std::optional<int32_t> getBatteryCapacity(int32_t deviceId,
+ int32_t batteryId) const override final;
+
+ std::optional<int32_t> getBatteryStatus(int32_t deviceId,
+ int32_t batteryId) const override final;
+
+ bool isDeviceEnabled(int32_t deviceId) override final;
+
+ status_t enableDevice(int32_t deviceId) override final;
+
+ status_t disableDevice(int32_t deviceId) override final;
+
+ ~EventHub() override;
private:
- struct Device {
- Device* next;
+ struct AssociatedDevice {
+ // The device descriptor from evdev device the misc device associated with.
+ std::string descriptor;
+ // The sysfs root path of the misc device.
+ std::filesystem::path sysfsRootPath;
+ int32_t nextBatteryId;
+ int32_t nextLightId;
+ std::unordered_map<int32_t, RawBatteryInfo> batteryInfos;
+ std::unordered_map<int32_t, RawLightInfo> lightInfos;
+ explicit AssociatedDevice(std::filesystem::path sysfsRootPath)
+ : sysfsRootPath(sysfsRootPath), nextBatteryId(0), nextLightId(0) {}
+ bool configureBatteryLocked();
+ bool configureLightsLocked();
+ };
+
+ struct Device {
int fd; // may be -1 if device is closed
const int32_t id;
const std::string path;
@@ -330,27 +552,31 @@
std::unique_ptr<TouchVideoDevice> videoDevice;
- uint32_t classes;
+ Flags<InputDeviceClass> classes;
- uint8_t keyBitmask[(KEY_MAX + 1) / 8];
- uint8_t absBitmask[(ABS_MAX + 1) / 8];
- uint8_t relBitmask[(REL_MAX + 1) / 8];
- uint8_t swBitmask[(SW_MAX + 1) / 8];
- uint8_t ledBitmask[(LED_MAX + 1) / 8];
- uint8_t ffBitmask[(FF_MAX + 1) / 8];
- uint8_t propBitmask[(INPUT_PROP_MAX + 1) / 8];
+ BitArray<KEY_MAX> keyBitmask;
+ BitArray<KEY_MAX> keyState;
+ BitArray<ABS_MAX> absBitmask;
+ BitArray<REL_MAX> relBitmask;
+ BitArray<SW_MAX> swBitmask;
+ BitArray<SW_MAX> swState;
+ BitArray<LED_MAX> ledBitmask;
+ BitArray<FF_MAX> ffBitmask;
+ BitArray<INPUT_PROP_MAX> propBitmask;
+ BitArray<MSC_MAX> mscBitmask;
std::string configurationFile;
- PropertyMap* configuration;
+ std::unique_ptr<PropertyMap> configuration;
std::unique_ptr<VirtualKeyMap> virtualKeyMap;
KeyMap keyMap;
- sp<KeyCharacterMap> overlayKeyMap;
- sp<KeyCharacterMap> combinedKeyMap;
-
bool ffEffectPlaying;
int16_t ffEffectId; // initially -1
+ // A shared_ptr of a device associated with the input device.
+ // The input devices with same descriptor has the same associated device.
+ std::shared_ptr<AssociatedDevice> associatedDevice;
+
int32_t controllerNumber;
Device(int fd, int32_t id, const std::string& path,
@@ -362,72 +588,83 @@
bool enabled; // initially true
status_t enable();
status_t disable();
- bool hasValidFd();
+ bool hasValidFd() const;
const bool isVirtual; // set if fd < 0 is passed to constructor
- const sp<KeyCharacterMap>& getKeyCharacterMap() const {
- if (combinedKeyMap != nullptr) {
- return combinedKeyMap;
- }
- return keyMap.keyCharacterMap;
- }
+ const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap() const;
+
+ template <std::size_t N>
+ status_t readDeviceBitMask(unsigned long ioctlCode, BitArray<N>& bitArray);
+
+ void configureFd();
+ bool hasKeycodeLocked(int keycode) const;
+ void loadConfigurationLocked();
+ bool loadVirtualKeyMapLocked();
+ status_t loadKeyMapLocked();
+ bool isExternalDeviceLocked();
+ bool deviceHasMicLocked();
+ void setLedForControllerLocked();
+ status_t mapLed(int32_t led, int32_t* outScanCode) const;
+ void setLedStateLocked(int32_t led, bool on);
};
- status_t openDeviceLocked(const char* devicePath);
- void openVideoDeviceLocked(const std::string& devicePath);
- void createVirtualKeyboardLocked();
- void addDeviceLocked(Device* device);
- void assignDescriptorLocked(InputDeviceIdentifier& identifier);
+ /**
+ * Create a new device for the provided path.
+ */
+ void openDeviceLocked(const std::string& devicePath) REQUIRES(mLock);
+ void openVideoDeviceLocked(const std::string& devicePath) REQUIRES(mLock);
+ /**
+ * Try to associate a video device with an input device. If the association succeeds,
+ * the videoDevice is moved into the input device. 'videoDevice' will become null if this
+ * happens.
+ * Return true if the association succeeds.
+ * Return false otherwise.
+ */
+ bool tryAddVideoDeviceLocked(Device& device, std::unique_ptr<TouchVideoDevice>& videoDevice)
+ REQUIRES(mLock);
+ void createVirtualKeyboardLocked() REQUIRES(mLock);
+ void addDeviceLocked(std::unique_ptr<Device> device) REQUIRES(mLock);
+ void assignDescriptorLocked(InputDeviceIdentifier& identifier) REQUIRES(mLock);
- void closeDeviceByPathLocked(const char* devicePath);
- void closeVideoDeviceByPathLocked(const std::string& devicePath);
- void closeDeviceLocked(Device* device);
- void closeAllDevicesLocked();
+ void closeDeviceByPathLocked(const std::string& devicePath) REQUIRES(mLock);
+ void closeVideoDeviceByPathLocked(const std::string& devicePath) REQUIRES(mLock);
+ void closeDeviceLocked(Device& device) REQUIRES(mLock);
+ void closeAllDevicesLocked() REQUIRES(mLock);
- void configureFd(Device* device);
-
- bool isDeviceEnabled(int32_t deviceId) override;
- status_t enableDevice(int32_t deviceId) override;
- status_t disableDevice(int32_t deviceId) override;
status_t registerFdForEpoll(int fd);
status_t unregisterFdFromEpoll(int fd);
- status_t registerDeviceForEpollLocked(Device* device);
- void registerVideoDeviceForEpollLocked(const TouchVideoDevice& videoDevice);
- status_t unregisterDeviceFromEpollLocked(Device* device);
- void unregisterVideoDeviceFromEpollLocked(const TouchVideoDevice& videoDevice);
+ status_t registerDeviceForEpollLocked(Device& device) REQUIRES(mLock);
+ void registerVideoDeviceForEpollLocked(const TouchVideoDevice& videoDevice) REQUIRES(mLock);
+ status_t unregisterDeviceFromEpollLocked(Device& device) REQUIRES(mLock);
+ void unregisterVideoDeviceFromEpollLocked(const TouchVideoDevice& videoDevice) REQUIRES(mLock);
- status_t scanDirLocked(const char* dirname);
- status_t scanVideoDirLocked(const std::string& dirname);
- void scanDevicesLocked();
- status_t readNotifyLocked();
+ status_t scanDirLocked(const std::string& dirname) REQUIRES(mLock);
+ status_t scanVideoDirLocked(const std::string& dirname) REQUIRES(mLock);
+ void scanDevicesLocked() REQUIRES(mLock);
+ status_t readNotifyLocked() REQUIRES(mLock);
- Device* getDeviceByDescriptorLocked(const std::string& descriptor) const;
- Device* getDeviceLocked(int32_t deviceId) const;
- Device* getDeviceByPathLocked(const char* devicePath) const;
+ Device* getDeviceByDescriptorLocked(const std::string& descriptor) const REQUIRES(mLock);
+ Device* getDeviceLocked(int32_t deviceId) const REQUIRES(mLock);
+ Device* getDeviceByPathLocked(const std::string& devicePath) const REQUIRES(mLock);
/**
* Look through all available fd's (both for input devices and for video devices),
* and return the device pointer.
*/
- Device* getDeviceByFdLocked(int fd) const;
+ Device* getDeviceByFdLocked(int fd) const REQUIRES(mLock);
- bool hasKeycodeLocked(Device* device, int keycode) const;
+ int32_t getNextControllerNumberLocked(const std::string& name) REQUIRES(mLock);
+ void releaseControllerNumberLocked(int32_t num) REQUIRES(mLock);
+ void reportDeviceAddedForStatisticsLocked(const InputDeviceIdentifier& identifier,
+ Flags<InputDeviceClass> classes) REQUIRES(mLock);
- void loadConfigurationLocked(Device* device);
- bool loadVirtualKeyMapLocked(Device* device);
- status_t loadKeyMapLocked(Device* device);
+ const std::unordered_map<int32_t, RawBatteryInfo>& getBatteryInfoLocked(int32_t deviceId) const
+ REQUIRES(mLock);
- bool isExternalDeviceLocked(Device* device);
- bool deviceHasMicLocked(Device* device);
-
- int32_t getNextControllerNumberLocked(Device* device);
- void releaseControllerNumberLocked(Device* device);
- void setLedForControllerLocked(Device* device);
-
- status_t mapLed(Device* device, int32_t led, int32_t* outScanCode) const;
- void setLedStateLocked(Device* device, int32_t led, bool on);
+ const std::unordered_map<int32_t, RawLightInfo>& getLightInfoLocked(int32_t deviceId) const
+ REQUIRES(mLock);
// Protect all internal state.
- mutable Mutex mLock;
+ mutable std::mutex mLock;
// The actual id of the built-in keyboard, or NO_BUILT_IN_KEYBOARD if none.
// EventHub remaps the built-in keyboard to id 0 externally as required by the API.
@@ -442,7 +679,7 @@
BitSet32 mControllerNumbers;
- KeyedVector<int32_t, Device*> mDevices;
+ std::unordered_map<int32_t, std::unique_ptr<Device>> mDevices;
/**
* Video devices that report touchscreen heatmap, but have not (yet) been paired
* with a specific input device. Video device discovery is independent from input device
@@ -452,8 +689,8 @@
*/
std::vector<std::unique_ptr<TouchVideoDevice>> mUnattachedVideoDevices;
- Device* mOpeningDevices;
- Device* mClosingDevices;
+ std::vector<std::unique_ptr<Device>> mOpeningDevices;
+ std::vector<std::unique_ptr<Device>> mClosingDevices;
bool mNeedToSendFinishedDeviceScan;
bool mNeedToReopenDevices;
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index d2bb4f4..291f105 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -18,6 +18,7 @@
#define _UI_INPUTREADER_INPUT_DEVICE_H
#include <input/DisplayViewport.h>
+#include <input/Flags.h>
#include <input/InputDevice.h>
#include <input/PropertyMap.h>
#include <stdint.h>
@@ -31,7 +32,11 @@
#include "InputReaderContext.h"
namespace android {
+// TODO b/180733860 support multiple battery in API and remove this.
+constexpr int32_t DEFAULT_BATTERY_ID = 1;
+class PeripheralController;
+class PeripheralControllerInterface;
class InputDeviceContext;
class InputMapper;
@@ -48,7 +53,7 @@
inline int32_t getGeneration() const { return mGeneration; }
inline const std::string getName() const { return mIdentifier.name; }
inline const std::string getDescriptor() { return mIdentifier.descriptor; }
- inline uint32_t getClasses() const { return mClasses; }
+ inline Flags<InputDeviceClass> getClasses() const { return mClasses; }
inline uint32_t getSources() const { return mSources; }
inline bool hasEventHubDevices() const { return !mDevices.empty(); }
@@ -81,9 +86,23 @@
int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes,
uint8_t* outFlags);
- void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token);
+ void vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token);
void cancelVibrate(int32_t token);
- void cancelTouch(nsecs_t when);
+ bool isVibrating();
+ std::vector<int32_t> getVibratorIds();
+ void cancelTouch(nsecs_t when, nsecs_t readTime);
+ bool enableSensor(InputDeviceSensorType sensorType, std::chrono::microseconds samplingPeriod,
+ std::chrono::microseconds maxBatchReportLatency);
+ void disableSensor(InputDeviceSensorType sensorType);
+ void flushSensor(InputDeviceSensorType sensorType);
+
+ std::optional<int32_t> getBatteryCapacity();
+ std::optional<int32_t> getBatteryStatus();
+
+ bool setLightColor(int32_t lightId, int32_t color);
+ bool setLightPlayerId(int32_t lightId, int32_t playerId);
+ std::optional<int32_t> getLightColor(int32_t lightId);
+ std::optional<int32_t> getLightPlayerId(int32_t lightId);
int32_t getMetaState();
void updateMetaState(int32_t keyCode);
@@ -97,6 +116,8 @@
std::optional<int32_t> getAssociatedDisplayId();
+ void updateLedState(bool reset);
+
size_t getMapperCount();
// construct and add a mapper to the input device
@@ -114,6 +135,20 @@
return *mapper;
}
+ // construct and add a controller to the input device
+ template <class T>
+ T& addController(int32_t eventHubId) {
+ // ensure a device entry exists for this eventHubId
+ addEventHubDevice(eventHubId, false);
+
+ // create controller
+ auto& devicePair = mDevices[eventHubId];
+ auto& deviceContext = devicePair.first;
+
+ mController = std::make_unique<T>(*deviceContext);
+ return *(reinterpret_cast<T*>(mController.get()));
+ }
+
private:
InputReaderContext* mContext;
int32_t mId;
@@ -121,16 +156,20 @@
int32_t mControllerNumber;
InputDeviceIdentifier mIdentifier;
std::string mAlias;
- uint32_t mClasses;
+ Flags<InputDeviceClass> mClasses;
// map from eventHubId to device context and mappers
using MapperVector = std::vector<std::unique_ptr<InputMapper>>;
using DevicePair = std::pair<std::unique_ptr<InputDeviceContext>, MapperVector>;
+ // Map from EventHub ID to pair of device context and vector of mapper.
std::unordered_map<int32_t, DevicePair> mDevices;
+ // Misc devices controller for lights, battery, etc.
+ std::unique_ptr<PeripheralControllerInterface> mController;
uint32_t mSources;
bool mIsExternal;
std::optional<uint8_t> mAssociatedDisplayPort;
+ std::optional<std::string> mAssociatedDisplayUniqueId;
std::optional<DisplayViewport> mAssociatedViewport;
bool mHasMic;
bool mDropUntilNextSync;
@@ -206,7 +245,9 @@
inline int32_t getId() { return mDeviceId; }
inline int32_t getEventHubId() { return mId; }
- inline uint32_t getDeviceClasses() const { return mEventHub->getDeviceClasses(mId); }
+ inline Flags<InputDeviceClass> getDeviceClasses() const {
+ return mEventHub->getDeviceClasses(mId);
+ }
inline InputDeviceIdentifier getDeviceIdentifier() const {
return mEventHub->getDeviceIdentifier(mId);
}
@@ -222,9 +263,12 @@
inline bool hasRelativeAxis(int32_t code) const {
return mEventHub->hasRelativeAxis(mId, code);
}
- inline bool hasInputProperty(int property) const {
+ inline bool hasInputProperty(int32_t property) const {
return mEventHub->hasInputProperty(mId, property);
}
+
+ inline bool hasMscEvent(int mscEvent) const { return mEventHub->hasMscEvent(mId, mscEvent); }
+
inline status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t metaState,
int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const {
return mEventHub->mapKey(mId, scanCode, usageCode, metaState, outKeycode, outMetaState,
@@ -233,6 +277,34 @@
inline status_t mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const {
return mEventHub->mapAxis(mId, scanCode, outAxisInfo);
}
+ inline base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(int32_t absCode) {
+ return mEventHub->mapSensor(mId, absCode);
+ }
+
+ inline const std::vector<int32_t> getRawLightIds() { return mEventHub->getRawLightIds(mId); }
+
+ inline std::optional<RawLightInfo> getRawLightInfo(int32_t lightId) {
+ return mEventHub->getRawLightInfo(mId, lightId);
+ }
+
+ inline std::optional<int32_t> getLightBrightness(int32_t lightId) {
+ return mEventHub->getLightBrightness(mId, lightId);
+ }
+
+ inline void setLightBrightness(int32_t lightId, int32_t brightness) {
+ return mEventHub->setLightBrightness(mId, lightId, brightness);
+ }
+
+ inline std::optional<std::unordered_map<LightColor, int32_t>> getLightIntensities(
+ int32_t lightId) {
+ return mEventHub->getLightIntensities(mId, lightId);
+ }
+
+ inline void setLightIntensities(int32_t lightId,
+ std::unordered_map<LightColor, int32_t> intensities) {
+ return mEventHub->setLightIntensities(mId, lightId, intensities);
+ }
+
inline std::vector<TouchVideoFrame> getVideoFrames() { return mEventHub->getVideoFrames(mId); }
inline int32_t getScanCodeState(int32_t scanCode) const {
return mEventHub->getScanCodeState(mId, scanCode);
@@ -256,15 +328,35 @@
inline void getVirtualKeyDefinitions(std::vector<VirtualKeyDefinition>& outVirtualKeys) const {
return mEventHub->getVirtualKeyDefinitions(mId, outVirtualKeys);
}
- inline sp<KeyCharacterMap> getKeyCharacterMap() const {
+ inline const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap() const {
return mEventHub->getKeyCharacterMap(mId);
}
- inline bool setKeyboardLayoutOverlay(const sp<KeyCharacterMap>& map) {
+ inline bool setKeyboardLayoutOverlay(std::shared_ptr<KeyCharacterMap> map) {
return mEventHub->setKeyboardLayoutOverlay(mId, map);
}
- inline void vibrate(nsecs_t duration) { return mEventHub->vibrate(mId, duration); }
+ inline void vibrate(const VibrationElement& element) {
+ return mEventHub->vibrate(mId, element);
+ }
inline void cancelVibrate() { return mEventHub->cancelVibrate(mId); }
+ inline std::vector<int32_t> getVibratorIds() { return mEventHub->getVibratorIds(mId); }
+
+ inline const std::vector<int32_t> getRawBatteryIds() {
+ return mEventHub->getRawBatteryIds(mId);
+ }
+
+ inline std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t batteryId) {
+ return mEventHub->getRawBatteryInfo(mId, batteryId);
+ }
+
+ inline std::optional<int32_t> getBatteryCapacity(int32_t batteryId) {
+ return mEventHub->getBatteryCapacity(mId, batteryId);
+ }
+
+ inline std::optional<int32_t> getBatteryStatus(int32_t batteryId) {
+ return mEventHub->getBatteryStatus(mId, batteryId);
+ }
+
inline bool hasAbsoluteAxis(int32_t code) const {
RawAbsoluteAxisInfo info;
mEventHub->getAbsoluteAxisInfo(mId, code, &info);
@@ -291,7 +383,7 @@
inline std::optional<DisplayViewport> getAssociatedViewport() const {
return mDevice.getAssociatedViewport();
}
- inline void cancelTouch(nsecs_t when) { mDevice.cancelTouch(when); }
+ inline void cancelTouch(nsecs_t when, nsecs_t readTime) { mDevice.cancelTouch(when, readTime); }
inline void bumpGeneration() { mDevice.bumpGeneration(); }
inline const PropertyMap& getConfiguration() { return mDevice.getConfiguration(); }
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 2773f70..1405671 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -18,6 +18,7 @@
#define _UI_INPUTREADER_INPUT_READER_H
#include <PointerControllerInterface.h>
+#include <android-base/thread_annotations.h>
#include <utils/Condition.h>
#include <utils/Mutex.h>
@@ -55,39 +56,66 @@
const sp<InputListenerInterface>& listener);
virtual ~InputReader();
- virtual void dump(std::string& dump) override;
- virtual void monitor() override;
+ void dump(std::string& dump) override;
+ void monitor() override;
- virtual status_t start() override;
- virtual status_t stop() override;
+ status_t start() override;
+ status_t stop() override;
- virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) override;
+ std::vector<InputDeviceInfo> getInputDevices() const override;
- virtual bool isInputDeviceEnabled(int32_t deviceId) override;
+ bool isInputDeviceEnabled(int32_t deviceId) override;
- virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
- int32_t scanCode) override;
- virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
- int32_t keyCode) override;
- virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) override;
+ int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, int32_t scanCode) override;
+ int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) override;
+ int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) override;
- virtual void toggleCapsLockState(int32_t deviceId) override;
+ void toggleCapsLockState(int32_t deviceId) override;
- virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
- const int32_t* keyCodes, uint8_t* outFlags) override;
+ bool hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes,
+ uint8_t* outFlags) override;
- virtual void requestRefreshConfiguration(uint32_t changes) override;
+ void requestRefreshConfiguration(uint32_t changes) override;
- virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
- ssize_t repeat, int32_t token) override;
- virtual void cancelVibrate(int32_t deviceId, int32_t token) override;
+ void vibrate(int32_t deviceId, const VibrationSequence& sequence, ssize_t repeat,
+ int32_t token) override;
+ void cancelVibrate(int32_t deviceId, int32_t token) override;
- virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) override;
+ bool isVibrating(int32_t deviceId) override;
+
+ std::vector<int32_t> getVibratorIds(int32_t deviceId) override;
+
+ bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) override;
+
+ bool enableSensor(int32_t deviceId, InputDeviceSensorType sensorType,
+ std::chrono::microseconds samplingPeriod,
+ std::chrono::microseconds maxBatchReportLatency) override;
+
+ void disableSensor(int32_t deviceId, InputDeviceSensorType sensorType) override;
+
+ void flushSensor(int32_t deviceId, InputDeviceSensorType sensorType) override;
+
+ std::optional<int32_t> getBatteryCapacity(int32_t deviceId) override;
+
+ std::optional<int32_t> getBatteryStatus(int32_t deviceId) override;
+
+ std::vector<int32_t> getLightIds(int32_t deviceId) override;
+
+ const InputDeviceLightInfo* getLightInfo(int32_t deviceId, int32_t lightId) override;
+
+ bool setLightColor(int32_t deviceId, int32_t lightId, int32_t color) override;
+
+ bool setLightPlayerId(int32_t deviceId, int32_t lightId, int32_t playerId) override;
+
+ std::optional<int32_t> getLightColor(int32_t deviceId, int32_t lightId) override;
+
+ std::optional<int32_t> getLightPlayerId(int32_t deviceId, int32_t lightId) override;
protected:
// These members are protected so they can be instrumented by test cases.
- virtual std::shared_ptr<InputDevice> createDeviceLocked(
- int32_t deviceId, const InputDeviceIdentifier& identifier);
+ virtual std::shared_ptr<InputDevice> createDeviceLocked(int32_t deviceId,
+ const InputDeviceIdentifier& identifier)
+ REQUIRES(mLock);
// With each iteration of the loop, InputReader reads and processes one incoming message from
// the EventHub.
@@ -99,32 +127,37 @@
public:
explicit ContextImpl(InputReader* reader);
-
- virtual void updateGlobalMetaState() override;
- virtual int32_t getGlobalMetaState() override;
- virtual void disableVirtualKeysUntil(nsecs_t time) override;
- virtual bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode) override;
- virtual void fadePointer() override;
- virtual std::shared_ptr<PointerControllerInterface> getPointerController(
- int32_t deviceId) override;
- virtual void requestTimeoutAtTime(nsecs_t when) override;
- virtual int32_t bumpGeneration() override;
- virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override;
- virtual void dispatchExternalStylusState(const StylusState& outState) override;
- virtual InputReaderPolicyInterface* getPolicy() override;
- virtual InputListenerInterface* getListener() override;
- virtual EventHubInterface* getEventHub() override;
- virtual int32_t getNextId() override;
+ // lock is already held by the input loop
+ void updateGlobalMetaState() NO_THREAD_SAFETY_ANALYSIS override;
+ int32_t getGlobalMetaState() NO_THREAD_SAFETY_ANALYSIS override;
+ void disableVirtualKeysUntil(nsecs_t time) NO_THREAD_SAFETY_ANALYSIS override;
+ bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode,
+ int32_t scanCode) NO_THREAD_SAFETY_ANALYSIS override;
+ void fadePointer() NO_THREAD_SAFETY_ANALYSIS override;
+ std::shared_ptr<PointerControllerInterface> getPointerController(int32_t deviceId)
+ NO_THREAD_SAFETY_ANALYSIS override;
+ void requestTimeoutAtTime(nsecs_t when) NO_THREAD_SAFETY_ANALYSIS override;
+ int32_t bumpGeneration() NO_THREAD_SAFETY_ANALYSIS override;
+ void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices)
+ NO_THREAD_SAFETY_ANALYSIS override;
+ void dispatchExternalStylusState(const StylusState& outState)
+ NO_THREAD_SAFETY_ANALYSIS override;
+ InputReaderPolicyInterface* getPolicy() NO_THREAD_SAFETY_ANALYSIS override;
+ InputListenerInterface* getListener() NO_THREAD_SAFETY_ANALYSIS override;
+ EventHubInterface* getEventHub() NO_THREAD_SAFETY_ANALYSIS override;
+ int32_t getNextId() NO_THREAD_SAFETY_ANALYSIS override;
+ void updateLedMetaState(int32_t metaState) NO_THREAD_SAFETY_ANALYSIS override;
+ int32_t getLedMetaState() NO_THREAD_SAFETY_ANALYSIS override;
} mContext;
friend class ContextImpl;
+ // Test cases need to override the locked functions
+ mutable std::mutex mLock;
private:
std::unique_ptr<InputThread> mThread;
- Mutex mLock;
-
- Condition mReaderIsAliveCondition;
+ std::condition_variable mReaderIsAliveCondition;
// This could be unique_ptr, but due to the way InputReader tests are written,
// it is made shared_ptr here. In the tests, an EventHub reference is retained by the test
@@ -133,72 +166,79 @@
sp<InputReaderPolicyInterface> mPolicy;
sp<QueuedInputListener> mQueuedListener;
- InputReaderConfiguration mConfig;
+ InputReaderConfiguration mConfig GUARDED_BY(mLock);
// The event queue.
static const int EVENT_BUFFER_SIZE = 256;
- RawEvent mEventBuffer[EVENT_BUFFER_SIZE];
+ RawEvent mEventBuffer[EVENT_BUFFER_SIZE] GUARDED_BY(mLock);
// An input device can represent a collection of EventHub devices. This map provides a way
// to lookup the input device instance from the EventHub device id.
- std::unordered_map<int32_t /*eventHubId*/, std::shared_ptr<InputDevice>> mDevices;
+ std::unordered_map<int32_t /*eventHubId*/, std::shared_ptr<InputDevice>> mDevices
+ GUARDED_BY(mLock);
// An input device contains one or more eventHubId, this map provides a way to lookup the
// EventHubIds contained in the input device from the input device instance.
std::unordered_map<std::shared_ptr<InputDevice>, std::vector<int32_t> /*eventHubId*/>
- mDeviceToEventHubIdsMap;
+ mDeviceToEventHubIdsMap GUARDED_BY(mLock);
// low-level input event decoding and device management
- void processEventsLocked(const RawEvent* rawEvents, size_t count);
+ void processEventsLocked(const RawEvent* rawEvents, size_t count) REQUIRES(mLock);
- void addDeviceLocked(nsecs_t when, int32_t eventHubId);
- void removeDeviceLocked(nsecs_t when, int32_t eventHubId);
- void processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents, size_t count);
- void timeoutExpiredLocked(nsecs_t when);
+ void addDeviceLocked(nsecs_t when, int32_t eventHubId) REQUIRES(mLock);
+ void removeDeviceLocked(nsecs_t when, int32_t eventHubId) REQUIRES(mLock);
+ void processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents, size_t count)
+ REQUIRES(mLock);
+ void timeoutExpiredLocked(nsecs_t when) REQUIRES(mLock);
- void handleConfigurationChangedLocked(nsecs_t when);
+ void handleConfigurationChangedLocked(nsecs_t when) REQUIRES(mLock);
- int32_t mGlobalMetaState;
- void updateGlobalMetaStateLocked();
- int32_t getGlobalMetaStateLocked();
+ int32_t mGlobalMetaState GUARDED_BY(mLock);
+ void updateGlobalMetaStateLocked() REQUIRES(mLock);
+ int32_t getGlobalMetaStateLocked() REQUIRES(mLock);
- void notifyExternalStylusPresenceChanged();
- void getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices);
+ int32_t mLedMetaState GUARDED_BY(mLock);
+ void updateLedMetaStateLocked(int32_t metaState) REQUIRES(mLock);
+ int32_t getLedMetaStateLocked() REQUIRES(mLock);
+
+ void notifyExternalStylusPresenceChangedLocked() REQUIRES(mLock);
+ void getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices) REQUIRES(mLock);
void dispatchExternalStylusState(const StylusState& state);
// The PointerController that is shared among all the input devices that need it.
std::weak_ptr<PointerControllerInterface> mPointerController;
- std::shared_ptr<PointerControllerInterface> getPointerControllerLocked(int32_t deviceId);
- void updatePointerDisplayLocked();
- void fadePointerLocked();
+ std::shared_ptr<PointerControllerInterface> getPointerControllerLocked(int32_t deviceId)
+ REQUIRES(mLock);
+ void updatePointerDisplayLocked() REQUIRES(mLock);
+ void fadePointerLocked() REQUIRES(mLock);
- int32_t mGeneration;
- int32_t bumpGenerationLocked();
+ int32_t mGeneration GUARDED_BY(mLock);
+ int32_t bumpGenerationLocked() REQUIRES(mLock);
- int32_t mNextInputDeviceId;
- int32_t nextInputDeviceIdLocked();
+ int32_t mNextInputDeviceId GUARDED_BY(mLock);
+ int32_t nextInputDeviceIdLocked() REQUIRES(mLock);
- void getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDevices);
+ std::vector<InputDeviceInfo> getInputDevicesLocked() const REQUIRES(mLock);
- nsecs_t mDisableVirtualKeysTimeout;
- void disableVirtualKeysUntilLocked(nsecs_t time);
- bool shouldDropVirtualKeyLocked(nsecs_t now, int32_t keyCode, int32_t scanCode);
+ nsecs_t mDisableVirtualKeysTimeout GUARDED_BY(mLock);
+ void disableVirtualKeysUntilLocked(nsecs_t time) REQUIRES(mLock);
+ bool shouldDropVirtualKeyLocked(nsecs_t now, int32_t keyCode, int32_t scanCode) REQUIRES(mLock);
- nsecs_t mNextTimeout;
- void requestTimeoutAtTimeLocked(nsecs_t when);
+ nsecs_t mNextTimeout GUARDED_BY(mLock);
+ void requestTimeoutAtTimeLocked(nsecs_t when) REQUIRES(mLock);
- uint32_t mConfigurationChangesToRefresh;
- void refreshConfigurationLocked(uint32_t changes);
+ uint32_t mConfigurationChangesToRefresh GUARDED_BY(mLock);
+ void refreshConfigurationLocked(uint32_t changes) REQUIRES(mLock);
// state queries
typedef int32_t (InputDevice::*GetStateFunc)(uint32_t sourceMask, int32_t code);
int32_t getStateLocked(int32_t deviceId, uint32_t sourceMask, int32_t code,
- GetStateFunc getStateFunc);
+ GetStateFunc getStateFunc) REQUIRES(mLock);
bool markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
- const int32_t* keyCodes, uint8_t* outFlags);
+ const int32_t* keyCodes, uint8_t* outFlags) REQUIRES(mLock);
// find an InputDevice from an InputDevice id
- InputDevice* findInputDevice(int32_t deviceId);
+ InputDevice* findInputDeviceLocked(int32_t deviceId) REQUIRES(mLock);
};
} // namespace android
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
index ffb8d8c..dc807f7 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -59,6 +59,9 @@
virtual EventHubInterface* getEventHub() = 0;
virtual int32_t getNextId() = 0;
+
+ virtual void updateLedMetaState(int32_t metaState) = 0;
+ virtual int32_t getLedMetaState() = 0;
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 55c1a1d..d6bd823 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -14,7 +14,9 @@
* limitations under the License.
*/
+// clang-format off
#include "../Macros.h"
+// clang-format on
#include "CursorInputMapper.h"
@@ -186,11 +188,29 @@
if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
mOrientation = DISPLAY_ORIENTATION_0;
- if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
- std::optional<DisplayViewport> internalViewport =
- config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
- if (internalViewport) {
- mOrientation = internalViewport->orientation;
+ const bool isOrientedDevice =
+ (mParameters.orientationAware && mParameters.hasAssociatedDisplay);
+
+ if (isPerWindowInputRotationEnabled()) {
+ // When per-window input rotation is enabled, InputReader works in the un-rotated
+ // coordinate space, so we don't need to do anything if the device is already
+ // orientation-aware. If the device is not orientation-aware, then we need to apply the
+ // inverse rotation of the display so that when the display rotation is applied later
+ // as a part of the per-window transform, we get the expected screen coordinates.
+ if (!isOrientedDevice) {
+ std::optional<DisplayViewport> internalViewport =
+ config->getDisplayViewportByType(ViewportType::INTERNAL);
+ if (internalViewport) {
+ mOrientation = getInverseRotation(internalViewport->orientation);
+ }
+ }
+ } else {
+ if (isOrientedDevice) {
+ std::optional<DisplayViewport> internalViewport =
+ config->getDisplayViewportByType(ViewportType::INTERNAL);
+ if (internalViewport) {
+ mOrientation = internalViewport->orientation;
+ }
}
}
@@ -263,11 +283,11 @@
mCursorScrollAccumulator.process(rawEvent);
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
- sync(rawEvent->when);
+ sync(rawEvent->when, rawEvent->readTime);
}
}
-void CursorInputMapper::sync(nsecs_t when) {
+void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {
int32_t lastButtonState = mButtonState;
int32_t currentButtonState = mCursorButtonAccumulator.getButtonState();
mButtonState = currentButtonState;
@@ -292,11 +312,8 @@
float deltaY = mCursorMotionAccumulator.getRelativeY() * mYScale;
bool moved = deltaX != 0 || deltaY != 0;
- // Rotate delta according to orientation if needed.
- if (mParameters.orientationAware && mParameters.hasAssociatedDisplay &&
- (deltaX != 0.0f || deltaY != 0.0f)) {
- rotateDelta(mOrientation, &deltaX, &deltaY);
- }
+ // Rotate delta according to orientation.
+ rotateDelta(mOrientation, &deltaX, &deltaY);
// Move the pointer.
PointerProperties pointerProperties;
@@ -324,7 +341,15 @@
mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
if (moved) {
- mPointerController->move(deltaX, deltaY);
+ float dx = deltaX;
+ float dy = deltaY;
+ if (isPerWindowInputRotationEnabled()) {
+ // Rotate the delta from InputReader's un-rotated coordinate space to
+ // PointerController's rotated coordinate space that is oriented with the
+ // viewport.
+ rotateDelta(getInverseRotation(mOrientation), &dx, &dy);
+ }
+ mPointerController->move(dx, dy);
}
if (buttonsChanged) {
@@ -360,8 +385,8 @@
}
// Synthesize key down from buttons if needed.
- synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,
- displayId, policyFlags, lastButtonState, currentButtonState);
+ synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, readTime, getDeviceId(),
+ mSource, displayId, policyFlags, lastButtonState, currentButtonState);
// Send motion event.
if (downChanged || moved || scrolled || buttonsChanged) {
@@ -381,8 +406,8 @@
while (!released.isEmpty()) {
int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit());
buttonState &= ~actionButton;
- NotifyMotionArgs releaseArgs(getContext()->getNextId(), when, getDeviceId(),
- mSource, displayId, policyFlags,
+ NotifyMotionArgs releaseArgs(getContext()->getNextId(), when, readTime,
+ getDeviceId(), mSource, displayId, policyFlags,
AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
metaState, buttonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
@@ -393,11 +418,11 @@
}
}
- NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
- policyFlags, motionEventAction, 0, 0, metaState, currentButtonState,
- MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
- &pointerProperties, &pointerCoords, mXPrecision, mYPrecision,
- xCursorPosition, yCursorPosition, downTime,
+ NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+ displayId, policyFlags, motionEventAction, 0, 0, metaState,
+ currentButtonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
+ mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime,
/* videoFrames */ {});
getListener()->notifyMotion(&args);
@@ -406,8 +431,8 @@
while (!pressed.isEmpty()) {
int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit());
buttonState |= actionButton;
- NotifyMotionArgs pressArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
- displayId, policyFlags,
+ NotifyMotionArgs pressArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+ mSource, displayId, policyFlags,
AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0,
metaState, buttonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
@@ -422,9 +447,10 @@
// Send hover move after UP to tell the application that the mouse is hovering now.
if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) {
- NotifyMotionArgs hoverArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0,
- 0, metaState, currentButtonState, MotionClassification::NONE,
+ NotifyMotionArgs hoverArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+ mSource, displayId, policyFlags,
+ AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
+ currentButtonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
&pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
yCursorPosition, downTime, /* videoFrames */ {});
@@ -436,9 +462,10 @@
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
- NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
- metaState, currentButtonState, MotionClassification::NONE,
+ NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+ mSource, displayId, policyFlags,
+ AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
+ currentButtonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
&pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
yCursorPosition, downTime, /* videoFrames */ {});
@@ -447,7 +474,7 @@
}
// Synthesize key up from buttons if needed.
- synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,
+ synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, readTime, getDeviceId(), mSource,
displayId, policyFlags, lastButtonState, currentButtonState);
mCursorMotionAccumulator.finishSync();
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index 05bbb26..9a8ca01 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -114,7 +114,7 @@
void configureParameters();
void dumpParameters(std::string& dump);
- void sync(nsecs_t when);
+ void sync(nsecs_t when, nsecs_t readTime);
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
index a8fe39a..df1acd4 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -56,12 +56,29 @@
return false;
}
-void InputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
- int32_t token) {}
+void InputMapper::vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token) {}
void InputMapper::cancelVibrate(int32_t token) {}
-void InputMapper::cancelTouch(nsecs_t when) {}
+bool InputMapper::isVibrating() {
+ return false;
+}
+
+std::vector<int32_t> InputMapper::getVibratorIds() {
+ return {};
+}
+
+void InputMapper::cancelTouch(nsecs_t when, nsecs_t readTime) {}
+
+bool InputMapper::enableSensor(InputDeviceSensorType sensorType,
+ std::chrono::microseconds samplingPeriod,
+ std::chrono::microseconds maxBatchReportLatency) {
+ return true;
+}
+
+void InputMapper::disableSensor(InputDeviceSensorType sensorType) {}
+
+void InputMapper::flushSensor(InputDeviceSensorType sensorType) {}
int32_t InputMapper::getMetaState() {
return 0;
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index 949c7ea..15cff1c 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -22,6 +22,7 @@
#include "InputListener.h"
#include "InputReaderContext.h"
#include "StylusState.h"
+#include "VibrationElement.h"
namespace android {
@@ -62,9 +63,24 @@
virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
const int32_t* keyCodes, uint8_t* outFlags);
- virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token);
+ virtual void vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token);
virtual void cancelVibrate(int32_t token);
- virtual void cancelTouch(nsecs_t when);
+ virtual bool isVibrating();
+ virtual std::vector<int32_t> getVibratorIds();
+ virtual void cancelTouch(nsecs_t when, nsecs_t readTime);
+ virtual bool enableSensor(InputDeviceSensorType sensorType,
+ std::chrono::microseconds samplingPeriod,
+ std::chrono::microseconds maxBatchReportLatency);
+ virtual void disableSensor(InputDeviceSensorType sensorType);
+ virtual void flushSensor(InputDeviceSensorType sensorType);
+
+ virtual std::optional<int32_t> getBatteryCapacity() { return std::nullopt; }
+ virtual std::optional<int32_t> getBatteryStatus() { return std::nullopt; }
+
+ virtual bool setLightColor(int32_t lightId, int32_t color) { return true; }
+ virtual bool setLightPlayerId(int32_t lightId, int32_t playerId) { return true; }
+ virtual std::optional<int32_t> getLightColor(int32_t lightId) { return std::nullopt; }
+ virtual std::optional<int32_t> getLightPlayerId(int32_t lightId) { return std::nullopt; }
virtual int32_t getMetaState();
virtual void updateMetaState(int32_t keyCode);
@@ -72,6 +88,7 @@
virtual void updateExternalStylusState(const StylusState& state);
virtual std::optional<int32_t> getAssociatedDisplayId() { return std::nullopt; }
+ virtual void updateLedState(bool reset) {}
protected:
InputDeviceContext& mDeviceContext;
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index 030a846..0dc312e 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -32,8 +32,8 @@
void JoystickInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
InputMapper::populateDeviceInfo(info);
- for (size_t i = 0; i < mAxes.size(); i++) {
- const Axis& axis = mAxes.valueAt(i);
+ for (std::pair<const int32_t, Axis>& pair : mAxes) {
+ const Axis& axis = pair.second;
addMotionRange(axis.axisInfo.axis, axis, info);
if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
@@ -72,17 +72,15 @@
dump += INDENT2 "Joystick Input Mapper:\n";
dump += INDENT3 "Axes:\n";
- size_t numAxes = mAxes.size();
- for (size_t i = 0; i < numAxes; i++) {
- const Axis& axis = mAxes.valueAt(i);
- const char* label = getAxisLabel(axis.axisInfo.axis);
+ for (const auto& [rawAxis, axis] : mAxes) {
+ const char* label = InputEventLookup::getAxisLabel(axis.axisInfo.axis);
if (label) {
dump += StringPrintf(INDENT4 "%s", label);
} else {
dump += StringPrintf(INDENT4 "%d", axis.axisInfo.axis);
}
if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
- label = getAxisLabel(axis.axisInfo.highAxis);
+ label = InputEventLookup::getAxisLabel(axis.axisInfo.highAxis);
if (label) {
dump += StringPrintf(" / %s (split at %d)", label, axis.axisInfo.splitValue);
} else {
@@ -100,7 +98,7 @@
axis.scale, axis.offset, axis.highScale, axis.highOffset);
dump += StringPrintf(INDENT4 " rawAxis=%d, rawMin=%d, rawMax=%d, "
"rawFlat=%d, rawFuzz=%d, rawResolution=%d\n",
- mAxes.keyAt(i), axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue,
+ rawAxis, axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue,
axis.rawAxisInfo.flat, axis.rawAxisInfo.fuzz,
axis.rawAxisInfo.resolution);
}
@@ -113,8 +111,8 @@
if (!changes) { // first time only
// Collect all axes.
for (int32_t abs = 0; abs <= ABS_MAX; abs++) {
- if (!(getAbsAxisUsage(abs, getDeviceContext().getDeviceClasses()) &
- INPUT_DEVICE_CLASS_JOYSTICK)) {
+ if (!(getAbsAxisUsage(abs, getDeviceContext().getDeviceClasses())
+ .test(InputDeviceClass::JOYSTICK))) {
continue; // axis must be claimed by a different device
}
@@ -123,43 +121,14 @@
if (rawAxisInfo.valid) {
// Map axis.
AxisInfo axisInfo;
- bool explicitlyMapped = !getDeviceContext().mapAxis(abs, &axisInfo);
+ const bool explicitlyMapped = !getDeviceContext().mapAxis(abs, &axisInfo);
+
if (!explicitlyMapped) {
// Axis is not explicitly mapped, will choose a generic axis later.
axisInfo.mode = AxisInfo::MODE_NORMAL;
axisInfo.axis = -1;
}
-
- // Apply flat override.
- int32_t rawFlat =
- axisInfo.flatOverride < 0 ? rawAxisInfo.flat : axisInfo.flatOverride;
-
- // Calculate scaling factors and limits.
- Axis axis;
- if (axisInfo.mode == AxisInfo::MODE_SPLIT) {
- float scale = 1.0f / (axisInfo.splitValue - rawAxisInfo.minValue);
- float highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue);
- axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, 0.0f, highScale,
- 0.0f, 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
- rawAxisInfo.resolution * scale);
- } else if (isCenteredAxis(axisInfo.axis)) {
- float scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
- float offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale;
- axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, offset, scale,
- offset, -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
- rawAxisInfo.resolution * scale);
- } else {
- float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
- axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, 0.0f, scale,
- 0.0f, 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
- rawAxisInfo.resolution * scale);
- }
-
- // To eliminate noise while the joystick is at rest, filter out small variations
- // in axis values up front.
- axis.filter = axis.fuzz ? axis.fuzz : axis.flat * 0.25f;
-
- mAxes.add(abs, axis);
+ mAxes.insert({abs, createAxis(axisInfo, rawAxisInfo, explicitlyMapped)});
}
}
@@ -174,9 +143,8 @@
// Assign generic axis ids to remaining axes.
int32_t nextGenericAxisId = AMOTION_EVENT_AXIS_GENERIC_1;
- size_t numAxes = mAxes.size();
- for (size_t i = 0; i < numAxes; i++) {
- Axis& axis = mAxes.editValueAt(i);
+ for (auto it = mAxes.begin(); it != mAxes.end(); /*increment it inside loop*/) {
+ Axis& axis = it->second;
if (axis.axisInfo.axis < 0) {
while (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16 &&
haveAxis(nextGenericAxisId)) {
@@ -189,19 +157,57 @@
} else {
ALOGI("Ignoring joystick '%s' axis %d because all of the generic axis ids "
"have already been assigned to other axes.",
- getDeviceName().c_str(), mAxes.keyAt(i));
- mAxes.removeItemsAt(i--);
- numAxes -= 1;
+ getDeviceName().c_str(), it->first);
+ it = mAxes.erase(it);
+ continue;
}
}
+ it++;
}
}
}
+JoystickInputMapper::Axis JoystickInputMapper::createAxis(const AxisInfo& axisInfo,
+ const RawAbsoluteAxisInfo& rawAxisInfo,
+ bool explicitlyMapped) {
+ // Apply flat override.
+ int32_t rawFlat = axisInfo.flatOverride < 0 ? rawAxisInfo.flat : axisInfo.flatOverride;
+
+ float scale = std::numeric_limits<float>::signaling_NaN();
+ float highScale = std::numeric_limits<float>::signaling_NaN();
+ float highOffset = 0;
+ float offset = 0;
+ float min = 0;
+ // Calculate scaling factors and limits.
+ if (axisInfo.mode == AxisInfo::MODE_SPLIT) {
+ scale = 1.0f / (axisInfo.splitValue - rawAxisInfo.minValue);
+ highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue);
+ } else if (isCenteredAxis(axisInfo.axis)) {
+ scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
+ offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale;
+ highOffset = offset;
+ highScale = scale;
+ min = -1.0f;
+ } else {
+ scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
+ highScale = scale;
+ }
+
+ constexpr float max = 1.0;
+ const float flat = rawFlat * scale;
+ const float fuzz = rawAxisInfo.fuzz * scale;
+ const float resolution = rawAxisInfo.resolution * scale;
+
+ // To eliminate noise while the joystick is at rest, filter out small variations
+ // in axis values up front.
+ const float filter = fuzz ? fuzz : flat * 0.25f;
+ return Axis(rawAxisInfo, axisInfo, explicitlyMapped, scale, offset, highScale, highOffset, min,
+ max, flat, fuzz, resolution, filter);
+}
+
bool JoystickInputMapper::haveAxis(int32_t axisId) {
- size_t numAxes = mAxes.size();
- for (size_t i = 0; i < numAxes; i++) {
- const Axis& axis = mAxes.valueAt(i);
+ for (const std::pair<const int32_t, Axis>& pair : mAxes) {
+ const Axis& axis = pair.second;
if (axis.axisInfo.axis == axisId ||
(axis.axisInfo.mode == AxisInfo::MODE_SPLIT && axis.axisInfo.highAxis == axisId)) {
return true;
@@ -211,14 +217,14 @@
}
void JoystickInputMapper::pruneAxes(bool ignoreExplicitlyMappedAxes) {
- size_t i = mAxes.size();
- while (mAxes.size() > PointerCoords::MAX_AXES && i-- > 0) {
- if (ignoreExplicitlyMappedAxes && mAxes.valueAt(i).explicitlyMapped) {
+ while (mAxes.size() > PointerCoords::MAX_AXES) {
+ auto it = mAxes.begin();
+ if (ignoreExplicitlyMappedAxes && it->second.explicitlyMapped) {
continue;
}
ALOGI("Discarding joystick '%s' axis %d because there are too many axes.",
- getDeviceName().c_str(), mAxes.keyAt(i));
- mAxes.removeItemsAt(i);
+ getDeviceName().c_str(), it->first);
+ mAxes.erase(it);
}
}
@@ -243,9 +249,8 @@
void JoystickInputMapper::reset(nsecs_t when) {
// Recenter all axes.
- size_t numAxes = mAxes.size();
- for (size_t i = 0; i < numAxes; i++) {
- Axis& axis = mAxes.editValueAt(i);
+ for (std::pair<const int32_t, Axis>& pair : mAxes) {
+ Axis& axis = pair.second;
axis.resetValue();
}
@@ -255,9 +260,9 @@
void JoystickInputMapper::process(const RawEvent* rawEvent) {
switch (rawEvent->type) {
case EV_ABS: {
- ssize_t index = mAxes.indexOfKey(rawEvent->code);
- if (index >= 0) {
- Axis& axis = mAxes.editValueAt(index);
+ auto it = mAxes.find(rawEvent->code);
+ if (it != mAxes.end()) {
+ Axis& axis = it->second;
float newValue, highNewValue;
switch (axis.axisInfo.mode) {
case AxisInfo::MODE_INVERT:
@@ -294,14 +299,14 @@
case EV_SYN:
switch (rawEvent->code) {
case SYN_REPORT:
- sync(rawEvent->when, false /*force*/);
+ sync(rawEvent->when, rawEvent->readTime, false /*force*/);
break;
}
break;
}
}
-void JoystickInputMapper::sync(nsecs_t when, bool force) {
+void JoystickInputMapper::sync(nsecs_t when, nsecs_t readTime, bool force) {
if (!filterAxes(force)) {
return;
}
@@ -317,9 +322,8 @@
PointerCoords pointerCoords;
pointerCoords.clear();
- size_t numAxes = mAxes.size();
- for (size_t i = 0; i < numAxes; i++) {
- const Axis& axis = mAxes.valueAt(i);
+ for (std::pair<const int32_t, Axis>& pair : mAxes) {
+ const Axis& axis = pair.second;
setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.axis, axis.currentValue);
if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.highAxis,
@@ -333,9 +337,10 @@
// TODO: Use the input device configuration to control this behavior more finely.
uint32_t policyFlags = 0;
- NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), AINPUT_SOURCE_JOYSTICK,
- ADISPLAY_ID_NONE, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
- buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+ NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(),
+ AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, policyFlags,
+ AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
&pointerProperties, &pointerCoords, 0, 0,
AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
@@ -357,9 +362,8 @@
bool JoystickInputMapper::filterAxes(bool force) {
bool atLeastOneSignificantChange = force;
- size_t numAxes = mAxes.size();
- for (size_t i = 0; i < numAxes; i++) {
- Axis& axis = mAxes.editValueAt(i);
+ for (std::pair<const int32_t, Axis>& pair : mAxes) {
+ Axis& axis = pair.second;
if (force ||
hasValueChangedSignificantly(axis.filter, axis.newValue, axis.currentValue, axis.min,
axis.max)) {
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h
index 823a096..bba95ad 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.h
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h
@@ -36,6 +36,26 @@
private:
struct Axis {
+ explicit Axis(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo,
+ bool explicitlyMapped, float scale, float offset, float highScale,
+ float highOffset, float min, float max, float flat, float fuzz,
+ float resolution, float filter)
+ : rawAxisInfo(rawAxisInfo),
+ axisInfo(axisInfo),
+ explicitlyMapped(explicitlyMapped),
+ scale(scale),
+ offset(offset),
+ highScale(highScale),
+ highOffset(highOffset),
+ min(min),
+ max(max),
+ flat(flat),
+ fuzz(fuzz),
+ resolution(resolution),
+ filter(filter) {
+ resetValue();
+ }
+
RawAbsoluteAxisInfo rawAxisInfo;
AxisInfo axisInfo;
@@ -58,26 +78,6 @@
float highCurrentValue; // current value of high split
float highNewValue; // most recent value of high split
- void initialize(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo,
- bool explicitlyMapped, float scale, float offset, float highScale,
- float highOffset, float min, float max, float flat, float fuzz,
- float resolution) {
- this->rawAxisInfo = rawAxisInfo;
- this->axisInfo = axisInfo;
- this->explicitlyMapped = explicitlyMapped;
- this->scale = scale;
- this->offset = offset;
- this->highScale = highScale;
- this->highOffset = highOffset;
- this->min = min;
- this->max = max;
- this->flat = flat;
- this->fuzz = fuzz;
- this->resolution = resolution;
- this->filter = 0;
- resetValue();
- }
-
void resetValue() {
this->currentValue = 0;
this->newValue = 0;
@@ -86,10 +86,13 @@
}
};
- // Axes indexed by raw ABS_* axis index.
- KeyedVector<int32_t, Axis> mAxes;
+ static Axis createAxis(const AxisInfo& AxisInfo, const RawAbsoluteAxisInfo& rawAxisInfo,
+ bool explicitlyMapped);
- void sync(nsecs_t when, bool force);
+ // Axes indexed by raw ABS_* axis index.
+ std::unordered_map<int32_t, Axis> mAxes;
+
+ void sync(nsecs_t when, nsecs_t readTime, bool force);
bool haveAxis(int32_t axisId);
void pruneAxes(bool ignoreExplicitlyMappedAxes);
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index e009221..104d087 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -14,7 +14,9 @@
* limitations under the License.
*/
+// clang-format off
#include "../Macros.h"
+// clang-format on
#include "KeyboardInputMapper.h"
@@ -130,15 +132,13 @@
std::optional<DisplayViewport> KeyboardInputMapper::findViewport(
nsecs_t when, const InputReaderConfiguration* config) {
- const std::optional<uint8_t> displayPort = getDeviceContext().getAssociatedDisplayPort();
- if (displayPort) {
- // Find the viewport that contains the same port
+ if (getDeviceContext().getAssociatedViewport()) {
return getDeviceContext().getAssociatedViewport();
}
// No associated display defined, try to find default display if orientationAware.
if (mParameters.orientationAware) {
- return config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ return config->getDisplayViewportByType(ViewportType::INTERNAL);
}
return std::nullopt;
@@ -214,7 +214,8 @@
mCurrentHidUsage = 0;
if (isKeyboardOrGamepadKey(scanCode)) {
- processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode);
+ processKey(rawEvent->when, rawEvent->readTime, rawEvent->value != 0, scanCode,
+ usageCode);
}
break;
}
@@ -267,7 +268,8 @@
return false;
}
-void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode) {
+void KeyboardInputMapper::processKey(nsecs_t when, nsecs_t readTime, bool down, int32_t scanCode,
+ int32_t usageCode) {
int32_t keyCode;
int32_t keyMetaState;
uint32_t policyFlags;
@@ -297,7 +299,7 @@
return;
}
if (policyFlags & POLICY_FLAG_GESTURE) {
- getDeviceContext().cancelTouch(when);
+ getDeviceContext().cancelTouch(when, readTime);
}
KeyDown keyDown;
@@ -348,8 +350,9 @@
policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
}
- NotifyKeyArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, getDisplayId(),
- policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
+ NotifyKeyArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+ getDisplayId(), policyFlags,
+ down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
getListener()->notifyKey(&args);
}
@@ -388,11 +391,14 @@
bool KeyboardInputMapper::updateMetaStateIfNeeded(int32_t keyCode, bool down) {
int32_t oldMetaState = mMetaState;
int32_t newMetaState = android::updateMetaState(keyCode, down, oldMetaState);
- bool metaStateChanged = oldMetaState != newMetaState;
+ int32_t metaStateChanged = oldMetaState ^ newMetaState;
if (metaStateChanged) {
mMetaState = newMetaState;
- updateLedState(false);
-
+ constexpr int32_t allLedMetaState =
+ AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON;
+ if ((metaStateChanged & allLedMetaState) != 0) {
+ getContext()->updateLedMetaState(newMetaState & allLedMetaState);
+ }
getContext()->updateGlobalMetaState();
}
@@ -413,6 +419,26 @@
}
void KeyboardInputMapper::updateLedState(bool reset) {
+ mMetaState |= getContext()->getLedMetaState();
+
+ constexpr int32_t META_NUM = 3;
+ const std::array<int32_t, META_NUM> keyCodes = {AKEYCODE_CAPS_LOCK, AKEYCODE_NUM_LOCK,
+ AKEYCODE_SCROLL_LOCK};
+ const std::array<int32_t, META_NUM> metaCodes = {AMETA_CAPS_LOCK_ON, AMETA_NUM_LOCK_ON,
+ AMETA_SCROLL_LOCK_ON};
+ std::array<uint8_t, META_NUM> flags = {0, 0, 0};
+ bool hasKeyLayout =
+ getDeviceContext().markSupportedKeyCodes(META_NUM, keyCodes.data(), flags.data());
+ // If the device doesn't have the physical meta key it shouldn't generate the corresponding
+ // meta state.
+ if (hasKeyLayout) {
+ for (int i = 0; i < META_NUM; i++) {
+ if (!flags[i]) {
+ mMetaState &= ~metaCodes[i];
+ }
+ }
+ }
+
updateLedStateForModifier(mCapsLockLedState, ALED_CAPS_LOCK, AMETA_CAPS_LOCK_ON, reset);
updateLedStateForModifier(mNumLockLedState, ALED_NUM_LOCK, AMETA_NUM_LOCK_ON, reset);
updateLedStateForModifier(mScrollLockLedState, ALED_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, reset);
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 0bdeded..ca41712 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -42,6 +42,7 @@
virtual int32_t getMetaState() override;
virtual void updateMetaState(int32_t keyCode) override;
virtual std::optional<int32_t> getAssociatedDisplayId() override;
+ virtual void updateLedState(bool reset);
private:
// The current viewport.
@@ -85,7 +86,7 @@
bool isKeyboardOrGamepadKey(int32_t scanCode);
bool isMediaKey(int32_t keyCode);
- void processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode);
+ void processKey(nsecs_t when, nsecs_t readTime, bool down, int32_t scanCode, int32_t usageCode);
bool updateMetaStateIfNeeded(int32_t keyCode, bool down);
@@ -93,7 +94,6 @@
void resetLedState();
void initializeLedState(LedState& ledState, int32_t led);
- void updateLedState(bool reset);
void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset);
std::optional<DisplayViewport> findViewport(nsecs_t when,
const InputReaderConfiguration* config);
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index 0440f49..ca43123 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -268,6 +268,10 @@
if (id) {
outState->rawPointerData.canceledIdBits.markBit(id.value());
}
+#if DEBUG_POINTERS
+ ALOGI("Stop processing slot %zu for it received a palm event from device %s", inIndex,
+ getDeviceName().c_str());
+#endif
continue;
}
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index 9885889..e9d0189 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -14,7 +14,9 @@
* limitations under the License.
*/
+// clang-format off
#include "../Macros.h"
+// clang-format on
#include "RotaryEncoderInputMapper.h"
@@ -66,7 +68,7 @@
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
std::optional<DisplayViewport> internalViewport =
- config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ config->getDisplayViewportByType(ViewportType::INTERNAL);
if (internalViewport) {
mOrientation = internalViewport->orientation;
} else {
@@ -85,11 +87,11 @@
mRotaryEncoderScrollAccumulator.process(rawEvent);
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
- sync(rawEvent->when);
+ sync(rawEvent->when, rawEvent->readTime);
}
}
-void RotaryEncoderInputMapper::sync(nsecs_t when) {
+void RotaryEncoderInputMapper::sync(nsecs_t when, nsecs_t readTime) {
PointerCoords pointerCoords;
pointerCoords.clear();
@@ -119,9 +121,9 @@
int32_t metaState = getContext()->getGlobalMetaState();
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor);
- NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
- metaState, /* buttonState */ 0, MotionClassification::NONE,
+ NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+ mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0,
+ 0, metaState, /* buttonState */ 0, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
&pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
index 7a77b12..e0c9404 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
@@ -42,7 +42,7 @@
float mScalingFactor;
int32_t mOrientation;
- void sync(nsecs_t when);
+ void sync(nsecs_t when, nsecs_t readTime);
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
new file mode 100644
index 0000000..7ac2dec
--- /dev/null
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2020 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 <locale>
+
+#include "../Macros.h"
+
+#include "SensorInputMapper.h"
+
+// Log detailed debug messages about each sensor event notification to the dispatcher.
+constexpr bool DEBUG_SENSOR_EVENT_DETAILS = false;
+
+namespace android {
+
+// Mask for the LSB 2nd, 3rd and fourth bits.
+constexpr int REPORTING_MODE_MASK = 0xE;
+constexpr int REPORTING_MODE_SHIFT = 1;
+constexpr float GRAVITY_MS2_UNIT = 9.80665f;
+constexpr float DEGREE_RADIAN_UNIT = 0.0174533f;
+
+/* Convert the sensor data from Linux to Android
+ * Linux accelerometer unit is per g, Android unit is m/s^2
+ * Linux gyroscope unit is degree/second, Android unit is radians/second
+ */
+static void convertFromLinuxToAndroid(std::vector<float>& values,
+ InputDeviceSensorType sensorType) {
+ for (size_t i = 0; i < values.size(); i++) {
+ switch (sensorType) {
+ case InputDeviceSensorType::ACCELEROMETER:
+ values[i] *= GRAVITY_MS2_UNIT;
+ break;
+ case InputDeviceSensorType::GYROSCOPE:
+ values[i] *= DEGREE_RADIAN_UNIT;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+SensorInputMapper::SensorInputMapper(InputDeviceContext& deviceContext)
+ : InputMapper(deviceContext) {}
+
+SensorInputMapper::~SensorInputMapper() {}
+
+uint32_t SensorInputMapper::getSources() {
+ return AINPUT_SOURCE_SENSOR;
+}
+
+template <typename T>
+bool SensorInputMapper::tryGetProperty(std::string keyName, T& outValue) {
+ const auto& config = getDeviceContext().getConfiguration();
+ return config.tryGetProperty(String8(keyName.c_str()), outValue);
+}
+
+void SensorInputMapper::parseSensorConfiguration(InputDeviceSensorType sensorType, int32_t absCode,
+ int32_t sensorDataIndex, const Axis& axis) {
+ auto it = mSensors.find(sensorType);
+ if (it == mSensors.end()) {
+ Sensor sensor = createSensor(sensorType, axis);
+ sensor.dataVec[sensorDataIndex] = absCode;
+ mSensors.emplace(sensorType, sensor);
+ } else {
+ it->second.dataVec[sensorDataIndex] = absCode;
+ }
+}
+
+void SensorInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+ InputMapper::populateDeviceInfo(info);
+
+ for (const auto& [sensorType, sensor] : mSensors) {
+ info->addSensorInfo(sensor.sensorInfo);
+ info->setHasSensor(true);
+ }
+}
+
+void SensorInputMapper::dump(std::string& dump) {
+ dump += INDENT2 "Sensor Input Mapper:\n";
+ dump += StringPrintf(INDENT3 " isDeviceEnabled %d\n", getDeviceContext().isDeviceEnabled());
+ dump += StringPrintf(INDENT3 " mHasHardwareTimestamp %d\n", mHasHardwareTimestamp);
+ dump += INDENT3 "Sensors:\n";
+ for (const auto& [sensorType, sensor] : mSensors) {
+ dump += StringPrintf(INDENT4 "%s\n", NamedEnum::string(sensorType).c_str());
+ dump += StringPrintf(INDENT5 "enabled: %d\n", sensor.enabled);
+ dump += StringPrintf(INDENT5 "samplingPeriod: %lld\n", sensor.samplingPeriod.count());
+ dump += StringPrintf(INDENT5 "maxBatchReportLatency: %lld\n",
+ sensor.maxBatchReportLatency.count());
+ dump += StringPrintf(INDENT5 "maxRange: %f\n", sensor.sensorInfo.maxRange);
+ dump += StringPrintf(INDENT5 "power: %f\n", sensor.sensorInfo.power);
+ for (ssize_t i = 0; i < SENSOR_VEC_LEN; i++) {
+ int32_t rawAxis = sensor.dataVec[i];
+ dump += StringPrintf(INDENT5 "[%zd]: rawAxis: %d \n", i, rawAxis);
+ const auto it = mAxes.find(rawAxis);
+ if (it != mAxes.end()) {
+ const Axis& axis = it->second;
+ dump += StringPrintf(INDENT5 " min=%0.5f, max=%0.5f, flat=%0.5f, fuzz=%0.5f,"
+ "resolution=%0.5f\n",
+ axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution);
+ dump += StringPrintf(INDENT5 " scale=%0.5f, offset=%0.5f\n", axis.scale,
+ axis.offset);
+ dump += StringPrintf(INDENT5 " rawMin=%d, rawMax=%d, "
+ "rawFlat=%d, rawFuzz=%d, rawResolution=%d\n",
+ axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue,
+ axis.rawAxisInfo.flat, axis.rawAxisInfo.fuzz,
+ axis.rawAxisInfo.resolution);
+ }
+ }
+ }
+}
+
+void SensorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
+ uint32_t changes) {
+ InputMapper::configure(when, config, changes);
+
+ if (!changes) { // first time only
+ mDeviceEnabled = true;
+ // Check if device has MSC_TIMESTAMP event.
+ mHasHardwareTimestamp = getDeviceContext().hasMscEvent(MSC_TIMESTAMP);
+ // Collect all axes.
+ for (int32_t abs = ABS_X; abs <= ABS_MAX; abs++) {
+ // axis must be claimed by sensor class device
+ if (!(getAbsAxisUsage(abs, getDeviceContext().getDeviceClasses())
+ .test(InputDeviceClass::SENSOR))) {
+ continue;
+ }
+ RawAbsoluteAxisInfo rawAxisInfo;
+ getAbsoluteAxisInfo(abs, &rawAxisInfo);
+ if (rawAxisInfo.valid) {
+ AxisInfo axisInfo;
+ // Axis doesn't need to be mapped, as sensor mapper doesn't generate any motion
+ // input events
+ axisInfo.mode = AxisInfo::MODE_NORMAL;
+ axisInfo.axis = -1;
+ // Check key layout map for sensor data mapping to axes
+ auto ret = getDeviceContext().mapSensor(abs);
+ if (ret.ok()) {
+ InputDeviceSensorType sensorType = (*ret).first;
+ int32_t sensorDataIndex = (*ret).second;
+ const Axis& axis = createAxis(axisInfo, rawAxisInfo);
+ parseSensorConfiguration(sensorType, abs, sensorDataIndex, axis);
+
+ mAxes.insert({abs, axis});
+ }
+ }
+ }
+ }
+}
+
+SensorInputMapper::Axis SensorInputMapper::createAxis(const AxisInfo& axisInfo,
+ const RawAbsoluteAxisInfo& rawAxisInfo) {
+ // Apply flat override.
+ int32_t rawFlat = axisInfo.flatOverride < 0 ? rawAxisInfo.flat : axisInfo.flatOverride;
+
+ float scale = std::numeric_limits<float>::signaling_NaN();
+ float offset = 0;
+
+ // resolution is 1 of sensor's unit. For accelerometer, it is G, for gyroscope,
+ // it is degree/s.
+ scale = 1.0f / rawAxisInfo.resolution;
+ offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale;
+
+ const float max = rawAxisInfo.maxValue / rawAxisInfo.resolution;
+ const float min = rawAxisInfo.minValue / rawAxisInfo.resolution;
+ const float flat = rawFlat * scale;
+ const float fuzz = rawAxisInfo.fuzz * scale;
+ const float resolution = rawAxisInfo.resolution;
+
+ // To eliminate noise while the Sensor is at rest, filter out small variations
+ // in axis values up front.
+ const float filter = fuzz ? fuzz : flat * 0.25f;
+ return Axis(rawAxisInfo, axisInfo, scale, offset, min, max, flat, fuzz, resolution, filter);
+}
+
+void SensorInputMapper::reset(nsecs_t when) {
+ // Recenter all axes.
+ for (std::pair<const int32_t, Axis>& pair : mAxes) {
+ Axis& axis = pair.second;
+ axis.resetValue();
+ }
+ mHardwareTimestamp = 0;
+ mPrevMscTime = 0;
+ InputMapper::reset(when);
+}
+
+SensorInputMapper::Sensor SensorInputMapper::createSensor(InputDeviceSensorType sensorType,
+ const Axis& axis) {
+ InputDeviceIdentifier identifier = getDeviceContext().getDeviceIdentifier();
+ // Sensor Id will be assigned to device Id to distinguish same sensor from multiple input
+ // devices, in such a way that the sensor Id will be same as input device Id.
+ // The sensorType is to distinguish different sensors within one device.
+ // One input device can only have 1 sensor for each sensor Type.
+ InputDeviceSensorInfo sensorInfo(identifier.name, std::to_string(identifier.vendor),
+ identifier.version, sensorType,
+ InputDeviceSensorAccuracy::ACCURACY_HIGH,
+ axis.max /* maxRange */, axis.scale /* resolution */,
+ 0.0f /* power */, 0 /* minDelay */,
+ 0 /* fifoReservedEventCount */, 0 /* fifoMaxEventCount */,
+ NamedEnum::string(sensorType), 0 /* maxDelay */, 0 /* flags */,
+ getDeviceId());
+
+ std::string prefix = "sensor." + NamedEnum::string(sensorType);
+ transform(prefix.begin(), prefix.end(), prefix.begin(), ::tolower);
+
+ int32_t reportingMode = 0;
+ if (!tryGetProperty(prefix + ".reportingMode", reportingMode)) {
+ sensorInfo.flags |= (reportingMode & REPORTING_MODE_MASK) << REPORTING_MODE_SHIFT;
+ }
+
+ tryGetProperty(prefix + ".maxDelay", sensorInfo.maxDelay);
+
+ tryGetProperty(prefix + ".minDelay", sensorInfo.minDelay);
+
+ tryGetProperty(prefix + ".power", sensorInfo.power);
+
+ tryGetProperty(prefix + ".fifoReservedEventCount", sensorInfo.fifoReservedEventCount);
+
+ tryGetProperty(prefix + ".fifoMaxEventCount", sensorInfo.fifoMaxEventCount);
+
+ return Sensor(sensorInfo);
+}
+
+void SensorInputMapper::processHardWareTimestamp(nsecs_t evTime, int32_t mscTime) {
+ // Since MSC_TIMESTAMP initial state is different from the system time, we
+ // calculate the difference between two MSC_TIMESTAMP events, and use that
+ // to calculate the system time that should be tagged on the event.
+ // if the first time MSC_TIMESTAMP, store it
+ // else calculate difference between previous and current MSC_TIMESTAMP
+ if (mPrevMscTime == 0) {
+ mHardwareTimestamp = evTime;
+ if (DEBUG_SENSOR_EVENT_DETAILS) {
+ ALOGD("Initialize hardware timestamp = %" PRId64, mHardwareTimestamp);
+ }
+ } else {
+ // Calculate the difference between current msc_timestamp and
+ // previous msc_timestamp, including when msc_timestamp wraps around.
+ uint32_t timeDiff = (mPrevMscTime > static_cast<uint32_t>(mscTime))
+ ? (UINT32_MAX - mPrevMscTime + static_cast<uint32_t>(mscTime + 1))
+ : (static_cast<uint32_t>(mscTime) - mPrevMscTime);
+
+ mHardwareTimestamp += timeDiff * 1000LL;
+ }
+ mPrevMscTime = static_cast<uint32_t>(mscTime);
+}
+
+void SensorInputMapper::process(const RawEvent* rawEvent) {
+ switch (rawEvent->type) {
+ case EV_ABS: {
+ auto it = mAxes.find(rawEvent->code);
+ if (it != mAxes.end()) {
+ Axis& axis = it->second;
+ axis.newValue = rawEvent->value * axis.scale + axis.offset;
+ }
+ break;
+ }
+
+ case EV_SYN:
+ switch (rawEvent->code) {
+ case SYN_REPORT:
+ for (std::pair<const int32_t, Axis>& pair : mAxes) {
+ Axis& axis = pair.second;
+ axis.currentValue = axis.newValue;
+ }
+ sync(rawEvent->when, false /*force*/);
+ break;
+ }
+ break;
+
+ case EV_MSC:
+ switch (rawEvent->code) {
+ case MSC_TIMESTAMP:
+ // hardware timestamp is nano seconds
+ processHardWareTimestamp(rawEvent->when, rawEvent->value);
+ break;
+ }
+ }
+}
+
+bool SensorInputMapper::setSensorEnabled(InputDeviceSensorType sensorType, bool enabled) {
+ auto it = mSensors.find(sensorType);
+ if (it == mSensors.end()) {
+ return false;
+ }
+
+ it->second.enabled = enabled;
+ if (!enabled) {
+ it->second.resetValue();
+ }
+
+ /* Currently we can't enable/disable sensors individually. Enabling any sensor will enable
+ * the device
+ */
+ mDeviceEnabled = false;
+ for (const auto& [sensorType, sensor] : mSensors) {
+ // If any sensor is on we will turn on the device.
+ if (sensor.enabled) {
+ mDeviceEnabled = true;
+ break;
+ }
+ }
+ return true;
+}
+
+void SensorInputMapper::flushSensor(InputDeviceSensorType sensorType) {
+ auto it = mSensors.find(sensorType);
+ if (it == mSensors.end()) {
+ return;
+ }
+ auto& sensor = it->second;
+ sensor.lastSampleTimeNs = 0;
+ for (size_t i = 0; i < SENSOR_VEC_LEN; i++) {
+ int32_t abs = sensor.dataVec[i];
+ auto itAxis = mAxes.find(abs);
+ if (itAxis != mAxes.end()) {
+ Axis& axis = itAxis->second;
+ axis.resetValue();
+ }
+ }
+}
+
+bool SensorInputMapper::enableSensor(InputDeviceSensorType sensorType,
+ std::chrono::microseconds samplingPeriod,
+ std::chrono::microseconds maxBatchReportLatency) {
+ if (DEBUG_SENSOR_EVENT_DETAILS) {
+ ALOGD("Enable Sensor %s samplingPeriod %lld maxBatchReportLatency %lld",
+ NamedEnum::string(sensorType).c_str(), samplingPeriod.count(),
+ maxBatchReportLatency.count());
+ }
+
+ if (!setSensorEnabled(sensorType, true /* enabled */)) {
+ return false;
+ }
+
+ // Enable device
+ if (mDeviceEnabled) {
+ getDeviceContext().enableDevice();
+ }
+
+ // We know the sensor exists now, update the sampling period and batch report latency.
+ auto it = mSensors.find(sensorType);
+ it->second.samplingPeriod =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(samplingPeriod);
+ it->second.maxBatchReportLatency =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(maxBatchReportLatency);
+ return true;
+}
+
+void SensorInputMapper::disableSensor(InputDeviceSensorType sensorType) {
+ if (DEBUG_SENSOR_EVENT_DETAILS) {
+ ALOGD("Disable Sensor %s", NamedEnum::string(sensorType).c_str());
+ }
+
+ if (!setSensorEnabled(sensorType, false /* enabled */)) {
+ return;
+ }
+
+ // Disable device
+ if (!mDeviceEnabled) {
+ mHardwareTimestamp = 0;
+ mPrevMscTime = 0;
+ getDeviceContext().disableDevice();
+ }
+}
+
+void SensorInputMapper::sync(nsecs_t when, bool force) {
+ for (auto& [sensorType, sensor] : mSensors) {
+ // Skip if sensor not enabled
+ if (!sensor.enabled) {
+ continue;
+ }
+ std::vector<float> values;
+ for (ssize_t i = 0; i < SENSOR_VEC_LEN; i++) {
+ int32_t abs = sensor.dataVec[i];
+ auto it = mAxes.find(abs);
+ if (it != mAxes.end()) {
+ const Axis& axis = it->second;
+ values.push_back(axis.currentValue);
+ }
+ }
+
+ nsecs_t timestamp = mHasHardwareTimestamp ? mHardwareTimestamp : when;
+ if (DEBUG_SENSOR_EVENT_DETAILS) {
+ ALOGD("Sensor %s timestamp %" PRIu64 " values [%f %f %f]",
+ NamedEnum::string(sensorType).c_str(), timestamp, values[0], values[1],
+ values[2]);
+ }
+ if (sensor.lastSampleTimeNs.has_value() &&
+ timestamp - sensor.lastSampleTimeNs.value() < sensor.samplingPeriod.count()) {
+ if (DEBUG_SENSOR_EVENT_DETAILS) {
+ ALOGD("Sensor %s Skip a sample.", NamedEnum::string(sensorType).c_str());
+ }
+ } else {
+ // Convert to Android unit
+ convertFromLinuxToAndroid(values, sensorType);
+ // Notify dispatcher for sensor event
+ NotifySensorArgs args(getContext()->getNextId(), when, getDeviceId(),
+ AINPUT_SOURCE_SENSOR, sensorType, sensor.sensorInfo.accuracy,
+ sensor.accuracy !=
+ sensor.sensorInfo.accuracy /* accuracyChanged */,
+ timestamp /* hwTimestamp */, values);
+
+ getListener()->notifySensor(&args);
+ sensor.lastSampleTimeNs = timestamp;
+ sensor.accuracy = sensor.sensorInfo.accuracy;
+ }
+ }
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.h b/services/inputflinger/reader/mapper/SensorInputMapper.h
new file mode 100644
index 0000000..1797fe3
--- /dev/null
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef _UI_INPUTREADER_SENSOR_INPUT_MAPPER_H
+#define _UI_INPUTREADER_SENSOR_INPUT_MAPPER_H
+
+#include "InputMapper.h"
+
+namespace android {
+// sensor data vector length
+static constexpr ssize_t SENSOR_VEC_LEN = 3;
+
+class SensorInputMapper : public InputMapper {
+public:
+ explicit SensorInputMapper(InputDeviceContext& deviceContext);
+ ~SensorInputMapper() override;
+
+ uint32_t getSources() override;
+ void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+ void dump(std::string& dump) override;
+ void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) override;
+ void reset(nsecs_t when) override;
+ void process(const RawEvent* rawEvent) override;
+ bool enableSensor(InputDeviceSensorType sensorType, std::chrono::microseconds samplingPeriod,
+ std::chrono::microseconds maxBatchReportLatency) override;
+ void disableSensor(InputDeviceSensorType sensorType) override;
+ void flushSensor(InputDeviceSensorType sensorType) override;
+
+private:
+ struct Axis {
+ explicit Axis(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo, float scale,
+ float offset, float min, float max, float flat, float fuzz, float resolution,
+ float filter)
+ : rawAxisInfo(rawAxisInfo),
+ axisInfo(axisInfo),
+ scale(scale),
+ offset(offset),
+ min(min),
+ max(max),
+ flat(flat),
+ fuzz(fuzz),
+ resolution(resolution),
+ filter(filter) {
+ resetValue();
+ }
+
+ RawAbsoluteAxisInfo rawAxisInfo;
+ AxisInfo axisInfo;
+
+ float scale; // scale factor from raw to normalized values
+ float offset; // offset to add after scaling for normalization
+
+ float min; // normalized inclusive minimum
+ float max; // normalized inclusive maximum
+ float flat; // normalized flat region size
+ float fuzz; // normalized error tolerance
+ float resolution; // normalized resolution in units
+
+ float filter; // filter out small variations of this size
+ float currentValue; // current value
+ float newValue; // most recent value
+
+ void resetValue() {
+ this->currentValue = 0;
+ this->newValue = 0;
+ }
+ };
+
+ struct Sensor {
+ explicit Sensor(const InputDeviceSensorInfo& sensorInfo) : sensorInfo(sensorInfo) {
+ resetValue();
+ }
+ bool enabled;
+ InputDeviceSensorAccuracy accuracy;
+ std::chrono::nanoseconds samplingPeriod;
+ std::chrono::nanoseconds maxBatchReportLatency;
+ // last sample time in nano seconds
+ std::optional<nsecs_t> lastSampleTimeNs;
+ InputDeviceSensorInfo sensorInfo;
+ // Sensor X, Y, Z data mapping to abs
+ std::array<int32_t, SENSOR_VEC_LEN> dataVec;
+ void resetValue() {
+ this->enabled = false;
+ this->accuracy = InputDeviceSensorAccuracy::ACCURACY_NONE;
+ this->samplingPeriod = std::chrono::nanoseconds(0);
+ this->maxBatchReportLatency = std::chrono::nanoseconds(0);
+ this->lastSampleTimeNs = std::nullopt;
+ }
+ };
+
+ static Axis createAxis(const AxisInfo& AxisInfo, const RawAbsoluteAxisInfo& rawAxisInfo);
+
+ // Axes indexed by raw ABS_* axis index.
+ std::unordered_map<int32_t, Axis> mAxes;
+
+ // hardware timestamp from MSC_TIMESTAMP
+ nsecs_t mHardwareTimestamp;
+ uint32_t mPrevMscTime;
+
+ bool mDeviceEnabled;
+ // Does device support MSC_TIMESTAMP
+ bool mHasHardwareTimestamp;
+
+ // Sensor list
+ std::unordered_map<InputDeviceSensorType, Sensor> mSensors;
+
+ void sync(nsecs_t when, bool force);
+
+ template <typename T>
+ bool tryGetProperty(std::string keyName, T& outValue);
+
+ void parseSensorConfiguration(InputDeviceSensorType sensorType, int32_t absCode,
+ int32_t sensorDataIndex, const Axis& axis);
+
+ void processHardWareTimestamp(nsecs_t evTime, int32_t evValue);
+
+ Sensor createSensor(InputDeviceSensorType sensorType, const Axis& axis);
+
+ bool setSensorEnabled(InputDeviceSensorType sensorType, bool enabled);
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_SENSOR_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
index a86443d..1843b03 100644
--- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
@@ -17,6 +17,7 @@
#ifndef _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H
#define _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H
+#include <android-base/properties.h>
#include <input/DisplayViewport.h>
#include <stdint.h>
@@ -28,6 +29,26 @@
// --- Static Definitions ---
+// When per-window input rotation is enabled, display transformations such as rotation and
+// projection are part of the input window's transform. This means InputReader should work in the
+// un-rotated coordinate space.
+static bool isPerWindowInputRotationEnabled() {
+ static const bool PER_WINDOW_INPUT_ROTATION =
+ base::GetBoolProperty("persist.debug.per_window_input_rotation", false);
+ return PER_WINDOW_INPUT_ROTATION;
+}
+
+static int32_t getInverseRotation(int32_t orientation) {
+ switch (orientation) {
+ case DISPLAY_ORIENTATION_90:
+ return DISPLAY_ORIENTATION_270;
+ case DISPLAY_ORIENTATION_270:
+ return DISPLAY_ORIENTATION_90;
+ default:
+ return orientation;
+ }
+}
+
static void rotateDelta(int32_t orientation, float* deltaX, float* deltaY) {
float temp;
switch (orientation) {
@@ -59,27 +80,27 @@
}
static void synthesizeButtonKey(InputReaderContext* context, int32_t action, nsecs_t when,
- int32_t deviceId, uint32_t source, int32_t displayId,
- uint32_t policyFlags, int32_t lastButtonState,
+ nsecs_t readTime, int32_t deviceId, uint32_t source,
+ int32_t displayId, uint32_t policyFlags, int32_t lastButtonState,
int32_t currentButtonState, int32_t buttonState, int32_t keyCode) {
if ((action == AKEY_EVENT_ACTION_DOWN && !(lastButtonState & buttonState) &&
(currentButtonState & buttonState)) ||
(action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) &&
!(currentButtonState & buttonState))) {
- NotifyKeyArgs args(context->getNextId(), when, deviceId, source, displayId, policyFlags,
- action, 0, keyCode, 0, context->getGlobalMetaState(), when);
+ NotifyKeyArgs args(context->getNextId(), when, readTime, deviceId, source, displayId,
+ policyFlags, action, 0, keyCode, 0, context->getGlobalMetaState(), when);
context->getListener()->notifyKey(&args);
}
}
static void synthesizeButtonKeys(InputReaderContext* context, int32_t action, nsecs_t when,
- int32_t deviceId, uint32_t source, int32_t displayId,
- uint32_t policyFlags, int32_t lastButtonState,
+ nsecs_t readTime, int32_t deviceId, uint32_t source,
+ int32_t displayId, uint32_t policyFlags, int32_t lastButtonState,
int32_t currentButtonState) {
- synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags,
+ synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId, policyFlags,
lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_BACK,
AKEYCODE_BACK);
- synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags,
+ synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId, policyFlags,
lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_FORWARD,
AKEYCODE_FORWARD);
}
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index cbdb1d0..13712ee 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -18,6 +18,7 @@
#include "../Macros.h"
// clang-format on
+#include <input/NamedEnum.h>
#include "TouchInputMapper.h"
#include "CursorButtonAccumulator.h"
@@ -27,6 +28,30 @@
namespace android {
+namespace {
+
+// Rotates the given point (x, y) by the supplied orientation. The width and height are the
+// dimensions of the surface prior to this rotation being applied.
+void rotatePoint(int32_t orientation, float& x, float& y, int32_t width, int32_t height) {
+ rotateDelta(orientation, &x, &y);
+ switch (orientation) {
+ case DISPLAY_ORIENTATION_90:
+ y += width;
+ break;
+ case DISPLAY_ORIENTATION_180:
+ x += width;
+ y += height;
+ break;
+ case DISPLAY_ORIENTATION_270:
+ x += height;
+ break;
+ default:
+ break;
+ }
+}
+
+} // namespace
+
// --- Constants ---
// Maximum amount of latency to add to touch events while waiting for data from an
@@ -170,6 +195,8 @@
mRawSurfaceHeight(-1),
mSurfaceLeft(0),
mSurfaceTop(0),
+ mSurfaceRight(0),
+ mSurfaceBottom(0),
mPhysicalWidth(-1),
mPhysicalHeight(-1),
mPhysicalLeft(0),
@@ -255,7 +282,8 @@
}
void TouchInputMapper::dump(std::string& dump) {
- dump += StringPrintf(INDENT2 "Touch Input Mapper (mode - %s):\n", modeToString(mDeviceMode));
+ dump += StringPrintf(INDENT2 "Touch Input Mapper (mode - %s):\n",
+ NamedEnum::string(mDeviceMode).c_str());
dumpParameters(dump);
dumpVirtualKeys(dump);
dumpRawPointerAxes(dump);
@@ -343,22 +371,6 @@
}
}
-const char* TouchInputMapper::modeToString(DeviceMode deviceMode) {
- switch (deviceMode) {
- case DeviceMode::DISABLED:
- return "disabled";
- case DeviceMode::DIRECT:
- return "direct";
- case DeviceMode::UNSCALED:
- return "unscaled";
- case DeviceMode::NAVIGATION:
- return "navigation";
- case DeviceMode::POINTER:
- return "pointer";
- }
- return "unknown";
-}
-
void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
uint32_t changes) {
InputMapper::configure(when, config, changes);
@@ -510,33 +522,9 @@
void TouchInputMapper::dumpParameters(std::string& dump) {
dump += INDENT3 "Parameters:\n";
- switch (mParameters.gestureMode) {
- case Parameters::GestureMode::SINGLE_TOUCH:
- dump += INDENT4 "GestureMode: single-touch\n";
- break;
- case Parameters::GestureMode::MULTI_TOUCH:
- dump += INDENT4 "GestureMode: multi-touch\n";
- break;
- default:
- assert(false);
- }
+ dump += INDENT4 "GestureMode: " + NamedEnum::string(mParameters.gestureMode) + "\n";
- switch (mParameters.deviceType) {
- case Parameters::DeviceType::TOUCH_SCREEN:
- dump += INDENT4 "DeviceType: touchScreen\n";
- break;
- case Parameters::DeviceType::TOUCH_PAD:
- dump += INDENT4 "DeviceType: touchPad\n";
- break;
- case Parameters::DeviceType::TOUCH_NAVIGATION:
- dump += INDENT4 "DeviceType: touchNavigation\n";
- break;
- case Parameters::DeviceType::POINTER:
- dump += INDENT4 "DeviceType: pointer\n";
- break;
- default:
- ALOG_ASSERT(false);
- }
+ dump += INDENT4 "DeviceType: " + NamedEnum::string(mParameters.deviceType) + "\n";
dump += StringPrintf(INDENT4 "AssociatedDisplay: hasAssociatedDisplay=%s, isExternal=%s, "
"displayId='%s'\n",
@@ -606,18 +594,18 @@
ViewportType viewportTypeToUse;
if (mParameters.associatedDisplayIsExternal) {
- viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL;
+ viewportTypeToUse = ViewportType::EXTERNAL;
} else {
- viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL;
+ viewportTypeToUse = ViewportType::INTERNAL;
}
std::optional<DisplayViewport> viewport =
mConfig.getDisplayViewportByType(viewportTypeToUse);
- if (!viewport && viewportTypeToUse == ViewportType::VIEWPORT_EXTERNAL) {
+ if (!viewport && viewportTypeToUse == ViewportType::EXTERNAL) {
ALOGW("Input device %s should be associated with external display, "
"fallback to internal one for the external viewport is not found.",
getDeviceName().c_str());
- viewport = mConfig.getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ viewport = mConfig.getDisplayViewportByType(ViewportType::INTERNAL);
}
return viewport;
@@ -645,8 +633,7 @@
if (hasStylus()) {
mSource |= AINPUT_SOURCE_STYLUS;
}
- } else if (mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN &&
- mParameters.hasAssociatedDisplay) {
+ } else if (isTouchScreen()) {
mSource = AINPUT_SOURCE_TOUCHSCREEN;
mDeviceMode = DeviceMode::DIRECT;
if (hasStylus()) {
@@ -683,6 +670,13 @@
return;
}
+ if (!newViewport->isActive) {
+ ALOGI("Disabling %s (device %i) because the associated viewport is not active",
+ getDeviceName().c_str(), getDeviceId());
+ mDeviceMode = DeviceMode::DISABLED;
+ return;
+ }
+
// Raw width and height in the natural orientation.
int32_t rawWidth = mRawPointerAxes.getRawWidth();
int32_t rawHeight = mRawPointerAxes.getRawHeight();
@@ -759,8 +753,20 @@
mSurfaceRight = mSurfaceLeft + naturalLogicalWidth;
mSurfaceBottom = mSurfaceTop + naturalLogicalHeight;
- mSurfaceOrientation =
- mParameters.orientationAware ? mViewport.orientation : DISPLAY_ORIENTATION_0;
+ if (isPerWindowInputRotationEnabled()) {
+ // When per-window input rotation is enabled, InputReader works in the un-rotated
+ // coordinate space, so we don't need to do anything if the device is already
+ // orientation-aware. If the device is not orientation-aware, then we need to apply
+ // the inverse rotation of the display so that when the display rotation is applied
+ // later as a part of the per-window transform, we get the expected screen
+ // coordinates.
+ mSurfaceOrientation = mParameters.orientationAware
+ ? DISPLAY_ORIENTATION_0
+ : getInverseRotation(mViewport.orientation);
+ } else {
+ mSurfaceOrientation = mParameters.orientationAware ? mViewport.orientation
+ : DISPLAY_ORIENTATION_0;
+ }
} else {
mPhysicalWidth = rawWidth;
mPhysicalHeight = rawHeight;
@@ -1046,7 +1052,8 @@
mPointerGestureMaxSwipeWidth = mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal;
// Abort current pointer usages because the state has changed.
- abortPointerUsage(when, 0 /*policyFlags*/);
+ const nsecs_t readTime = when; // synthetic event
+ abortPointerUsage(when, readTime, 0 /*policyFlags*/);
}
// Inform the dispatcher about the changes.
@@ -1436,32 +1443,34 @@
mTouchButtonAccumulator.process(rawEvent);
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
- sync(rawEvent->when);
+ sync(rawEvent->when, rawEvent->readTime);
}
}
-void TouchInputMapper::sync(nsecs_t when) {
- const RawState* last =
- mRawStatesPending.empty() ? &mCurrentRawState : &mRawStatesPending.back();
-
+void TouchInputMapper::sync(nsecs_t when, nsecs_t readTime) {
// Push a new state.
mRawStatesPending.emplace_back();
- RawState* next = &mRawStatesPending.back();
- next->clear();
- next->when = when;
+ RawState& next = mRawStatesPending.back();
+ next.clear();
+ next.when = when;
+ next.readTime = readTime;
// Sync button state.
- next->buttonState =
+ next.buttonState =
mTouchButtonAccumulator.getButtonState() | mCursorButtonAccumulator.getButtonState();
// Sync scroll
- next->rawVScroll = mCursorScrollAccumulator.getRelativeVWheel();
- next->rawHScroll = mCursorScrollAccumulator.getRelativeHWheel();
+ next.rawVScroll = mCursorScrollAccumulator.getRelativeVWheel();
+ next.rawHScroll = mCursorScrollAccumulator.getRelativeHWheel();
mCursorScrollAccumulator.finishSync();
// Sync touch
- syncTouch(when, next);
+ syncTouch(when, &next);
+
+ // The last RawState is the actually second to last, since we just added a new state
+ const RawState& last =
+ mRawStatesPending.size() == 1 ? mCurrentRawState : mRawStatesPending.rbegin()[1];
// Assign pointer ids.
if (!mHavePointerIds) {
@@ -1471,10 +1480,10 @@
#if DEBUG_RAW_EVENTS
ALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, "
"hovering ids 0x%08x -> 0x%08x, canceled ids 0x%08x",
- last->rawPointerData.pointerCount, next->rawPointerData.pointerCount,
- last->rawPointerData.touchingIdBits.value, next->rawPointerData.touchingIdBits.value,
- last->rawPointerData.hoveringIdBits.value, next->rawPointerData.hoveringIdBits.value,
- next->rawPointerData.canceledIdBits.value);
+ last.rawPointerData.pointerCount, next.rawPointerData.pointerCount,
+ last.rawPointerData.touchingIdBits.value, next.rawPointerData.touchingIdBits.value,
+ last.rawPointerData.hoveringIdBits.value, next.rawPointerData.hoveringIdBits.value,
+ next.rawPointerData.canceledIdBits.value);
#endif
processRawTouches(false /*timeout*/);
@@ -1483,8 +1492,9 @@
void TouchInputMapper::processRawTouches(bool timeout) {
if (mDeviceMode == DeviceMode::DISABLED) {
// Drop all input if the device is disabled.
- mCurrentRawState.clear();
- mRawStatesPending.clear();
+ cancelTouch(mCurrentRawState.when, mCurrentRawState.readTime);
+ mCurrentCookedState.clear();
+ updateTouchSpots();
return;
}
@@ -1508,8 +1518,9 @@
mCurrentRawState.copyFrom(next);
if (mCurrentRawState.when < mLastRawState.when) {
mCurrentRawState.when = mLastRawState.when;
+ mCurrentRawState.readTime = mLastRawState.readTime;
}
- cookAndDispatch(mCurrentRawState.when);
+ cookAndDispatch(mCurrentRawState.when, mCurrentRawState.readTime);
}
if (count != 0) {
mRawStatesPending.erase(mRawStatesPending.begin(), mRawStatesPending.begin() + count);
@@ -1523,7 +1534,8 @@
#if DEBUG_STYLUS_FUSION
ALOGD("Timeout expired, synthesizing event with new stylus data");
#endif
- cookAndDispatch(when);
+ const nsecs_t readTime = when; // consider this synthetic event to be zero latency
+ cookAndDispatch(when, readTime);
} else if (mExternalStylusFusionTimeout == LLONG_MAX) {
mExternalStylusFusionTimeout = mExternalStylusState.when + TOUCH_DATA_TIMEOUT;
getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
@@ -1531,7 +1543,7 @@
}
}
-void TouchInputMapper::cookAndDispatch(nsecs_t when) {
+void TouchInputMapper::cookAndDispatch(nsecs_t when, nsecs_t readTime) {
// Always start with a clean state.
mCurrentCookedState.clear();
@@ -1557,7 +1569,7 @@
// Consume raw off-screen touches before cooking pointer data.
// If touches are consumed, subsequent code will not receive any pointer data.
- if (consumeRawTouches(when, policyFlags)) {
+ if (consumeRawTouches(when, readTime, policyFlags)) {
mCurrentRawState.rawPointerData.clear();
}
@@ -1570,8 +1582,8 @@
applyExternalStylusTouchState(when);
// Synthesize key down from raw buttons if needed.
- synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,
- mViewport.displayId, policyFlags, mLastCookedState.buttonState,
+ synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, readTime, getDeviceId(),
+ mSource, mViewport.displayId, policyFlags, mLastCookedState.buttonState,
mCurrentCookedState.buttonState);
// Dispatch the touches either directly or by translation through a pointer on screen.
@@ -1614,26 +1626,16 @@
pointerUsage = PointerUsage::GESTURES;
}
- dispatchPointerUsage(when, policyFlags, pointerUsage);
+ dispatchPointerUsage(when, readTime, policyFlags, pointerUsage);
} else {
- if (mDeviceMode == DeviceMode::DIRECT && mConfig.showTouches &&
- mPointerController != nullptr) {
- mPointerController->setPresentation(PointerControllerInterface::Presentation::SPOT);
- mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
-
- mPointerController->setButtonState(mCurrentRawState.buttonState);
- mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
- mCurrentCookedState.cookedPointerData.idToIndex,
- mCurrentCookedState.cookedPointerData.touchingIdBits,
- mViewport.displayId);
- }
+ updateTouchSpots();
if (!mCurrentMotionAborted) {
- dispatchButtonRelease(when, policyFlags);
- dispatchHoverExit(when, policyFlags);
- dispatchTouches(when, policyFlags);
- dispatchHoverEnterAndMove(when, policyFlags);
- dispatchButtonPress(when, policyFlags);
+ dispatchButtonRelease(when, readTime, policyFlags);
+ dispatchHoverExit(when, readTime, policyFlags);
+ dispatchTouches(when, readTime, policyFlags);
+ dispatchHoverEnterAndMove(when, readTime, policyFlags);
+ dispatchButtonPress(when, readTime, policyFlags);
}
if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {
@@ -1642,7 +1644,7 @@
}
// Synthesize key up from raw buttons if needed.
- synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,
+ synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, readTime, getDeviceId(), mSource,
mViewport.displayId, policyFlags, mLastCookedState.buttonState,
mCurrentCookedState.buttonState);
@@ -1655,6 +1657,32 @@
mLastCookedState.copyFrom(mCurrentCookedState);
}
+void TouchInputMapper::updateTouchSpots() {
+ if (!mConfig.showTouches || mPointerController == nullptr) {
+ return;
+ }
+
+ // Update touch spots when this is a touchscreen even when it's not enabled so that we can
+ // clear touch spots.
+ if (mDeviceMode != DeviceMode::DIRECT &&
+ (mDeviceMode != DeviceMode::DISABLED || !isTouchScreen())) {
+ return;
+ }
+
+ mPointerController->setPresentation(PointerControllerInterface::Presentation::SPOT);
+ mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
+
+ mPointerController->setButtonState(mCurrentRawState.buttonState);
+ setTouchSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
+ mCurrentCookedState.cookedPointerData.idToIndex,
+ mCurrentCookedState.cookedPointerData.touchingIdBits, mViewport.displayId);
+}
+
+bool TouchInputMapper::isTouchScreen() {
+ return mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN &&
+ mParameters.hasAssociatedDisplay;
+}
+
void TouchInputMapper::applyExternalStylusButtonState(nsecs_t when) {
if (mDeviceMode == DeviceMode::DIRECT && hasExternalStylus() && mExternalStylusId != -1) {
mCurrentRawState.buttonState |= mExternalStylusState.buttons;
@@ -1728,7 +1756,9 @@
void TouchInputMapper::timeoutExpired(nsecs_t when) {
if (mDeviceMode == DeviceMode::POINTER) {
if (mPointerUsage == PointerUsage::GESTURES) {
- dispatchPointerGestures(when, 0 /*policyFlags*/, true /*isTimeout*/);
+ // Since this is a synthetic event, we can consider its latency to be zero
+ const nsecs_t readTime = when;
+ dispatchPointerGestures(when, readTime, 0 /*policyFlags*/, true /*isTimeout*/);
}
} else if (mDeviceMode == DeviceMode::DIRECT) {
if (mExternalStylusFusionTimeout < when) {
@@ -1750,7 +1780,7 @@
}
}
-bool TouchInputMapper::consumeRawTouches(nsecs_t when, uint32_t policyFlags) {
+bool TouchInputMapper::consumeRawTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
// Check for release of a virtual key.
if (mCurrentVirtualKey.down) {
if (mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) {
@@ -1761,7 +1791,7 @@
ALOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d",
mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
#endif
- dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_UP,
+ dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
}
return true;
@@ -1788,7 +1818,7 @@
ALOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d", mCurrentVirtualKey.keyCode,
mCurrentVirtualKey.scanCode);
#endif
- dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_UP,
+ dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY |
AKEY_EVENT_FLAG_CANCELED);
}
@@ -1819,7 +1849,7 @@
ALOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d",
mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
#endif
- dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_DOWN,
+ dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_DOWN,
AKEY_EVENT_FLAG_FROM_SYSTEM |
AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
}
@@ -1850,7 +1880,7 @@
return false;
}
-void TouchInputMapper::dispatchVirtualKey(nsecs_t when, uint32_t policyFlags,
+void TouchInputMapper::dispatchVirtualKey(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
int32_t keyEventAction, int32_t keyEventFlags) {
int32_t keyCode = mCurrentVirtualKey.keyCode;
int32_t scanCode = mCurrentVirtualKey.scanCode;
@@ -1858,19 +1888,19 @@
int32_t metaState = getContext()->getGlobalMetaState();
policyFlags |= POLICY_FLAG_VIRTUAL;
- NotifyKeyArgs args(getContext()->getNextId(), when, getDeviceId(), AINPUT_SOURCE_KEYBOARD,
- mViewport.displayId, policyFlags, keyEventAction, keyEventFlags, keyCode,
- scanCode, metaState, downTime);
+ NotifyKeyArgs args(getContext()->getNextId(), when, readTime, getDeviceId(),
+ AINPUT_SOURCE_KEYBOARD, mViewport.displayId, policyFlags, keyEventAction,
+ keyEventFlags, keyCode, scanCode, metaState, downTime);
getListener()->notifyKey(&args);
}
-void TouchInputMapper::abortTouches(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::abortTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits;
if (!currentIdBits.isEmpty()) {
int32_t metaState = getContext()->getGlobalMetaState();
int32_t buttonState = mCurrentCookedState.buttonState;
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState,
- buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0,
+ metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
mCurrentCookedState.cookedPointerData.pointerProperties,
mCurrentCookedState.cookedPointerData.pointerCoords,
mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1,
@@ -1879,7 +1909,7 @@
}
}
-void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::dispatchTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits;
BitSet32 lastIdBits = mLastCookedState.cookedPointerData.touchingIdBits;
int32_t metaState = getContext()->getGlobalMetaState();
@@ -1889,8 +1919,8 @@
if (!currentIdBits.isEmpty()) {
// No pointer id changes so this is a move event.
// The listener takes care of batching moves so we don't have to deal with that here.
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
- buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0,
+ metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
mCurrentCookedState.cookedPointerData.pointerProperties,
mCurrentCookedState.cookedPointerData.pointerCoords,
mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1,
@@ -1921,7 +1951,10 @@
while (!upIdBits.isEmpty()) {
uint32_t upId = upIdBits.clearFirstMarkedBit();
bool isCanceled = mCurrentCookedState.cookedPointerData.canceledIdBits.hasBit(upId);
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0,
+ if (isCanceled) {
+ ALOGI("Canceling pointer %d for the palm event was detected.", upId);
+ }
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0,
isCanceled ? AMOTION_EVENT_FLAG_CANCELED : 0, metaState, buttonState, 0,
mLastCookedState.cookedPointerData.pointerProperties,
mLastCookedState.cookedPointerData.pointerCoords,
@@ -1936,8 +1969,9 @@
// events, they do not generally handle them except when presented in a move event.
if (moveNeeded && !moveIdBits.isEmpty()) {
ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value);
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
- buttonState, 0, mCurrentCookedState.cookedPointerData.pointerProperties,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0,
+ metaState, buttonState, 0,
+ mCurrentCookedState.cookedPointerData.pointerProperties,
mCurrentCookedState.cookedPointerData.pointerCoords,
mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, -1,
mOrientedXPrecision, mOrientedYPrecision, mDownTime);
@@ -1953,8 +1987,8 @@
mDownTime = when;
}
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0,
- metaState, buttonState, 0,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN,
+ 0, 0, metaState, buttonState, 0,
mCurrentCookedState.cookedPointerData.pointerProperties,
mCurrentCookedState.cookedPointerData.pointerCoords,
mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits,
@@ -1963,13 +1997,13 @@
}
}
-void TouchInputMapper::dispatchHoverExit(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::dispatchHoverExit(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
if (mSentHoverEnter &&
(mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty() ||
!mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty())) {
int32_t metaState = getContext()->getGlobalMetaState();
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState,
- mLastCookedState.buttonState, 0,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0,
+ metaState, mLastCookedState.buttonState, 0,
mLastCookedState.cookedPointerData.pointerProperties,
mLastCookedState.cookedPointerData.pointerCoords,
mLastCookedState.cookedPointerData.idToIndex,
@@ -1979,13 +2013,14 @@
}
}
-void TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags) {
if (mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty() &&
!mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty()) {
int32_t metaState = getContext()->getGlobalMetaState();
if (!mSentHoverEnter) {
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0,
- metaState, mCurrentRawState.buttonState, 0,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_ENTER,
+ 0, 0, metaState, mCurrentRawState.buttonState, 0,
mCurrentCookedState.cookedPointerData.pointerProperties,
mCurrentCookedState.cookedPointerData.pointerCoords,
mCurrentCookedState.cookedPointerData.idToIndex,
@@ -1994,8 +2029,8 @@
mSentHoverEnter = true;
}
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
- mCurrentRawState.buttonState, 0,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
+ metaState, mCurrentRawState.buttonState, 0,
mCurrentCookedState.cookedPointerData.pointerProperties,
mCurrentCookedState.cookedPointerData.pointerCoords,
mCurrentCookedState.cookedPointerData.idToIndex,
@@ -2004,7 +2039,7 @@
}
}
-void TouchInputMapper::dispatchButtonRelease(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::dispatchButtonRelease(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
BitSet32 releasedButtons(mLastCookedState.buttonState & ~mCurrentCookedState.buttonState);
const BitSet32& idBits = findActiveIdBits(mLastCookedState.cookedPointerData);
const int32_t metaState = getContext()->getGlobalMetaState();
@@ -2012,7 +2047,7 @@
while (!releasedButtons.isEmpty()) {
int32_t actionButton = BitSet32::valueForBit(releasedButtons.clearFirstMarkedBit());
buttonState &= ~actionButton;
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
actionButton, 0, metaState, buttonState, 0,
mCurrentCookedState.cookedPointerData.pointerProperties,
mCurrentCookedState.cookedPointerData.pointerCoords,
@@ -2021,7 +2056,7 @@
}
}
-void TouchInputMapper::dispatchButtonPress(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::dispatchButtonPress(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
BitSet32 pressedButtons(mCurrentCookedState.buttonState & ~mLastCookedState.buttonState);
const BitSet32& idBits = findActiveIdBits(mCurrentCookedState.cookedPointerData);
const int32_t metaState = getContext()->getGlobalMetaState();
@@ -2029,8 +2064,8 @@
while (!pressedButtons.isEmpty()) {
int32_t actionButton = BitSet32::valueForBit(pressedButtons.clearFirstMarkedBit());
buttonState |= actionButton;
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton,
- 0, metaState, buttonState, 0,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ actionButton, 0, metaState, buttonState, 0,
mCurrentCookedState.cookedPointerData.pointerProperties,
mCurrentCookedState.cookedPointerData.pointerCoords,
mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
@@ -2317,38 +2352,38 @@
}
}
-void TouchInputMapper::dispatchPointerUsage(nsecs_t when, uint32_t policyFlags,
+void TouchInputMapper::dispatchPointerUsage(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
PointerUsage pointerUsage) {
if (pointerUsage != mPointerUsage) {
- abortPointerUsage(when, policyFlags);
+ abortPointerUsage(when, readTime, policyFlags);
mPointerUsage = pointerUsage;
}
switch (mPointerUsage) {
case PointerUsage::GESTURES:
- dispatchPointerGestures(when, policyFlags, false /*isTimeout*/);
+ dispatchPointerGestures(when, readTime, policyFlags, false /*isTimeout*/);
break;
case PointerUsage::STYLUS:
- dispatchPointerStylus(when, policyFlags);
+ dispatchPointerStylus(when, readTime, policyFlags);
break;
case PointerUsage::MOUSE:
- dispatchPointerMouse(when, policyFlags);
+ dispatchPointerMouse(when, readTime, policyFlags);
break;
case PointerUsage::NONE:
break;
}
}
-void TouchInputMapper::abortPointerUsage(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::abortPointerUsage(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
switch (mPointerUsage) {
case PointerUsage::GESTURES:
- abortPointerGestures(when, policyFlags);
+ abortPointerGestures(when, readTime, policyFlags);
break;
case PointerUsage::STYLUS:
- abortPointerStylus(when, policyFlags);
+ abortPointerStylus(when, readTime, policyFlags);
break;
case PointerUsage::MOUSE:
- abortPointerMouse(when, policyFlags);
+ abortPointerMouse(when, readTime, policyFlags);
break;
case PointerUsage::NONE:
break;
@@ -2357,7 +2392,8 @@
mPointerUsage = PointerUsage::NONE;
}
-void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout) {
+void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
+ bool isTimeout) {
// Update current gesture coordinates.
bool cancelPreviousGesture, finishPreviousGesture;
bool sendEvents =
@@ -2377,10 +2413,9 @@
}
if (mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM) {
- mPointerController->setSpots(mPointerGesture.currentGestureCoords,
- mPointerGesture.currentGestureIdToIndex,
- mPointerGesture.currentGestureIdBits,
- mPointerController->getDisplayId());
+ setTouchSpots(mPointerGesture.currentGestureCoords,
+ mPointerGesture.currentGestureIdToIndex,
+ mPointerGesture.currentGestureIdBits, mPointerController->getDisplayId());
}
} else {
mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
@@ -2450,8 +2485,8 @@
BitSet32 dispatchedGestureIdBits(mPointerGesture.lastGestureIdBits);
if (!dispatchedGestureIdBits.isEmpty()) {
if (cancelPreviousGesture) {
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState,
- buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0,
+ metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
mPointerGesture.downTime);
@@ -2468,9 +2503,9 @@
while (!upGestureIdBits.isEmpty()) {
uint32_t id = upGestureIdBits.clearFirstMarkedBit();
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0,
- metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
- mPointerGesture.lastGestureProperties,
+ dispatchMotion(when, readTime, policyFlags, mSource,
+ AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, metaState, buttonState,
+ AMOTION_EVENT_EDGE_FLAG_NONE, mPointerGesture.lastGestureProperties,
mPointerGesture.lastGestureCoords,
mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, id, 0,
0, mPointerGesture.downTime);
@@ -2482,8 +2517,8 @@
// Send motion events for all pointers that moved.
if (moveNeeded) {
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
- buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0,
+ metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
mPointerGesture.currentGestureProperties,
mPointerGesture.currentGestureCoords,
mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
@@ -2502,8 +2537,9 @@
mPointerGesture.downTime = when;
}
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0,
- metaState, buttonState, 0, mPointerGesture.currentGestureProperties,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN,
+ 0, 0, metaState, buttonState, 0,
+ mPointerGesture.currentGestureProperties,
mPointerGesture.currentGestureCoords,
mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, id, 0,
0, mPointerGesture.downTime);
@@ -2512,8 +2548,8 @@
// Send motion events for hover.
if (mPointerGesture.currentGestureMode == PointerGesture::Mode::HOVER) {
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
- buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
+ metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
mPointerGesture.currentGestureProperties,
mPointerGesture.currentGestureCoords,
mPointerGesture.currentGestureIdToIndex,
@@ -2523,8 +2559,7 @@
// the pointer is hovering again even if the user is not currently touching
// the touch pad. This ensures that a view will receive a fresh hover enter
// event after a tap.
- float x, y;
- mPointerController->getPosition(&x, &y);
+ auto [x, y] = getMouseCursorPosition();
PointerProperties pointerProperties;
pointerProperties.clear();
@@ -2537,11 +2572,11 @@
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
const int32_t displayId = mPointerController->getDisplayId();
- NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
- policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
- buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
- 1, &pointerProperties, &pointerCoords, 0, 0, x, y,
- mPointerGesture.downTime, /* videoFrames */ {});
+ NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+ displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
+ metaState, buttonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
+ 0, 0, x, y, mPointerGesture.downTime, /* videoFrames */ {});
getListener()->notifyMotion(&args);
}
@@ -2563,13 +2598,13 @@
}
}
-void TouchInputMapper::abortPointerGestures(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::abortPointerGestures(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
// Cancel previously dispatches pointers.
if (!mPointerGesture.lastGestureIdBits.isEmpty()) {
int32_t metaState = getContext()->getGlobalMetaState();
int32_t buttonState = mCurrentRawState.buttonState;
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState,
- buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0,
+ metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
mPointerGesture.lastGestureIdToIndex, mPointerGesture.lastGestureIdBits, -1,
0, 0, mPointerGesture.downTime);
@@ -2627,14 +2662,14 @@
// Update the velocity tracker.
{
- VelocityTracker::Position positions[MAX_POINTERS];
- uint32_t count = 0;
- for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty(); count++) {
+ std::vector<VelocityTracker::Position> positions;
+ for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) {
uint32_t id = idBits.clearFirstMarkedBit();
const RawPointerData::Pointer& pointer =
mCurrentRawState.rawPointerData.pointerForId(id);
- positions[count].x = pointer.x * mPointerXMovementScale;
- positions[count].y = pointer.y * mPointerYMovementScale;
+ float x = pointer.x * mPointerXMovementScale;
+ float y = pointer.y * mPointerYMovementScale;
+ positions.push_back({x, y});
}
mPointerGesture.velocityTracker.addMovement(when, mCurrentCookedState.fingerIdBits,
positions);
@@ -2781,13 +2816,12 @@
// Move the pointer using a relative motion.
// When using spots, the click will occur at the position of the anchor
// spot and all other spots will move there.
- mPointerController->move(deltaX, deltaY);
+ moveMouseCursor(deltaX, deltaY);
} else {
mPointerVelocityControl.reset();
}
- float x, y;
- mPointerController->getPosition(&x, &y);
+ auto [x, y] = getMouseCursorPosition();
mPointerGesture.currentGestureMode = PointerGesture::Mode::BUTTON_CLICK_OR_DRAG;
mPointerGesture.currentGestureIdBits.clear();
@@ -2813,8 +2847,7 @@
mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP_DRAG) &&
lastFingerCount == 1) {
if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) {
- float x, y;
- mPointerController->getPosition(&x, &y);
+ auto [x, y] = getMouseCursorPosition();
if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop &&
fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
#if DEBUG_GESTURES
@@ -2882,8 +2915,7 @@
mPointerGesture.currentGestureMode = PointerGesture::Mode::HOVER;
if (mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP) {
if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) {
- float x, y;
- mPointerController->getPosition(&x, &y);
+ auto [x, y] = getMouseCursorPosition();
if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop &&
fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
mPointerGesture.currentGestureMode = PointerGesture::Mode::TAP_DRAG;
@@ -2917,7 +2949,7 @@
// Move the pointer using a relative motion.
// When using spots, the hover or drag will occur at the position of the anchor spot.
- mPointerController->move(deltaX, deltaY);
+ moveMouseCursor(deltaX, deltaY);
} else {
mPointerVelocityControl.reset();
}
@@ -2939,8 +2971,7 @@
down = false;
}
- float x, y;
- mPointerController->getPosition(&x, &y);
+ auto [x, y] = getMouseCursorPosition();
mPointerGesture.currentGestureIdBits.clear();
mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
@@ -3013,8 +3044,9 @@
mCurrentRawState.rawPointerData
.getCentroidOfTouchingPointers(&mPointerGesture.referenceTouchX,
&mPointerGesture.referenceTouchY);
- mPointerController->getPosition(&mPointerGesture.referenceGestureX,
- &mPointerGesture.referenceGestureY);
+ auto [x, y] = getMouseCursorPosition();
+ mPointerGesture.referenceGestureX = x;
+ mPointerGesture.referenceGestureY = y;
}
// Clear the reference deltas for fingers not yet included in the reference calculation.
@@ -3344,7 +3376,7 @@
return true;
}
-void TouchInputMapper::dispatchPointerStylus(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::dispatchPointerStylus(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
mPointerSimple.currentCoords.clear();
mPointerSimple.currentProperties.clear();
@@ -3352,14 +3384,13 @@
if (!mCurrentCookedState.stylusIdBits.isEmpty()) {
uint32_t id = mCurrentCookedState.stylusIdBits.firstMarkedBit();
uint32_t index = mCurrentCookedState.cookedPointerData.idToIndex[id];
- float x = mCurrentCookedState.cookedPointerData.pointerCoords[index].getX();
- float y = mCurrentCookedState.cookedPointerData.pointerCoords[index].getY();
- mPointerController->setPosition(x, y);
+ setMouseCursorPosition(mCurrentCookedState.cookedPointerData.pointerCoords[index].getX(),
+ mCurrentCookedState.cookedPointerData.pointerCoords[index].getY());
hovering = mCurrentCookedState.cookedPointerData.hoveringIdBits.hasBit(id);
down = !hovering;
- mPointerController->getPosition(&x, &y);
+ auto [x, y] = getMouseCursorPosition();
mPointerSimple.currentCoords.copyFrom(
mCurrentCookedState.cookedPointerData.pointerCoords[index]);
mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
@@ -3372,14 +3403,14 @@
hovering = false;
}
- dispatchPointerSimple(when, policyFlags, down, hovering);
+ dispatchPointerSimple(when, readTime, policyFlags, down, hovering);
}
-void TouchInputMapper::abortPointerStylus(nsecs_t when, uint32_t policyFlags) {
- abortPointerSimple(when, policyFlags);
+void TouchInputMapper::abortPointerStylus(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+ abortPointerSimple(when, readTime, policyFlags);
}
-void TouchInputMapper::dispatchPointerMouse(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::dispatchPointerMouse(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
mPointerSimple.currentCoords.clear();
mPointerSimple.currentProperties.clear();
@@ -3400,7 +3431,7 @@
rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
mPointerVelocityControl.move(when, &deltaX, &deltaY);
- mPointerController->move(deltaX, deltaY);
+ moveMouseCursor(deltaX, deltaY);
} else {
mPointerVelocityControl.reset();
}
@@ -3408,8 +3439,7 @@
down = isPointerDown(mCurrentRawState.buttonState);
hovering = !down;
- float x, y;
- mPointerController->getPosition(&x, &y);
+ auto [x, y] = getMouseCursorPosition();
mPointerSimple.currentCoords.copyFrom(
mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex]);
mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
@@ -3426,17 +3456,17 @@
hovering = false;
}
- dispatchPointerSimple(when, policyFlags, down, hovering);
+ dispatchPointerSimple(when, readTime, policyFlags, down, hovering);
}
-void TouchInputMapper::abortPointerMouse(nsecs_t when, uint32_t policyFlags) {
- abortPointerSimple(when, policyFlags);
+void TouchInputMapper::abortPointerMouse(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+ abortPointerSimple(when, readTime, policyFlags);
mPointerVelocityControl.reset();
}
-void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, bool down,
- bool hovering) {
+void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
+ bool down, bool hovering) {
int32_t metaState = getContext()->getGlobalMetaState();
if (down || hovering) {
@@ -3449,16 +3479,14 @@
}
int32_t displayId = mPointerController->getDisplayId();
- float xCursorPosition;
- float yCursorPosition;
- mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+ auto [xCursorPosition, yCursorPosition] = getMouseCursorPosition();
if (mPointerSimple.down && !down) {
mPointerSimple.down = false;
// Send up.
- NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
- policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState,
+ NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+ displayId, policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState,
mLastRawState.buttonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
&mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
@@ -3471,9 +3499,9 @@
mPointerSimple.hovering = false;
// Send hover exit.
- NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
- policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState,
- mLastRawState.buttonState, MotionClassification::NONE,
+ NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+ displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0,
+ metaState, mLastRawState.buttonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
&mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
xCursorPosition, yCursorPosition, mPointerSimple.downTime,
@@ -3487,7 +3515,7 @@
mPointerSimple.downTime = when;
// Send down.
- NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource,
+ NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0,
metaState, mCurrentRawState.buttonState,
MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
@@ -3498,8 +3526,8 @@
}
// Send move.
- NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
- policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
+ NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+ displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
mCurrentRawState.buttonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
&mPointerSimple.currentCoords, mOrientedXPrecision,
@@ -3513,7 +3541,7 @@
mPointerSimple.hovering = true;
// Send hover enter.
- NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource,
+ NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0,
metaState, mCurrentRawState.buttonState,
MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
@@ -3524,9 +3552,9 @@
}
// Send hover move.
- NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
- policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
- mCurrentRawState.buttonState, MotionClassification::NONE,
+ NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+ displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
+ metaState, mCurrentRawState.buttonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
&mPointerSimple.currentCoords, mOrientedXPrecision,
mOrientedYPrecision, xCursorPosition, yCursorPosition,
@@ -3546,8 +3574,8 @@
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
- NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
- policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
+ NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+ displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
mCurrentRawState.buttonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
&pointerCoords, mOrientedXPrecision, mOrientedYPrecision,
@@ -3565,17 +3593,17 @@
}
}
-void TouchInputMapper::abortPointerSimple(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::abortPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
mPointerSimple.currentCoords.clear();
mPointerSimple.currentProperties.clear();
- dispatchPointerSimple(when, policyFlags, false, false);
+ dispatchPointerSimple(when, readTime, policyFlags, false, false);
}
-void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,
- int32_t action, int32_t actionButton, int32_t flags,
- int32_t metaState, int32_t buttonState, int32_t edgeFlags,
- const PointerProperties* properties,
+void TouchInputMapper::dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
+ uint32_t source, int32_t action, int32_t actionButton,
+ int32_t flags, int32_t metaState, int32_t buttonState,
+ int32_t edgeFlags, const PointerProperties* properties,
const PointerCoords* coords, const uint32_t* idToIndex,
BitSet32 idBits, int32_t changedId, float xPrecision,
float yPrecision, nsecs_t downTime) {
@@ -3617,15 +3645,17 @@
float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
if (mDeviceMode == DeviceMode::POINTER) {
- mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+ auto [x, y] = getMouseCursorPosition();
+ xCursorPosition = x;
+ yCursorPosition = y;
}
const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE);
const int32_t deviceId = getDeviceId();
std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames();
std::for_each(frames.begin(), frames.end(),
[this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); });
- NotifyMotionArgs args(getContext()->getNextId(), when, deviceId, source, displayId, policyFlags,
- action, actionButton, flags, metaState, buttonState,
+ NotifyMotionArgs args(getContext()->getNextId(), when, readTime, deviceId, source, displayId,
+ policyFlags, action, actionButton, flags, metaState, buttonState,
MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties,
pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
downTime, std::move(frames));
@@ -3662,9 +3692,9 @@
return changed;
}
-void TouchInputMapper::cancelTouch(nsecs_t when) {
- abortPointerUsage(when, 0 /*policyFlags*/);
- abortTouches(when, 0 /* policyFlags*/);
+void TouchInputMapper::cancelTouch(nsecs_t when, nsecs_t readTime) {
+ abortPointerUsage(when, readTime, 0 /*policyFlags*/);
+ abortTouches(when, readTime, 0 /* policyFlags*/);
}
// Transform raw coordinate to surface coordinate
@@ -3730,11 +3760,11 @@
return nullptr;
}
-void TouchInputMapper::assignPointerIds(const RawState* last, RawState* current) {
- uint32_t currentPointerCount = current->rawPointerData.pointerCount;
- uint32_t lastPointerCount = last->rawPointerData.pointerCount;
+void TouchInputMapper::assignPointerIds(const RawState& last, RawState& current) {
+ uint32_t currentPointerCount = current.rawPointerData.pointerCount;
+ uint32_t lastPointerCount = last.rawPointerData.pointerCount;
- current->rawPointerData.clearIdBits();
+ current.rawPointerData.clearIdBits();
if (currentPointerCount == 0) {
// No pointers to assign.
@@ -3745,20 +3775,20 @@
// All pointers are new.
for (uint32_t i = 0; i < currentPointerCount; i++) {
uint32_t id = i;
- current->rawPointerData.pointers[i].id = id;
- current->rawPointerData.idToIndex[id] = i;
- current->rawPointerData.markIdBit(id, current->rawPointerData.isHovering(i));
+ current.rawPointerData.pointers[i].id = id;
+ current.rawPointerData.idToIndex[id] = i;
+ current.rawPointerData.markIdBit(id, current.rawPointerData.isHovering(i));
}
return;
}
if (currentPointerCount == 1 && lastPointerCount == 1 &&
- current->rawPointerData.pointers[0].toolType == last->rawPointerData.pointers[0].toolType) {
+ current.rawPointerData.pointers[0].toolType == last.rawPointerData.pointers[0].toolType) {
// Only one pointer and no change in count so it must have the same id as before.
- uint32_t id = last->rawPointerData.pointers[0].id;
- current->rawPointerData.pointers[0].id = id;
- current->rawPointerData.idToIndex[id] = 0;
- current->rawPointerData.markIdBit(id, current->rawPointerData.isHovering(0));
+ uint32_t id = last.rawPointerData.pointers[0].id;
+ current.rawPointerData.pointers[0].id = id;
+ current.rawPointerData.idToIndex[id] = 0;
+ current.rawPointerData.markIdBit(id, current.rawPointerData.isHovering(0));
return;
}
@@ -3776,9 +3806,9 @@
for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount;
lastPointerIndex++) {
const RawPointerData::Pointer& currentPointer =
- current->rawPointerData.pointers[currentPointerIndex];
+ current.rawPointerData.pointers[currentPointerIndex];
const RawPointerData::Pointer& lastPointer =
- last->rawPointerData.pointers[lastPointerIndex];
+ last.rawPointerData.pointers[lastPointerIndex];
if (currentPointer.toolType == lastPointer.toolType) {
int64_t deltaX = currentPointer.x - lastPointer.x;
int64_t deltaY = currentPointer.y - lastPointer.y;
@@ -3864,9 +3894,9 @@
#if DEBUG_POINTER_ASSIGNMENT
ALOGD("assignPointerIds - reduced distance min-heap: size=%d", heapSize);
- for (size_t i = 0; i < heapSize; i++) {
- ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, i,
- heap[i].currentPointerIndex, heap[i].lastPointerIndex, heap[i].distance);
+ for (size_t j = 0; j < heapSize; j++) {
+ ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, j,
+ heap[j].currentPointerIndex, heap[j].lastPointerIndex, heap[j].distance);
}
#endif
}
@@ -3882,12 +3912,12 @@
matchedCurrentBits.markBit(currentPointerIndex);
matchedLastBits.markBit(lastPointerIndex);
- uint32_t id = last->rawPointerData.pointers[lastPointerIndex].id;
- current->rawPointerData.pointers[currentPointerIndex].id = id;
- current->rawPointerData.idToIndex[id] = currentPointerIndex;
- current->rawPointerData.markIdBit(id,
- current->rawPointerData.isHovering(
- currentPointerIndex));
+ uint32_t id = last.rawPointerData.pointers[lastPointerIndex].id;
+ current.rawPointerData.pointers[currentPointerIndex].id = id;
+ current.rawPointerData.idToIndex[id] = currentPointerIndex;
+ current.rawPointerData.markIdBit(id,
+ current.rawPointerData.isHovering(
+ currentPointerIndex));
usedIdBits.markBit(id);
#if DEBUG_POINTER_ASSIGNMENT
@@ -3904,10 +3934,10 @@
uint32_t currentPointerIndex = matchedCurrentBits.markFirstUnmarkedBit();
uint32_t id = usedIdBits.markFirstUnmarkedBit();
- current->rawPointerData.pointers[currentPointerIndex].id = id;
- current->rawPointerData.idToIndex[id] = currentPointerIndex;
- current->rawPointerData.markIdBit(id,
- current->rawPointerData.isHovering(currentPointerIndex));
+ current.rawPointerData.pointers[currentPointerIndex].id = id;
+ current.rawPointerData.idToIndex[id] = currentPointerIndex;
+ current.rawPointerData.markIdBit(id,
+ current.rawPointerData.isHovering(currentPointerIndex));
#if DEBUG_POINTER_ASSIGNMENT
ALOGD("assignPointerIds - assigned: cur=%" PRIu32 ", id=%" PRIu32, currentPointerIndex, id);
@@ -3967,4 +3997,63 @@
return std::nullopt;
}
+void TouchInputMapper::moveMouseCursor(float dx, float dy) const {
+ if (isPerWindowInputRotationEnabled()) {
+ // Convert from InputReader's un-rotated coordinate space to PointerController's coordinate
+ // space that is oriented with the viewport.
+ rotateDelta(mViewport.orientation, &dx, &dy);
+ }
+
+ mPointerController->move(dx, dy);
+}
+
+std::pair<float, float> TouchInputMapper::getMouseCursorPosition() const {
+ float x = 0;
+ float y = 0;
+ mPointerController->getPosition(&x, &y);
+
+ if (!isPerWindowInputRotationEnabled()) return {x, y};
+ if (!mViewport.isValid()) return {x, y};
+
+ // Convert from PointerController's rotated coordinate space that is oriented with the viewport
+ // to InputReader's un-rotated coordinate space.
+ const int32_t orientation = getInverseRotation(mViewport.orientation);
+ rotatePoint(orientation, x, y, mViewport.deviceWidth, mViewport.deviceHeight);
+ return {x, y};
+}
+
+void TouchInputMapper::setMouseCursorPosition(float x, float y) const {
+ if (isPerWindowInputRotationEnabled() && mViewport.isValid()) {
+ // Convert from InputReader's un-rotated coordinate space to PointerController's rotated
+ // coordinate space that is oriented with the viewport.
+ rotatePoint(mViewport.orientation, x, y, mRawSurfaceWidth, mRawSurfaceHeight);
+ }
+
+ mPointerController->setPosition(x, y);
+}
+
+void TouchInputMapper::setTouchSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
+ BitSet32 spotIdBits, int32_t displayId) {
+ std::array<PointerCoords, MAX_POINTERS> outSpotCoords{};
+
+ for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
+ const uint32_t index = spotIdToIndex[idBits.clearFirstMarkedBit()];
+ float x = spotCoords[index].getX();
+ float y = spotCoords[index].getY();
+ float pressure = spotCoords[index].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
+
+ if (isPerWindowInputRotationEnabled()) {
+ // Convert from InputReader's un-rotated coordinate space to PointerController's rotated
+ // coordinate space.
+ rotatePoint(mViewport.orientation, x, y, mRawSurfaceWidth, mRawSurfaceHeight);
+ }
+
+ outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_X, x);
+ outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+ outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
+ }
+
+ mPointerController->setSpots(outSpotCoords.data(), spotIdToIndex, spotIdBits, displayId);
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index df6581d..5146299 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -150,7 +150,7 @@
bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes,
uint8_t* outFlags) override;
- void cancelTouch(nsecs_t when) override;
+ void cancelTouch(nsecs_t when, nsecs_t readTime) override;
void timeoutExpired(nsecs_t when) override;
void updateExternalStylusState(const StylusState& state) override;
std::optional<int32_t> getAssociatedDisplayId() override;
@@ -298,6 +298,7 @@
struct RawState {
nsecs_t when;
+ nsecs_t readTime;
// Raw pointer sample data.
RawPointerData rawPointerData;
@@ -310,6 +311,7 @@
void copyFrom(const RawState& other) {
when = other.when;
+ readTime = other.readTime;
rawPointerData.copyFrom(other.rawPointerData);
buttonState = other.buttonState;
rawVScroll = other.rawVScroll;
@@ -318,6 +320,7 @@
void clear() {
when = 0;
+ readTime = 0;
rawPointerData.clear();
buttonState = 0;
rawVScroll = 0;
@@ -702,39 +705,42 @@
void resetExternalStylus();
void clearStylusDataPendingFlags();
- void sync(nsecs_t when);
+ void sync(nsecs_t when, nsecs_t readTime);
- bool consumeRawTouches(nsecs_t when, uint32_t policyFlags);
+ bool consumeRawTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
void processRawTouches(bool timeout);
- void cookAndDispatch(nsecs_t when);
- void dispatchVirtualKey(nsecs_t when, uint32_t policyFlags, int32_t keyEventAction,
- int32_t keyEventFlags);
+ void cookAndDispatch(nsecs_t when, nsecs_t readTime);
+ void dispatchVirtualKey(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
+ int32_t keyEventAction, int32_t keyEventFlags);
- void dispatchTouches(nsecs_t when, uint32_t policyFlags);
- void dispatchHoverExit(nsecs_t when, uint32_t policyFlags);
- void dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags);
- void dispatchButtonRelease(nsecs_t when, uint32_t policyFlags);
- void dispatchButtonPress(nsecs_t when, uint32_t policyFlags);
+ void dispatchTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+ void dispatchHoverExit(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+ void dispatchHoverEnterAndMove(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+ void dispatchButtonRelease(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+ void dispatchButtonPress(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
const BitSet32& findActiveIdBits(const CookedPointerData& cookedPointerData);
void cookPointerData();
- void abortTouches(nsecs_t when, uint32_t policyFlags);
+ void abortTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
- void dispatchPointerUsage(nsecs_t when, uint32_t policyFlags, PointerUsage pointerUsage);
- void abortPointerUsage(nsecs_t when, uint32_t policyFlags);
+ void dispatchPointerUsage(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
+ PointerUsage pointerUsage);
+ void abortPointerUsage(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
- void dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout);
- void abortPointerGestures(nsecs_t when, uint32_t policyFlags);
+ void dispatchPointerGestures(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
+ bool isTimeout);
+ void abortPointerGestures(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
bool preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture,
bool* outFinishPreviousGesture, bool isTimeout);
- void dispatchPointerStylus(nsecs_t when, uint32_t policyFlags);
- void abortPointerStylus(nsecs_t when, uint32_t policyFlags);
+ void dispatchPointerStylus(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+ void abortPointerStylus(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
- void dispatchPointerMouse(nsecs_t when, uint32_t policyFlags);
- void abortPointerMouse(nsecs_t when, uint32_t policyFlags);
+ void dispatchPointerMouse(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+ void abortPointerMouse(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
- void dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, bool down, bool hovering);
- void abortPointerSimple(nsecs_t when, uint32_t policyFlags);
+ void dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags, bool down,
+ bool hovering);
+ void abortPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
bool assignExternalStylusId(const RawState& state, bool timeout);
void applyExternalStylusButtonState(nsecs_t when);
@@ -744,9 +750,9 @@
// If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the
// method will take care of setting the index and transmuting the action to DOWN or UP
// it is the first / last pointer to go down / up.
- void dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, int32_t action,
- int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState,
- int32_t edgeFlags, const PointerProperties* properties,
+ void dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t policyFlags, uint32_t source,
+ int32_t action, int32_t actionButton, int32_t flags, int32_t metaState,
+ int32_t buttonState, int32_t edgeFlags, const PointerProperties* properties,
const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits,
int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime);
@@ -757,13 +763,27 @@
PointerCoords* outCoords, const uint32_t* outIdToIndex,
BitSet32 idBits) const;
+ // Returns if this touch device is a touch screen with an associated display.
+ bool isTouchScreen();
+ // Updates touch spots if they are enabled. Should only be used when this device is a
+ // touchscreen.
+ void updateTouchSpots();
+
bool isPointInsideSurface(int32_t x, int32_t y);
const VirtualKey* findVirtualKeyHit(int32_t x, int32_t y);
- static void assignPointerIds(const RawState* last, RawState* current);
+ static void assignPointerIds(const RawState& last, RawState& current);
const char* modeToString(DeviceMode deviceMode);
void rotateAndScale(float& x, float& y);
+
+ // Wrapper methods for interfacing with PointerController. These are used to convert points
+ // between the coordinate spaces used by InputReader and PointerController, if they differ.
+ void moveMouseCursor(float dx, float dy) const;
+ std::pair<float, float> getMouseCursorPosition() const;
+ void setMouseCursorPosition(float x, float y) const;
+ void setTouchSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
+ BitSet32 spotIdBits, int32_t displayId);
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
index 7665680..3df6f36 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
@@ -21,7 +21,7 @@
namespace android {
VibratorInputMapper::VibratorInputMapper(InputDeviceContext& deviceContext)
- : InputMapper(deviceContext), mVibrating(false) {}
+ : InputMapper(deviceContext), mVibrating(false), mSequence(0) {}
VibratorInputMapper::~VibratorInputMapper() {}
@@ -39,27 +39,22 @@
// TODO: Handle FF_STATUS, although it does not seem to be widely supported.
}
-void VibratorInputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+void VibratorInputMapper::vibrate(const VibrationSequence& sequence, ssize_t repeat,
int32_t token) {
#if DEBUG_VIBRATOR
- std::string patternStr;
- for (size_t i = 0; i < patternSize; i++) {
- if (i != 0) {
- patternStr += ", ";
- }
- patternStr += StringPrintf("%" PRId64, pattern[i]);
- }
ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", getDeviceId(),
- patternStr.c_str(), repeat, token);
+ sequence.toString().c_str(), repeat, token);
#endif
mVibrating = true;
- memcpy(mPattern, pattern, patternSize * sizeof(nsecs_t));
- mPatternSize = patternSize;
+ mSequence = sequence;
mRepeat = repeat;
mToken = token;
mIndex = -1;
+ // Request InputReader to notify InputManagerService for vibration started.
+ NotifyVibratorStateArgs args(getContext()->getNextId(), systemTime(), getDeviceId(), true);
+ getListener()->notifyVibratorState(&args);
nextStep();
}
@@ -73,6 +68,14 @@
}
}
+bool VibratorInputMapper::isVibrating() {
+ return mVibrating;
+}
+
+std::vector<int32_t> VibratorInputMapper::getVibratorIds() {
+ return getDeviceContext().getVibratorIds();
+}
+
void VibratorInputMapper::timeoutExpired(nsecs_t when) {
if (mVibrating) {
if (when >= mNextStepTime) {
@@ -84,8 +87,11 @@
}
void VibratorInputMapper::nextStep() {
+#if DEBUG_VIBRATOR
+ ALOGD("nextStep: index=%d, vibrate deviceId=%d", (int)mIndex, getDeviceId());
+#endif
mIndex += 1;
- if (size_t(mIndex) >= mPatternSize) {
+ if (size_t(mIndex) >= mSequence.pattern.size()) {
if (mRepeat < 0) {
// We are done.
stopVibrating();
@@ -94,13 +100,14 @@
mIndex = mRepeat;
}
- bool vibratorOn = mIndex & 1;
- nsecs_t duration = mPattern[mIndex];
- if (vibratorOn) {
+ const VibrationElement& element = mSequence.pattern[mIndex];
+ if (element.isOn()) {
#if DEBUG_VIBRATOR
- ALOGD("nextStep: sending vibrate deviceId=%d, duration=%" PRId64, getDeviceId(), duration);
+ std::string description = element.toString();
+ ALOGD("nextStep: sending vibrate deviceId=%d, element=%s", getDeviceId(),
+ description.c_str());
#endif
- getDeviceContext().vibrate(duration);
+ getDeviceContext().vibrate(element);
} else {
#if DEBUG_VIBRATOR
ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId());
@@ -108,10 +115,12 @@
getDeviceContext().cancelVibrate();
}
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- mNextStepTime = now + duration;
+ std::chrono::nanoseconds duration =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(element.duration);
+ mNextStepTime = now + duration.count();
getContext()->requestTimeoutAtTime(mNextStepTime);
#if DEBUG_VIBRATOR
- ALOGD("nextStep: scheduled timeout in %0.3fms", duration * 0.000001f);
+ ALOGD("nextStep: scheduled timeout in %lldms", element.duration.count());
#endif
}
@@ -121,11 +130,21 @@
ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId());
#endif
getDeviceContext().cancelVibrate();
+
+ // Request InputReader to notify InputManagerService for vibration complete.
+ NotifyVibratorStateArgs args(getContext()->getNextId(), systemTime(), getDeviceId(), false);
+ getListener()->notifyVibratorState(&args);
}
void VibratorInputMapper::dump(std::string& dump) {
dump += INDENT2 "Vibrator Input Mapper:\n";
dump += StringPrintf(INDENT3 "Vibrating: %s\n", toString(mVibrating));
+ if (mVibrating) {
+ dump += INDENT3 "Pattern: ";
+ dump += mSequence.toString();
+ dump += "\n";
+ dump += StringPrintf(INDENT3 "Repeat Index: %zd\n", mRepeat);
+ }
}
} // namespace android
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.h b/services/inputflinger/reader/mapper/VibratorInputMapper.h
index f69fdde..7ce621a 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.h
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h
@@ -30,16 +30,16 @@
virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
virtual void process(const RawEvent* rawEvent) override;
- virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
- int32_t token) override;
+ virtual void vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token) override;
virtual void cancelVibrate(int32_t token) override;
+ virtual bool isVibrating() override;
+ virtual std::vector<int32_t> getVibratorIds() override;
virtual void timeoutExpired(nsecs_t when) override;
virtual void dump(std::string& dump) override;
private:
bool mVibrating;
- nsecs_t mPattern[MAX_VIBRATE_PATTERN_SIZE];
- size_t mPatternSize;
+ VibrationSequence mSequence;
ssize_t mRepeat;
int32_t mToken;
ssize_t mIndex;
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 86ed60d..42b54c7 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -39,12 +39,24 @@
"AnrTracker_test.cpp",
"BlockingQueue_test.cpp",
"EventHub_test.cpp",
- "TestInputListener.cpp",
+ "FocusResolver_test.cpp",
+ "IInputFlingerQuery.aidl",
"InputClassifier_test.cpp",
"InputClassifierConverter_test.cpp",
"InputDispatcher_test.cpp",
"InputReader_test.cpp",
+ "InputFlingerService_test.cpp",
+ "TestInputListener.cpp",
"UinputDevice.cpp",
],
+ aidl: {
+ include_dirs: [
+ "frameworks/native/libs/input",
+ ],
+ },
+ static_libs: [
+ "libc++fs"
+ ],
require_root: true,
+ test_suites: ["device-tests"],
}
diff --git a/services/inputflinger/tests/BlockingQueue_test.cpp b/services/inputflinger/tests/BlockingQueue_test.cpp
index 0dea8d7..fd9d9d5 100644
--- a/services/inputflinger/tests/BlockingQueue_test.cpp
+++ b/services/inputflinger/tests/BlockingQueue_test.cpp
@@ -26,7 +26,7 @@
// --- BlockingQueueTest ---
/**
- * Sanity check of basic pop and push operation.
+ * Validate basic pop and push operation.
*/
TEST(BlockingQueueTest, Queue_AddAndRemove) {
constexpr size_t capacity = 10;
diff --git a/services/inputflinger/tests/EventHub_test.cpp b/services/inputflinger/tests/EventHub_test.cpp
index 71731b0..ef68a84 100644
--- a/services/inputflinger/tests/EventHub_test.cpp
+++ b/services/inputflinger/tests/EventHub_test.cpp
@@ -199,3 +199,76 @@
lastEventTime = event.when; // Ensure all returned events are monotonic
}
}
+
+// --- BitArrayTest ---
+class BitArrayTest : public testing::Test {
+protected:
+ static constexpr size_t SINGLE_ELE_BITS = 32UL;
+ static constexpr size_t MULTI_ELE_BITS = 256UL;
+
+ virtual void SetUp() override {
+ mBitmaskSingle.loadFromBuffer(mBufferSingle);
+ mBitmaskMulti.loadFromBuffer(mBufferMulti);
+ }
+
+ android::BitArray<SINGLE_ELE_BITS> mBitmaskSingle;
+ android::BitArray<MULTI_ELE_BITS> mBitmaskMulti;
+
+private:
+ const typename android::BitArray<SINGLE_ELE_BITS>::Buffer mBufferSingle = {
+ 0x800F0F0FUL // bit 0 - 31
+ };
+ const typename android::BitArray<MULTI_ELE_BITS>::Buffer mBufferMulti = {
+ 0xFFFFFFFFUL, // bit 0 - 31
+ 0x01000001UL, // bit 32 - 63
+ 0x00000000UL, // bit 64 - 95
+ 0x80000000UL, // bit 96 - 127
+ 0x00000000UL, // bit 128 - 159
+ 0x00000000UL, // bit 160 - 191
+ 0x80000008UL, // bit 192 - 223
+ 0x00000000UL, // bit 224 - 255
+ };
+};
+
+TEST_F(BitArrayTest, SetBit) {
+ ASSERT_TRUE(mBitmaskSingle.test(0));
+ ASSERT_TRUE(mBitmaskSingle.test(31));
+ ASSERT_FALSE(mBitmaskSingle.test(7));
+
+ ASSERT_TRUE(mBitmaskMulti.test(32));
+ ASSERT_TRUE(mBitmaskMulti.test(56));
+ ASSERT_FALSE(mBitmaskMulti.test(192));
+ ASSERT_TRUE(mBitmaskMulti.test(223));
+ ASSERT_FALSE(mBitmaskMulti.test(255));
+}
+
+TEST_F(BitArrayTest, AnyBit) {
+ ASSERT_TRUE(mBitmaskSingle.any(31, 32));
+ ASSERT_FALSE(mBitmaskSingle.any(12, 16));
+
+ ASSERT_TRUE(mBitmaskMulti.any(31, 32));
+ ASSERT_FALSE(mBitmaskMulti.any(33, 33));
+ ASSERT_TRUE(mBitmaskMulti.any(32, 55));
+ ASSERT_TRUE(mBitmaskMulti.any(33, 57));
+ ASSERT_FALSE(mBitmaskMulti.any(33, 55));
+ ASSERT_FALSE(mBitmaskMulti.any(130, 190));
+
+ ASSERT_FALSE(mBitmaskMulti.any(128, 195));
+ ASSERT_TRUE(mBitmaskMulti.any(128, 196));
+ ASSERT_TRUE(mBitmaskMulti.any(128, 224));
+ ASSERT_FALSE(mBitmaskMulti.any(255, 256));
+}
+
+TEST_F(BitArrayTest, SetBit_InvalidBitIndex) {
+ ASSERT_FALSE(mBitmaskSingle.test(32));
+ ASSERT_FALSE(mBitmaskMulti.test(256));
+}
+
+TEST_F(BitArrayTest, AnyBit_InvalidBitIndex) {
+ ASSERT_FALSE(mBitmaskSingle.any(32, 32));
+ ASSERT_FALSE(mBitmaskSingle.any(33, 34));
+
+ ASSERT_FALSE(mBitmaskMulti.any(256, 256));
+ ASSERT_FALSE(mBitmaskMulti.any(257, 258));
+ ASSERT_FALSE(mBitmaskMulti.any(0, 0));
+}
diff --git a/services/inputflinger/tests/FocusResolver_test.cpp b/services/inputflinger/tests/FocusResolver_test.cpp
new file mode 100644
index 0000000..17efb5b
--- /dev/null
+++ b/services/inputflinger/tests/FocusResolver_test.cpp
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2021 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 <gtest/gtest.h>
+
+#include "../FocusResolver.h"
+
+#define ASSERT_FOCUS_CHANGE(_changes, _oldFocus, _newFocus) \
+ { \
+ ASSERT_EQ(_oldFocus, _changes->oldFocus); \
+ ASSERT_EQ(_newFocus, _changes->newFocus); \
+ }
+
+// atest inputflinger_tests:FocusResolverTest
+
+namespace android::inputdispatcher {
+
+class FakeWindowHandle : public InputWindowHandle {
+public:
+ FakeWindowHandle(const std::string& name, const sp<IBinder>& token, bool focusable,
+ bool visible) {
+ mInfo.token = token;
+ mInfo.name = name;
+ mInfo.visible = visible;
+ mInfo.focusable = focusable;
+ }
+
+ bool updateInfo() { return true; }
+ void setFocusable(bool focusable) { mInfo.focusable = focusable; }
+ void setVisible(bool visible) { mInfo.visible = visible; }
+};
+
+TEST(FocusResolverTest, SetFocusedWindow) {
+ sp<IBinder> focusableWindowToken = new BBinder();
+ sp<IBinder> invisibleWindowToken = new BBinder();
+ sp<IBinder> unfocusableWindowToken = new BBinder();
+ std::vector<sp<InputWindowHandle>> windows;
+ windows.push_back(new FakeWindowHandle("Focusable", focusableWindowToken, true /* focusable */,
+ true /* visible */));
+ windows.push_back(new FakeWindowHandle("Invisible", invisibleWindowToken, true /* focusable */,
+ false /* visible */));
+ windows.push_back(new FakeWindowHandle("unfocusable", unfocusableWindowToken,
+ false /* focusable */, true /* visible */));
+
+ // focusable window can get focused
+ FocusRequest request;
+ request.displayId = 42;
+ request.token = focusableWindowToken;
+ FocusResolver focusResolver;
+ std::optional<FocusResolver::FocusChanges> changes =
+ focusResolver.setFocusedWindow(request, windows);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ focusableWindowToken);
+ ASSERT_EQ(request.displayId, changes->displayId);
+
+ // invisible window cannot get focused
+ request.token = invisibleWindowToken;
+ changes = focusResolver.setFocusedWindow(request, windows);
+ ASSERT_EQ(focusableWindowToken, changes->oldFocus);
+ ASSERT_EQ(nullptr, changes->newFocus);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ focusableWindowToken, /*to*/ nullptr);
+
+ // unfocusableWindowToken window cannot get focused
+ request.token = unfocusableWindowToken;
+ changes = focusResolver.setFocusedWindow(request, windows);
+ ASSERT_FALSE(changes);
+}
+
+TEST(FocusResolverTest, SetFocusedMirroredWindow) {
+ sp<IBinder> focusableWindowToken = new BBinder();
+ sp<IBinder> invisibleWindowToken = new BBinder();
+ sp<IBinder> unfocusableWindowToken = new BBinder();
+ std::vector<sp<InputWindowHandle>> windows;
+ windows.push_back(new FakeWindowHandle("Mirror1", focusableWindowToken, true /* focusable */,
+ true /* visible */));
+ windows.push_back(new FakeWindowHandle("Mirror1", focusableWindowToken, true /* focusable */,
+ true /* visible */));
+
+ windows.push_back(new FakeWindowHandle("Mirror2Visible", invisibleWindowToken,
+ true /* focusable */, true /* visible */));
+ windows.push_back(new FakeWindowHandle("Mirror2Invisible", invisibleWindowToken,
+ true /* focusable */, false /* visible */));
+
+ windows.push_back(new FakeWindowHandle("Mirror3Focusable", unfocusableWindowToken,
+ true /* focusable */, true /* visible */));
+ windows.push_back(new FakeWindowHandle("Mirror3Unfocusable", unfocusableWindowToken,
+ false /* focusable */, true /* visible */));
+
+ // mirrored window can get focused
+ FocusRequest request;
+ request.displayId = 42;
+ request.token = focusableWindowToken;
+ FocusResolver focusResolver;
+ std::optional<FocusResolver::FocusChanges> changes =
+ focusResolver.setFocusedWindow(request, windows);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ focusableWindowToken);
+
+ // mirrored window with one visible window can get focused
+ request.token = invisibleWindowToken;
+ changes = focusResolver.setFocusedWindow(request, windows);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ focusableWindowToken, /*to*/ invisibleWindowToken);
+
+ // mirrored window with one or more unfocusable window cannot get focused
+ request.token = unfocusableWindowToken;
+ changes = focusResolver.setFocusedWindow(request, windows);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ invisibleWindowToken, /*to*/ nullptr);
+}
+
+TEST(FocusResolverTest, SetInputWindows) {
+ sp<IBinder> focusableWindowToken = new BBinder();
+ std::vector<sp<InputWindowHandle>> windows;
+ sp<FakeWindowHandle> window = new FakeWindowHandle("Focusable", focusableWindowToken,
+ true /* focusable */, true /* visible */);
+ windows.push_back(window);
+
+ // focusable window can get focused
+ FocusRequest request;
+ request.displayId = 42;
+ request.token = focusableWindowToken;
+ FocusResolver focusResolver;
+ std::optional<FocusResolver::FocusChanges> changes =
+ focusResolver.setFocusedWindow(request, windows);
+ ASSERT_EQ(focusableWindowToken, changes->newFocus);
+
+ // Window visibility changes and the window loses focus
+ window->setVisible(false);
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ focusableWindowToken, /*to*/ nullptr);
+}
+
+TEST(FocusResolverTest, FocusRequestsCanBePending) {
+ sp<IBinder> invisibleWindowToken = new BBinder();
+ std::vector<sp<InputWindowHandle>> windows;
+
+ sp<FakeWindowHandle> invisibleWindow =
+ new FakeWindowHandle("Invisible", invisibleWindowToken, true /* focusable */,
+ false /* visible */);
+ windows.push_back(invisibleWindow);
+
+ // invisible window cannot get focused
+ FocusRequest request;
+ request.displayId = 42;
+ request.token = invisibleWindowToken;
+ FocusResolver focusResolver;
+ std::optional<FocusResolver::FocusChanges> changes =
+ focusResolver.setFocusedWindow(request, windows);
+ ASSERT_FALSE(changes);
+
+ // Window visibility changes and the window gets focused
+ invisibleWindow->setVisible(true);
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ invisibleWindowToken);
+}
+
+TEST(FocusResolverTest, FocusRequestsArePersistent) {
+ sp<IBinder> windowToken = new BBinder();
+ std::vector<sp<InputWindowHandle>> windows;
+
+ sp<FakeWindowHandle> window = new FakeWindowHandle("Test Window", windowToken,
+ false /* focusable */, true /* visible */);
+ windows.push_back(window);
+
+ // non-focusable window cannot get focused
+ FocusRequest request;
+ request.displayId = 42;
+ request.token = windowToken;
+ FocusResolver focusResolver;
+ std::optional<FocusResolver::FocusChanges> changes =
+ focusResolver.setFocusedWindow(request, windows);
+ ASSERT_FALSE(changes);
+
+ // Focusability changes and the window gets focused
+ window->setFocusable(true);
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
+
+ // Visibility changes and the window loses focus
+ window->setVisible(false);
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ windowToken, /*to*/ nullptr);
+
+ // Visibility changes and the window gets focused
+ window->setVisible(true);
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
+
+ // Window is gone and the window loses focus
+ changes = focusResolver.setInputWindows(request.displayId, {});
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ windowToken, /*to*/ nullptr);
+
+ // Window returns and the window gains focus
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
+}
+
+TEST(FocusResolverTest, ConditionalFocusRequestsAreNotPersistent) {
+ sp<IBinder> hostWindowToken = new BBinder();
+ std::vector<sp<InputWindowHandle>> windows;
+
+ sp<FakeWindowHandle> hostWindow =
+ new FakeWindowHandle("Host Window", hostWindowToken, true /* focusable */,
+ true /* visible */);
+ windows.push_back(hostWindow);
+ sp<IBinder> embeddedWindowToken = new BBinder();
+ sp<FakeWindowHandle> embeddedWindow =
+ new FakeWindowHandle("Embedded Window", embeddedWindowToken, true /* focusable */,
+ true /* visible */);
+ windows.push_back(embeddedWindow);
+
+ FocusRequest request;
+ request.displayId = 42;
+ request.token = hostWindowToken;
+ FocusResolver focusResolver;
+ std::optional<FocusResolver::FocusChanges> changes =
+ focusResolver.setFocusedWindow(request, windows);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ hostWindowToken);
+
+ request.focusedToken = hostWindow->getToken();
+ request.token = embeddedWindowToken;
+ changes = focusResolver.setFocusedWindow(request, windows);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ hostWindowToken, /*to*/ embeddedWindowToken);
+
+ embeddedWindow->setFocusable(false);
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ // The embedded window is no longer focusable, provide focus back to the original focused
+ // window.
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ embeddedWindowToken, /*to*/ hostWindowToken);
+
+ embeddedWindow->setFocusable(true);
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ // The embedded window is focusable again, but we it cannot gain focus unless there is another
+ // focus request.
+ ASSERT_FALSE(changes);
+
+ embeddedWindow->setVisible(false);
+ changes = focusResolver.setFocusedWindow(request, windows);
+ // If the embedded window is not visible/focusable, then we do not grant it focus and the
+ // request is dropped.
+ ASSERT_FALSE(changes);
+
+ embeddedWindow->setVisible(true);
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ // If the embedded window becomes visble/focusable, nothing changes since the request has been
+ // dropped.
+ ASSERT_FALSE(changes);
+}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/IInputFlingerQuery.aidl b/services/inputflinger/tests/IInputFlingerQuery.aidl
new file mode 100644
index 0000000..5c8a8da
--- /dev/null
+++ b/services/inputflinger/tests/IInputFlingerQuery.aidl
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2020, 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.
+ */
+
+import android.FocusRequest;
+import android.InputChannel;
+import android.InputWindowInfo;
+import android.os.ISetInputWindowsListener;
+
+/** @hide */
+interface IInputFlingerQuery
+{
+ /* Test interfaces */
+ void getInputWindows(out InputWindowInfo[] inputHandles);
+ void getInputChannels(out InputChannel[] channels);
+ void getLastFocusRequest(out FocusRequest request);
+ void resetInputManager();
+}
diff --git a/services/inputflinger/tests/InputClassifierConverter_test.cpp b/services/inputflinger/tests/InputClassifierConverter_test.cpp
index f58b628..c0ada9d 100644
--- a/services/inputflinger/tests/InputClassifierConverter_test.cpp
+++ b/services/inputflinger/tests/InputClassifierConverter_test.cpp
@@ -38,13 +38,13 @@
coords.setAxisValue(AMOTION_EVENT_AXIS_Y, 2);
coords.setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.5);
static constexpr nsecs_t downTime = 2;
- NotifyMotionArgs motionArgs(1 /*sequenceNum*/, downTime /*eventTime*/, 3 /*deviceId*/,
- AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4 /*policyFlags*/,
- AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, 0 /*flags*/,
- AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1 /*pointerCount*/, &properties,
- &coords, 0 /*xPrecision*/, 0 /*yPrecision*/,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ NotifyMotionArgs motionArgs(1 /*sequenceNum*/, downTime /*eventTime*/, 2 /*readTime*/,
+ 3 /*deviceId*/, AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT,
+ 4 /*policyFlags*/, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/,
+ 0 /*flags*/, AMETA_NONE, 0 /*buttonState*/,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
+ 1 /*pointerCount*/, &properties, &coords, 0 /*xPrecision*/,
+ 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime,
{} /*videoFrames*/);
return motionArgs;
diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp
index ab74a04..a72df01 100644
--- a/services/inputflinger/tests/InputClassifier_test.cpp
+++ b/services/inputflinger/tests/InputClassifier_test.cpp
@@ -41,13 +41,13 @@
coords.setAxisValue(AMOTION_EVENT_AXIS_X, 1);
coords.setAxisValue(AMOTION_EVENT_AXIS_Y, 1);
static constexpr nsecs_t downTime = 2;
- NotifyMotionArgs motionArgs(1 /*sequenceNum*/, downTime /*eventTime*/, 3 /*deviceId*/,
- AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4 /*policyFlags*/,
- AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, 0 /*flags*/,
- AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1 /*pointerCount*/, &properties,
- &coords, 0 /*xPrecision*/, 0 /*yPrecision*/,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ NotifyMotionArgs motionArgs(1 /*sequenceNum*/, downTime /*eventTime*/, 2 /*readTime*/,
+ 3 /*deviceId*/, AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT,
+ 4 /*policyFlags*/, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/,
+ 0 /*flags*/, AMETA_NONE, 0 /*buttonState*/,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
+ 1 /*pointerCount*/, &properties, &coords, 0 /*xPrecision*/,
+ 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime,
{} /*videoFrames*/);
return motionArgs;
@@ -85,9 +85,10 @@
TEST_F(InputClassifierTest, SendToNextStage_NotifyKeyArgs) {
// Create a basic key event and send to classifier
- NotifyKeyArgs args(1/*sequenceNum*/, 2/*eventTime*/, 3/*deviceId*/, AINPUT_SOURCE_KEYBOARD,
- ADISPLAY_ID_DEFAULT, 0/*policyFlags*/, AKEY_EVENT_ACTION_DOWN, 4/*flags*/,
- AKEYCODE_HOME, 5/*scanCode*/, AMETA_NONE, 6/*downTime*/);
+ NotifyKeyArgs args(1 /*sequenceNum*/, 2 /*eventTime*/, 21 /*readTime*/, 3 /*deviceId*/,
+ AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_DEFAULT, 0 /*policyFlags*/,
+ AKEY_EVENT_ACTION_DOWN, 4 /*flags*/, AKEYCODE_HOME, 5 /*scanCode*/,
+ AMETA_NONE, 6 /*downTime*/);
mClassifier->notifyKey(&args);
NotifyKeyArgs outArgs;
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 668a7ab..31d6900 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -19,16 +19,20 @@
#include <android-base/stringprintf.h>
#include <android-base/thread_annotations.h>
#include <binder/Binder.h>
-#include <input/Input.h>
-
#include <gtest/gtest.h>
+#include <input/Input.h>
#include <linux/input.h>
+
#include <cinttypes>
#include <thread>
#include <unordered_set>
#include <vector>
using android::base::StringPrintf;
+using android::os::InputEventInjectionResult;
+using android::os::InputEventInjectionSync;
+using android::os::TouchOcclusionMode;
+using namespace android::flag_operators;
namespace android::inputdispatcher {
@@ -45,6 +49,9 @@
static const int32_t INJECTOR_PID = 999;
static const int32_t INJECTOR_UID = 1001;
+// An arbitrary pid of the gesture monitor window
+static constexpr int32_t MONITOR_PID = 2001;
+
struct PointF {
float x;
float y;
@@ -68,12 +75,10 @@
InputDispatcherConfiguration mConfig;
protected:
- virtual ~FakeInputDispatcherPolicy() {
- }
+ virtual ~FakeInputDispatcherPolicy() {}
public:
- FakeInputDispatcherPolicy() {
- }
+ FakeInputDispatcherPolicy() {}
void assertFilterInputEventWasCalled(const NotifyKeyArgs& args) {
assertFilterInputEventWasCalled(AINPUT_EVENT_TYPE_KEY, args.eventTime, args.action,
@@ -123,33 +128,83 @@
// This function must be called soon after the expected ANR timer starts,
// because we are also checking how much time has passed.
- void assertNotifyAnrWasCalled(std::chrono::nanoseconds timeout,
- const sp<InputApplicationHandle>& expectedApplication,
- const sp<IBinder>& expectedToken) {
- std::pair<sp<InputApplicationHandle>, sp<IBinder>> anrData;
- ASSERT_NO_FATAL_FAILURE(anrData = getNotifyAnrData(timeout));
- ASSERT_EQ(expectedApplication, anrData.first);
- ASSERT_EQ(expectedToken, anrData.second);
+ void assertNotifyNoFocusedWindowAnrWasCalled(
+ std::chrono::nanoseconds timeout,
+ const std::shared_ptr<InputApplicationHandle>& expectedApplication) {
+ std::shared_ptr<InputApplicationHandle> application;
+ { // acquire lock
+ std::unique_lock lock(mLock);
+ android::base::ScopedLockAssertion assumeLocked(mLock);
+ ASSERT_NO_FATAL_FAILURE(
+ application = getAnrTokenLockedInterruptible(timeout, mAnrApplications, lock));
+ } // release lock
+ ASSERT_EQ(expectedApplication, application);
}
- std::pair<sp<InputApplicationHandle>, sp<IBinder>> getNotifyAnrData(
- std::chrono::nanoseconds timeout) {
- const std::chrono::time_point start = std::chrono::steady_clock::now();
+ void assertNotifyWindowUnresponsiveWasCalled(std::chrono::nanoseconds timeout,
+ const sp<IBinder>& expectedConnectionToken) {
+ sp<IBinder> connectionToken = getUnresponsiveWindowToken(timeout);
+ ASSERT_EQ(expectedConnectionToken, connectionToken);
+ }
+
+ void assertNotifyWindowResponsiveWasCalled(const sp<IBinder>& expectedConnectionToken) {
+ sp<IBinder> connectionToken = getResponsiveWindowToken();
+ ASSERT_EQ(expectedConnectionToken, connectionToken);
+ }
+
+ void assertNotifyMonitorUnresponsiveWasCalled(std::chrono::nanoseconds timeout) {
+ int32_t pid = getUnresponsiveMonitorPid(timeout);
+ ASSERT_EQ(MONITOR_PID, pid);
+ }
+
+ void assertNotifyMonitorResponsiveWasCalled() {
+ int32_t pid = getResponsiveMonitorPid();
+ ASSERT_EQ(MONITOR_PID, pid);
+ }
+
+ sp<IBinder> getUnresponsiveWindowToken(std::chrono::nanoseconds timeout) {
std::unique_lock lock(mLock);
- std::chrono::duration timeToWait = timeout + 100ms; // provide some slack
android::base::ScopedLockAssertion assumeLocked(mLock);
+ return getAnrTokenLockedInterruptible(timeout, mAnrWindowTokens, lock);
+ }
+
+ sp<IBinder> getResponsiveWindowToken() {
+ std::unique_lock lock(mLock);
+ android::base::ScopedLockAssertion assumeLocked(mLock);
+ return getAnrTokenLockedInterruptible(0s, mResponsiveWindowTokens, lock);
+ }
+
+ int32_t getUnresponsiveMonitorPid(std::chrono::nanoseconds timeout) {
+ std::unique_lock lock(mLock);
+ android::base::ScopedLockAssertion assumeLocked(mLock);
+ return getAnrTokenLockedInterruptible(timeout, mAnrMonitorPids, lock);
+ }
+
+ int32_t getResponsiveMonitorPid() {
+ std::unique_lock lock(mLock);
+ android::base::ScopedLockAssertion assumeLocked(mLock);
+ return getAnrTokenLockedInterruptible(0s, mResponsiveMonitorPids, lock);
+ }
+
+ // All three ANR-related callbacks behave the same way, so we use this generic function to wait
+ // for a specific container to become non-empty. When the container is non-empty, return the
+ // first entry from the container and erase it.
+ template <class T>
+ T getAnrTokenLockedInterruptible(std::chrono::nanoseconds timeout, std::queue<T>& storage,
+ std::unique_lock<std::mutex>& lock) REQUIRES(mLock) {
+ const std::chrono::time_point start = std::chrono::steady_clock::now();
+ std::chrono::duration timeToWait = timeout + 100ms; // provide some slack
// If there is an ANR, Dispatcher won't be idle because there are still events
// in the waitQueue that we need to check on. So we can't wait for dispatcher to be idle
// before checking if ANR was called.
- // Since dispatcher is not guaranteed to call notifyAnr right away, we need to provide
- // it some time to act. 100ms seems reasonable.
- mNotifyAnr.wait_for(lock, timeToWait, [this]() REQUIRES(mLock) {
- return !mAnrApplications.empty() && !mAnrWindowTokens.empty();
- });
+ // Since dispatcher is not guaranteed to call notifyNoFocusedWindowAnr right away, we need
+ // to provide it some time to act. 100ms seems reasonable.
+ mNotifyAnr.wait_for(lock, timeToWait,
+ [&storage]() REQUIRES(mLock) { return !storage.empty(); });
const std::chrono::duration waited = std::chrono::steady_clock::now() - start;
- if (mAnrApplications.empty() || mAnrWindowTokens.empty()) {
- ADD_FAILURE() << "Did not receive ANR callback";
+ if (storage.empty()) {
+ ADD_FAILURE() << "Did not receive the ANR callback";
return {};
}
// Ensure that the ANR didn't get raised too early. We can't be too strict here because
@@ -161,17 +216,22 @@
<< std::chrono::duration_cast<std::chrono::milliseconds>(waited).count()
<< "ms instead";
}
- std::pair<sp<InputApplicationHandle>, sp<IBinder>> result =
- std::make_pair(mAnrApplications.front(), mAnrWindowTokens.front());
- mAnrApplications.pop();
- mAnrWindowTokens.pop();
- return result;
+ T token = storage.front();
+ storage.pop();
+ return token;
}
void assertNotifyAnrWasNotCalled() {
std::scoped_lock lock(mLock);
ASSERT_TRUE(mAnrApplications.empty());
ASSERT_TRUE(mAnrWindowTokens.empty());
+ ASSERT_TRUE(mAnrMonitorPids.empty());
+ ASSERT_TRUE(mResponsiveWindowTokens.empty())
+ << "ANR was not called, but please also consume the 'connection is responsive' "
+ "signal";
+ ASSERT_TRUE(mResponsiveMonitorPids.empty())
+ << "Monitor ANR was not called, but please also consume the 'monitor is responsive'"
+ " signal";
}
void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) {
@@ -179,7 +239,39 @@
mConfig.keyRepeatDelay = delay;
}
- void setAnrTimeout(std::chrono::nanoseconds timeout) { mAnrTimeout = timeout; }
+ void waitForSetPointerCapture(bool enabled) {
+ std::unique_lock lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+
+ if (!mPointerCaptureChangedCondition.wait_for(lock, 100ms,
+ [this, enabled]() REQUIRES(mLock) {
+ return mPointerCaptureEnabled &&
+ *mPointerCaptureEnabled ==
+ enabled;
+ })) {
+ FAIL() << "Timed out waiting for setPointerCapture(" << enabled << ") to be called.";
+ }
+ mPointerCaptureEnabled.reset();
+ }
+
+ void assertSetPointerCaptureNotCalled() {
+ std::unique_lock lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+
+ if (mPointerCaptureChangedCondition.wait_for(lock, 100ms) != std::cv_status::timeout) {
+ FAIL() << "Expected setPointerCapture(enabled) to not be called, but was called. "
+ "enabled = "
+ << *mPointerCaptureEnabled;
+ }
+ mPointerCaptureEnabled.reset();
+ }
+
+ void assertDropTargetEquals(const sp<IBinder>& targetToken) {
+ std::scoped_lock lock(mLock);
+ ASSERT_TRUE(mNotifyDropWindowWasCalled);
+ ASSERT_EQ(targetToken, mDropTargetWindowToken);
+ mNotifyDropWindowWasCalled = false;
+ }
private:
std::mutex mLock;
@@ -188,35 +280,75 @@
sp<IBinder> mOnPointerDownToken GUARDED_BY(mLock);
std::optional<NotifySwitchArgs> mLastNotifySwitch GUARDED_BY(mLock);
- // ANR handling
- std::queue<sp<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock);
- std::queue<sp<IBinder>> mAnrWindowTokens GUARDED_BY(mLock);
- std::condition_variable mNotifyAnr;
- std::chrono::nanoseconds mAnrTimeout = 0ms;
+ std::condition_variable mPointerCaptureChangedCondition;
+ std::optional<bool> mPointerCaptureEnabled GUARDED_BY(mLock);
- virtual void notifyConfigurationChanged(nsecs_t when) override {
+ // ANR handling
+ std::queue<std::shared_ptr<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock);
+ std::queue<sp<IBinder>> mAnrWindowTokens GUARDED_BY(mLock);
+ std::queue<sp<IBinder>> mResponsiveWindowTokens GUARDED_BY(mLock);
+ std::queue<int32_t> mAnrMonitorPids GUARDED_BY(mLock);
+ std::queue<int32_t> mResponsiveMonitorPids GUARDED_BY(mLock);
+ std::condition_variable mNotifyAnr;
+
+ sp<IBinder> mDropTargetWindowToken GUARDED_BY(mLock);
+ bool mNotifyDropWindowWasCalled GUARDED_BY(mLock) = false;
+
+ void notifyConfigurationChanged(nsecs_t when) override {
std::scoped_lock lock(mLock);
mConfigurationChangedTime = when;
}
- virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>& application,
- const sp<IBinder>& windowToken, const std::string&) override {
+ void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, const std::string&) override {
std::scoped_lock lock(mLock);
- mAnrApplications.push(application);
- mAnrWindowTokens.push(windowToken);
+ mAnrWindowTokens.push(connectionToken);
mNotifyAnr.notify_all();
- return mAnrTimeout.count();
}
- virtual void notifyInputChannelBroken(const sp<IBinder>&) override {}
+ void notifyMonitorUnresponsive(int32_t pid, const std::string&) override {
+ std::scoped_lock lock(mLock);
+ mAnrMonitorPids.push(pid);
+ mNotifyAnr.notify_all();
+ }
- virtual void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
+ void notifyWindowResponsive(const sp<IBinder>& connectionToken) override {
+ std::scoped_lock lock(mLock);
+ mResponsiveWindowTokens.push(connectionToken);
+ mNotifyAnr.notify_all();
+ }
- virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override {
+ void notifyMonitorResponsive(int32_t pid) override {
+ std::scoped_lock lock(mLock);
+ mResponsiveMonitorPids.push(pid);
+ mNotifyAnr.notify_all();
+ }
+
+ void notifyNoFocusedWindowAnr(
+ const std::shared_ptr<InputApplicationHandle>& applicationHandle) override {
+ std::scoped_lock lock(mLock);
+ mAnrApplications.push(applicationHandle);
+ mNotifyAnr.notify_all();
+ }
+
+ void notifyInputChannelBroken(const sp<IBinder>&) override {}
+
+ void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
+
+ void notifyUntrustedTouch(const std::string& obscuringPackage) override {}
+ void notifySensorEvent(int32_t deviceId, InputDeviceSensorType sensorType,
+ InputDeviceSensorAccuracy accuracy, nsecs_t timestamp,
+ const std::vector<float>& values) override {}
+
+ void notifySensorAccuracy(int deviceId, InputDeviceSensorType sensorType,
+ InputDeviceSensorAccuracy accuracy) override {}
+
+ void notifyVibratorState(int32_t deviceId, bool isOn) override {}
+
+ void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override {
*outConfig = mConfig;
}
- virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override {
+ bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override {
std::scoped_lock lock(mLock);
switch (inputEvent->getType()) {
case AINPUT_EVENT_TYPE_KEY: {
@@ -234,22 +366,20 @@
return true;
}
- virtual void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) override {}
+ void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) override {}
- virtual void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
+ void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
- virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent*,
- uint32_t) override {
+ nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent*, uint32_t) override {
return 0;
}
- virtual bool dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent*, uint32_t,
- KeyEvent*) override {
+ bool dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent*, uint32_t, KeyEvent*) override {
return false;
}
- virtual void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
- uint32_t policyFlags) override {
+ void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
+ uint32_t policyFlags) override {
std::scoped_lock lock(mLock);
/** We simply reconstruct NotifySwitchArgs in policy because InputDispatcher is
* essentially a passthrough for notifySwitch.
@@ -257,17 +387,29 @@
mLastNotifySwitch = NotifySwitchArgs(1 /*id*/, when, policyFlags, switchValues, switchMask);
}
- virtual void pokeUserActivity(nsecs_t, int32_t) override {}
+ void pokeUserActivity(nsecs_t, int32_t, int32_t) override {}
- virtual bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override {
- return false;
+ bool checkInjectEventsPermissionNonReentrant(int32_t pid, int32_t uid) override {
+ return pid == INJECTOR_PID && uid == INJECTOR_UID;
}
- virtual void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {
+ void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {
std::scoped_lock lock(mLock);
mOnPointerDownToken = newToken;
}
+ void setPointerCapture(bool enabled) override {
+ std::scoped_lock lock(mLock);
+ mPointerCaptureEnabled = {enabled};
+ mPointerCaptureChangedCondition.notify_all();
+ }
+
+ void notifyDropWindow(const sp<IBinder>& token, float x, float y) override {
+ std::scoped_lock lock(mLock);
+ mNotifyDropWindowWasCalled = true;
+ mDropTargetWindowToken = token;
+ }
+
void assertFilterInputEventWasCalled(int type, nsecs_t eventTime, int32_t action,
int32_t displayId) {
std::scoped_lock lock(mLock);
@@ -292,70 +434,6 @@
}
};
-// --- HmacKeyManagerTest ---
-
-class HmacKeyManagerTest : public testing::Test {
-protected:
- HmacKeyManager mHmacKeyManager;
-};
-
-/**
- * Ensure that separate calls to sign the same data are generating the same key.
- * We avoid asserting against INVALID_HMAC. Since the key is random, there is a non-zero chance
- * that a specific key and data combination would produce INVALID_HMAC, which would cause flaky
- * tests.
- */
-TEST_F(HmacKeyManagerTest, GeneratedHmac_IsConsistent) {
- KeyEvent event = getTestKeyEvent();
- VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEvent(event);
-
- std::array<uint8_t, 32> hmac1 = mHmacKeyManager.sign(verifiedEvent);
- std::array<uint8_t, 32> hmac2 = mHmacKeyManager.sign(verifiedEvent);
- ASSERT_EQ(hmac1, hmac2);
-}
-
-/**
- * Ensure that changes in VerifiedKeyEvent produce a different hmac.
- */
-TEST_F(HmacKeyManagerTest, GeneratedHmac_ChangesWhenFieldsChange) {
- KeyEvent event = getTestKeyEvent();
- VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEvent(event);
- std::array<uint8_t, 32> initialHmac = mHmacKeyManager.sign(verifiedEvent);
-
- verifiedEvent.deviceId += 1;
- ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-
- verifiedEvent.source += 1;
- ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-
- verifiedEvent.eventTimeNanos += 1;
- ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-
- verifiedEvent.displayId += 1;
- ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-
- verifiedEvent.action += 1;
- ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-
- verifiedEvent.downTimeNanos += 1;
- ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-
- verifiedEvent.flags += 1;
- ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-
- verifiedEvent.keyCode += 1;
- ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-
- verifiedEvent.scanCode += 1;
- ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-
- verifiedEvent.metaState += 1;
- ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-
- verifiedEvent.repeatCount += 1;
- ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-}
-
// --- InputDispatcherTest ---
class InputDispatcherTest : public testing::Test {
@@ -367,7 +445,7 @@
mFakePolicy = new FakeInputDispatcherPolicy();
mDispatcher = new InputDispatcher(mFakePolicy);
mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
- //Start InputDispatcher thread
+ // Start InputDispatcher thread
ASSERT_EQ(OK, mDispatcher->start());
}
@@ -390,8 +468,19 @@
ALOGE("%s", to.c_str());
}
}
-};
+ void setFocusedWindow(const sp<InputWindowHandle>& window,
+ const sp<InputWindowHandle>& focusedWindow = nullptr) {
+ FocusRequest request;
+ request.token = window->getToken();
+ if (focusedWindow) {
+ request.focusedToken = focusedWindow->getToken();
+ }
+ request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
+ request.displayId = window->getInfo()->displayId;
+ mDispatcher->setFocusedWindow(request);
+ }
+};
TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesKeyEvents) {
KeyEvent event;
@@ -401,18 +490,18 @@
INVALID_HMAC,
/*action*/ -1, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME,
ARBITRARY_TIME);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+ InputEventInjectionSync::NONE, 0ms, 0))
<< "Should reject key events with undefined action.";
// Rejects ACTION_MULTIPLE since it is not supported despite being defined in the API.
event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
INVALID_HMAC, AKEY_EVENT_ACTION_MULTIPLE, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0,
ARBITRARY_TIME, ARBITRARY_TIME);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+ InputEventInjectionSync::NONE, 0ms, 0))
<< "Should reject key events with ACTION_MULTIPLE.";
}
@@ -432,113 +521,110 @@
constexpr int32_t metaState = AMETA_NONE;
constexpr MotionClassification classification = MotionClassification::NONE;
+ ui::Transform identityTransform;
// Rejects undefined motion actions.
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
- /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */,
- 1 /* yScale */, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification,
+ identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+ InputEventInjectionSync::NONE, 0ms, 0))
<< "Should reject motion events with undefined action.";
// Rejects pointer down with invalid index.
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_POINTER_DOWN |
(1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
- 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
+ pointerCoords);
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+ InputEventInjectionSync::NONE, 0ms, 0))
<< "Should reject motion events with pointer down index too large.";
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_POINTER_DOWN |
(~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
- 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
+ pointerCoords);
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+ InputEventInjectionSync::NONE, 0ms, 0))
<< "Should reject motion events with pointer down index too small.";
// Rejects pointer up with invalid index.
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_POINTER_UP |
(1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
- 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
+ pointerCoords);
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+ InputEventInjectionSync::NONE, 0ms, 0))
<< "Should reject motion events with pointer up index too large.";
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_POINTER_UP |
(~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
- 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
+ pointerCoords);
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+ InputEventInjectionSync::NONE, 0ms, 0))
<< "Should reject motion events with pointer up index too small.";
// Rejects motion events with invalid number of pointers.
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
- 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 0, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+ InputEventInjectionSync::NONE, 0ms, 0))
<< "Should reject motion events with 0 pointers.";
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
- 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+ InputEventInjectionSync::NONE, 0ms, 0))
<< "Should reject motion events with more than MAX_POINTERS pointers.";
// Rejects motion events with invalid pointer ids.
pointerProperties[0].id = -1;
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
- 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+ InputEventInjectionSync::NONE, 0ms, 0))
<< "Should reject motion events with pointer ids less than 0.";
pointerProperties[0].id = MAX_POINTER_ID + 1;
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
- 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+ InputEventInjectionSync::NONE, 0ms, 0))
<< "Should reject motion events with pointer ids greater than MAX_POINTER_ID.";
// Rejects motion events with duplicate pointer ids.
@@ -546,13 +632,12 @@
pointerProperties[1].id = 1;
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
- 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 2, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+ InputEventInjectionSync::NONE, 0ms, 0))
<< "Should reject motion events with duplicate pointer ids.";
}
@@ -586,24 +671,23 @@
FakeApplicationHandle() {
mInfo.name = "Fake Application";
mInfo.token = new BBinder();
- mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count();
+ mInfo.dispatchingTimeoutMillis =
+ std::chrono::duration_cast<std::chrono::milliseconds>(DISPATCHING_TIMEOUT).count();
}
virtual ~FakeApplicationHandle() {}
- virtual bool updateInfo() override {
- return true;
- }
+ virtual bool updateInfo() override { return true; }
- void setDispatchingTimeout(std::chrono::nanoseconds timeout) {
- mInfo.dispatchingTimeout = timeout.count();
+ void setDispatchingTimeout(std::chrono::milliseconds timeout) {
+ mInfo.dispatchingTimeoutMillis = timeout.count();
}
};
class FakeInputReceiver {
public:
- explicit FakeInputReceiver(const sp<InputChannel>& clientChannel, const std::string name)
+ explicit FakeInputReceiver(std::unique_ptr<InputChannel> clientChannel, const std::string name)
: mName(name) {
- mConsumer = std::make_unique<InputConsumer>(clientChannel);
+ mConsumer = std::make_unique<InputConsumer>(std::move(clientChannel));
}
InputEvent* consume() {
@@ -662,8 +746,14 @@
ASSERT_EQ(OK, status) << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
}
- void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId,
- int32_t expectedFlags) {
+ void sendTimeline(int32_t inputEventId, std::array<nsecs_t, GraphicsTimeline::SIZE> timeline) {
+ const status_t status = mConsumer->sendTimeline(inputEventId, timeline);
+ ASSERT_EQ(OK, status);
+ }
+
+ void consumeEvent(int32_t expectedEventType, int32_t expectedAction,
+ std::optional<int32_t> expectedDisplayId,
+ std::optional<int32_t> expectedFlags) {
InputEvent* event = consume();
ASSERT_NE(nullptr, event) << mName.c_str()
@@ -672,24 +762,36 @@
<< mName.c_str() << " expected " << inputEventTypeToString(expectedEventType)
<< " event, got " << inputEventTypeToString(event->getType()) << " event";
- EXPECT_EQ(expectedDisplayId, event->getDisplayId());
+ if (expectedDisplayId.has_value()) {
+ EXPECT_EQ(expectedDisplayId, event->getDisplayId());
+ }
switch (expectedEventType) {
case AINPUT_EVENT_TYPE_KEY: {
const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*event);
EXPECT_EQ(expectedAction, keyEvent.getAction());
- EXPECT_EQ(expectedFlags, keyEvent.getFlags());
+ if (expectedFlags.has_value()) {
+ EXPECT_EQ(expectedFlags.value(), keyEvent.getFlags());
+ }
break;
}
case AINPUT_EVENT_TYPE_MOTION: {
const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
EXPECT_EQ(expectedAction, motionEvent.getAction());
- EXPECT_EQ(expectedFlags, motionEvent.getFlags());
+ if (expectedFlags.has_value()) {
+ EXPECT_EQ(expectedFlags.value(), motionEvent.getFlags());
+ }
break;
}
case AINPUT_EVENT_TYPE_FOCUS: {
FAIL() << "Use 'consumeFocusEvent' for FOCUS events";
}
+ case AINPUT_EVENT_TYPE_CAPTURE: {
+ FAIL() << "Use 'consumeCaptureEvent' for CAPTURE events";
+ }
+ case AINPUT_EVENT_TYPE_DRAG: {
+ FAIL() << "Use 'consumeDragEvent' for DRAG events";
+ }
default: {
FAIL() << mName.c_str() << ": invalid event type: " << expectedEventType;
}
@@ -712,6 +814,38 @@
EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode());
}
+ void consumeCaptureEvent(bool hasCapture) {
+ const InputEvent* event = consume();
+ ASSERT_NE(nullptr, event) << mName.c_str()
+ << ": consumer should have returned non-NULL event.";
+ ASSERT_EQ(AINPUT_EVENT_TYPE_CAPTURE, event->getType())
+ << "Got " << inputEventTypeToString(event->getType())
+ << " event instead of CAPTURE event";
+
+ ASSERT_EQ(ADISPLAY_ID_NONE, event->getDisplayId())
+ << mName.c_str() << ": event displayId should always be NONE.";
+
+ const auto& captureEvent = static_cast<const CaptureEvent&>(*event);
+ EXPECT_EQ(hasCapture, captureEvent.getPointerCaptureEnabled());
+ }
+
+ void consumeDragEvent(bool isExiting, float x, float y) {
+ const InputEvent* event = consume();
+ ASSERT_NE(nullptr, event) << mName.c_str()
+ << ": consumer should have returned non-NULL event.";
+ ASSERT_EQ(AINPUT_EVENT_TYPE_DRAG, event->getType())
+ << "Got " << inputEventTypeToString(event->getType())
+ << " event instead of DRAG event";
+
+ EXPECT_EQ(ADISPLAY_ID_NONE, event->getDisplayId())
+ << mName.c_str() << ": event displayId should always be NONE.";
+
+ const auto& dragEvent = static_cast<const DragEvent&>(*event);
+ EXPECT_EQ(isExiting, dragEvent.isExiting());
+ EXPECT_EQ(x, dragEvent.getX());
+ EXPECT_EQ(y, dragEvent.getY());
+ }
+
void assertNoEvents() {
InputEvent* event = consume();
if (event == nullptr) {
@@ -729,6 +863,10 @@
FocusEvent& focusEvent = static_cast<FocusEvent&>(*event);
ADD_FAILURE() << "Received focus event, hasFocus = "
<< (focusEvent.getHasFocus() ? "true" : "false");
+ } else if (event->getType() == AINPUT_EVENT_TYPE_CAPTURE) {
+ const auto& captureEvent = static_cast<CaptureEvent&>(*event);
+ ADD_FAILURE() << "Received capture event, pointerCaptureEnabled = "
+ << (captureEvent.getPointerCaptureEnabled() ? "true" : "false");
}
FAIL() << mName.c_str()
<< ": should not have received any events, so consume() should return NULL";
@@ -748,71 +886,87 @@
static const int32_t WIDTH = 600;
static const int32_t HEIGHT = 800;
- FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
+ FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
const sp<InputDispatcher>& dispatcher, const std::string name,
- int32_t displayId, sp<IBinder> token = nullptr)
+ int32_t displayId, std::optional<sp<IBinder>> token = std::nullopt)
: mName(name) {
- if (token == nullptr) {
- sp<InputChannel> serverChannel, clientChannel;
- InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
- mInputReceiver = std::make_unique<FakeInputReceiver>(clientChannel, name);
- dispatcher->registerInputChannel(serverChannel);
- token = serverChannel->getConnectionToken();
+ if (token == std::nullopt) {
+ base::Result<std::unique_ptr<InputChannel>> channel =
+ dispatcher->createInputChannel(name);
+ token = (*channel)->getConnectionToken();
+ mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name);
}
inputApplicationHandle->updateInfo();
mInfo.applicationInfo = *inputApplicationHandle->getInfo();
- mInfo.token = token;
+ mInfo.token = *token;
mInfo.id = sId++;
mInfo.name = name;
- mInfo.layoutParamsFlags = 0;
- mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
- mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count();
+ mInfo.type = InputWindowInfo::Type::APPLICATION;
+ mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
+ mInfo.alpha = 1.0;
mInfo.frameLeft = 0;
mInfo.frameTop = 0;
mInfo.frameRight = WIDTH;
mInfo.frameBottom = HEIGHT;
+ mInfo.transform.set(0, 0);
mInfo.globalScaleFactor = 1.0;
mInfo.touchableRegion.clear();
mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
mInfo.visible = true;
- mInfo.canReceiveKeys = true;
- mInfo.hasFocus = false;
+ mInfo.focusable = false;
mInfo.hasWallpaper = false;
mInfo.paused = false;
mInfo.ownerPid = INJECTOR_PID;
mInfo.ownerUid = INJECTOR_UID;
- mInfo.inputFeatures = 0;
mInfo.displayId = displayId;
}
virtual bool updateInfo() { return true; }
- void setFocus(bool hasFocus) { mInfo.hasFocus = hasFocus; }
+ void setFocusable(bool focusable) { mInfo.focusable = focusable; }
+
+ void setVisible(bool visible) { mInfo.visible = visible; }
void setDispatchingTimeout(std::chrono::nanoseconds timeout) {
- mInfo.dispatchingTimeout = timeout.count();
+ mInfo.dispatchingTimeout = timeout;
}
void setPaused(bool paused) { mInfo.paused = paused; }
+ void setAlpha(float alpha) { mInfo.alpha = alpha; }
+
+ void setTouchOcclusionMode(android::os::TouchOcclusionMode mode) {
+ mInfo.touchOcclusionMode = mode;
+ }
+
+ void setApplicationToken(sp<IBinder> token) { mInfo.applicationInfo.token = token; }
+
void setFrame(const Rect& frame) {
mInfo.frameLeft = frame.left;
mInfo.frameTop = frame.top;
mInfo.frameRight = frame.right;
mInfo.frameBottom = frame.bottom;
+ mInfo.transform.set(-frame.left, -frame.top);
mInfo.touchableRegion.clear();
mInfo.addTouchableRegion(frame);
}
- void setLayoutParamFlags(int32_t flags) { mInfo.layoutParamsFlags = flags; }
+ void addFlags(Flags<InputWindowInfo::Flag> flags) { mInfo.flags |= flags; }
- void setWindowScale(float xScale, float yScale) {
- mInfo.windowXScale = xScale;
- mInfo.windowYScale = yScale;
+ void setFlags(Flags<InputWindowInfo::Flag> flags) { mInfo.flags = flags; }
+
+ void setInputFeatures(InputWindowInfo::Feature features) { mInfo.inputFeatures = features; }
+
+ void setWindowTransform(float dsdx, float dtdx, float dtdy, float dsdy) {
+ mInfo.transform.set(dsdx, dtdx, dtdy, dsdy);
}
+ void setWindowScale(float xScale, float yScale) { setWindowTransform(xScale, 0, 0, yScale); }
+
+ void setWindowOffset(float offsetX, float offsetY) { mInfo.transform.set(offsetX, offsetY); }
+
void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId,
expectedFlags);
@@ -823,56 +977,79 @@
}
void consumeMotionCancel(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
- int32_t expectedFlags = 0) {
+ int32_t expectedFlags = 0) {
consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, expectedDisplayId,
expectedFlags);
}
void consumeMotionMove(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
- int32_t expectedFlags = 0) {
+ int32_t expectedFlags = 0) {
consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_MOVE, expectedDisplayId,
expectedFlags);
}
void consumeMotionDown(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
- int32_t expectedFlags = 0) {
+ int32_t expectedFlags = 0) {
+ consumeAnyMotionDown(expectedDisplayId, expectedFlags);
+ }
+
+ void consumeAnyMotionDown(std::optional<int32_t> expectedDisplayId = std::nullopt,
+ std::optional<int32_t> expectedFlags = std::nullopt) {
consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_DOWN, expectedDisplayId,
expectedFlags);
}
void consumeMotionPointerDown(int32_t pointerIdx,
- int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, int32_t expectedFlags = 0) {
- int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN
- | (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
+ int32_t expectedFlags = 0) {
+ int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
consumeEvent(AINPUT_EVENT_TYPE_MOTION, action, expectedDisplayId, expectedFlags);
}
void consumeMotionPointerUp(int32_t pointerIdx, int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
- int32_t expectedFlags = 0) {
- int32_t action = AMOTION_EVENT_ACTION_POINTER_UP
- | (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ int32_t expectedFlags = 0) {
+ int32_t action = AMOTION_EVENT_ACTION_POINTER_UP |
+ (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
consumeEvent(AINPUT_EVENT_TYPE_MOTION, action, expectedDisplayId, expectedFlags);
}
void consumeMotionUp(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
- int32_t expectedFlags = 0) {
+ int32_t expectedFlags = 0) {
consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_UP, expectedDisplayId,
expectedFlags);
}
+ void consumeMotionOutside(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
+ int32_t expectedFlags = 0) {
+ consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_OUTSIDE, expectedDisplayId,
+ expectedFlags);
+ }
+
void consumeFocusEvent(bool hasFocus, bool inTouchMode = true) {
ASSERT_NE(mInputReceiver, nullptr)
<< "Cannot consume events from a window with no receiver";
mInputReceiver->consumeFocusEvent(hasFocus, inTouchMode);
}
- void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId,
- int32_t expectedFlags) {
+ void consumeCaptureEvent(bool hasCapture) {
+ ASSERT_NE(mInputReceiver, nullptr)
+ << "Cannot consume events from a window with no receiver";
+ mInputReceiver->consumeCaptureEvent(hasCapture);
+ }
+
+ void consumeEvent(int32_t expectedEventType, int32_t expectedAction,
+ std::optional<int32_t> expectedDisplayId,
+ std::optional<int32_t> expectedFlags) {
ASSERT_NE(mInputReceiver, nullptr) << "Invalid consume event on window with no receiver";
mInputReceiver->consumeEvent(expectedEventType, expectedAction, expectedDisplayId,
expectedFlags);
}
+ void consumeDragEvent(bool isExiting, float x, float y) {
+ mInputReceiver->consumeDragEvent(isExiting, x, y);
+ }
+
std::optional<uint32_t> receiveEvent(InputEvent** outEvent = nullptr) {
if (mInputReceiver == nullptr) {
ADD_FAILURE() << "Invalid receive event on window with no receiver";
@@ -886,6 +1063,11 @@
mInputReceiver->finishEvent(sequenceNum);
}
+ void sendTimeline(int32_t inputEventId, std::array<nsecs_t, GraphicsTimeline::SIZE> timeline) {
+ ASSERT_NE(mInputReceiver, nullptr) << "Invalid receive event on window with no receiver";
+ mInputReceiver->sendTimeline(inputEventId, timeline);
+ }
+
InputEvent* consume() {
if (mInputReceiver == nullptr) {
return nullptr;
@@ -894,8 +1076,12 @@
}
void assertNoEvents() {
- ASSERT_NE(mInputReceiver, nullptr)
- << "Call 'assertNoEvents' on a window with an InputReceiver";
+ if (mInputReceiver == nullptr &&
+ mInfo.inputFeatures.test(InputWindowInfo::Feature::NO_INPUT_CHANNEL)) {
+ return; // Can't receive events if the window does not have input channel
+ }
+ ASSERT_NE(nullptr, mInputReceiver)
+ << "Window without InputReceiver must specify feature NO_INPUT_CHANNEL";
mInputReceiver->assertNoEvents();
}
@@ -903,6 +1089,11 @@
const std::string& getName() { return mName; }
+ void setOwnerInfo(int32_t ownerPid, int32_t ownerUid) {
+ mInfo.ownerPid = ownerPid;
+ mInfo.ownerUid = ownerUid;
+ }
+
private:
const std::string mName;
std::unique_ptr<FakeInputReceiver> mInputReceiver;
@@ -911,10 +1102,12 @@
std::atomic<int32_t> FakeWindowHandle::sId{1};
-static int32_t injectKey(const sp<InputDispatcher>& dispatcher, int32_t action, int32_t repeatCount,
- int32_t displayId = ADISPLAY_ID_NONE,
- int32_t syncMode = INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
- std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT) {
+static InputEventInjectionResult injectKey(
+ const sp<InputDispatcher>& dispatcher, int32_t action, int32_t repeatCount,
+ int32_t displayId = ADISPLAY_ID_NONE,
+ InputEventInjectionSync syncMode = InputEventInjectionSync::WAIT_FOR_RESULT,
+ std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
+ bool allowKeyRepeat = true) {
KeyEvent event;
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
@@ -923,74 +1116,195 @@
INVALID_HMAC, action, /* flags */ 0, AKEYCODE_A, KEY_A, AMETA_NONE,
repeatCount, currentTime, currentTime);
+ int32_t policyFlags = POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER;
+ if (!allowKeyRepeat) {
+ policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
+ }
// Inject event until dispatch out.
return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, syncMode,
- injectionTimeout,
- POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+ injectionTimeout, policyFlags);
}
-static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher,
- int32_t displayId = ADISPLAY_ID_NONE) {
+static InputEventInjectionResult injectKeyDown(const sp<InputDispatcher>& dispatcher,
+ int32_t displayId = ADISPLAY_ID_NONE) {
return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId);
}
-static int32_t injectKeyUp(const sp<InputDispatcher>& dispatcher,
- int32_t displayId = ADISPLAY_ID_NONE) {
+// Inject a down event that has key repeat disabled. This allows InputDispatcher to idle without
+// sending a subsequent key up. When key repeat is enabled, the dispatcher cannot idle because it
+// has to be woken up to process the repeating key.
+static InputEventInjectionResult injectKeyDownNoRepeat(const sp<InputDispatcher>& dispatcher,
+ int32_t displayId = ADISPLAY_ID_NONE) {
+ return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId,
+ InputEventInjectionSync::WAIT_FOR_RESULT, INJECT_EVENT_TIMEOUT,
+ /* allowKeyRepeat */ false);
+}
+
+static InputEventInjectionResult injectKeyUp(const sp<InputDispatcher>& dispatcher,
+ int32_t displayId = ADISPLAY_ID_NONE) {
return injectKey(dispatcher, AKEY_EVENT_ACTION_UP, /* repeatCount */ 0, displayId);
}
-static int32_t injectMotionEvent(
- const sp<InputDispatcher>& dispatcher, int32_t action, int32_t source, int32_t displayId,
- const PointF& position,
- const PointF& cursorPosition = {AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION},
+class PointerBuilder {
+public:
+ PointerBuilder(int32_t id, int32_t toolType) {
+ mProperties.clear();
+ mProperties.id = id;
+ mProperties.toolType = toolType;
+ mCoords.clear();
+ }
+
+ PointerBuilder& x(float x) { return axis(AMOTION_EVENT_AXIS_X, x); }
+
+ PointerBuilder& y(float y) { return axis(AMOTION_EVENT_AXIS_Y, y); }
+
+ PointerBuilder& axis(int32_t axis, float value) {
+ mCoords.setAxisValue(axis, value);
+ return *this;
+ }
+
+ PointerProperties buildProperties() const { return mProperties; }
+
+ PointerCoords buildCoords() const { return mCoords; }
+
+private:
+ PointerProperties mProperties;
+ PointerCoords mCoords;
+};
+
+class MotionEventBuilder {
+public:
+ MotionEventBuilder(int32_t action, int32_t source) {
+ mAction = action;
+ mSource = source;
+ mEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ }
+
+ MotionEventBuilder& eventTime(nsecs_t eventTime) {
+ mEventTime = eventTime;
+ return *this;
+ }
+
+ MotionEventBuilder& displayId(int32_t displayId) {
+ mDisplayId = displayId;
+ return *this;
+ }
+
+ MotionEventBuilder& actionButton(int32_t actionButton) {
+ mActionButton = actionButton;
+ return *this;
+ }
+
+ MotionEventBuilder& buttonState(int32_t buttonState) {
+ mButtonState = buttonState;
+ return *this;
+ }
+
+ MotionEventBuilder& rawXCursorPosition(float rawXCursorPosition) {
+ mRawXCursorPosition = rawXCursorPosition;
+ return *this;
+ }
+
+ MotionEventBuilder& rawYCursorPosition(float rawYCursorPosition) {
+ mRawYCursorPosition = rawYCursorPosition;
+ return *this;
+ }
+
+ MotionEventBuilder& pointer(PointerBuilder pointer) {
+ mPointers.push_back(pointer);
+ return *this;
+ }
+
+ MotionEvent build() {
+ std::vector<PointerProperties> pointerProperties;
+ std::vector<PointerCoords> pointerCoords;
+ for (const PointerBuilder& pointer : mPointers) {
+ pointerProperties.push_back(pointer.buildProperties());
+ pointerCoords.push_back(pointer.buildCoords());
+ }
+
+ // Set mouse cursor position for the most common cases to avoid boilerplate.
+ if (mSource == AINPUT_SOURCE_MOUSE &&
+ !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition) &&
+ mPointers.size() == 1) {
+ mRawXCursorPosition = pointerCoords[0].getX();
+ mRawYCursorPosition = pointerCoords[0].getY();
+ }
+
+ MotionEvent event;
+ ui::Transform identityTransform;
+ event.initialize(InputEvent::nextId(), DEVICE_ID, mSource, mDisplayId, INVALID_HMAC,
+ mAction, mActionButton, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE,
+ mButtonState, MotionClassification::NONE, identityTransform,
+ /* xPrecision */ 0, /* yPrecision */ 0, mRawXCursorPosition,
+ mRawYCursorPosition, mEventTime, mEventTime, mPointers.size(),
+ pointerProperties.data(), pointerCoords.data());
+
+ return event;
+ }
+
+private:
+ int32_t mAction;
+ int32_t mSource;
+ nsecs_t mEventTime;
+ int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
+ int32_t mActionButton{0};
+ int32_t mButtonState{0};
+ float mRawXCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
+ float mRawYCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
+
+ std::vector<PointerBuilder> mPointers;
+};
+
+static InputEventInjectionResult injectMotionEvent(
+ const sp<InputDispatcher>& dispatcher, const MotionEvent& event,
std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
- int32_t injectionMode = INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
- nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC)) {
- MotionEvent event;
- PointerProperties pointerProperties[1];
- PointerCoords pointerCoords[1];
-
- pointerProperties[0].clear();
- pointerProperties[0].id = 0;
- pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
-
- pointerCoords[0].clear();
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, position.x);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, position.y);
-
- // Define a valid motion down event.
- event.initialize(InputEvent::nextId(), DEVICE_ID, source, displayId, INVALID_HMAC, action,
- /* actionButton */ 0,
- /* flags */ 0,
- /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
- /* xScale */ 1, /* yScale */ 1, /* xOffset */ 0, /* yOffset */ 0,
- /* xPrecision */ 0, /* yPrecision */ 0, cursorPosition.x, cursorPosition.y,
- eventTime, eventTime,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
-
- // Inject event until dispatch out.
+ InputEventInjectionSync injectionMode = InputEventInjectionSync::WAIT_FOR_RESULT) {
return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, injectionMode,
injectionTimeout,
POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
}
-static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t source,
- int32_t displayId, const PointF& location = {100, 200}) {
+static InputEventInjectionResult injectMotionEvent(
+ const sp<InputDispatcher>& dispatcher, int32_t action, int32_t source, int32_t displayId,
+ const PointF& position,
+ const PointF& cursorPosition = {AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION},
+ std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync injectionMode = InputEventInjectionSync::WAIT_FOR_RESULT,
+ nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC)) {
+ MotionEvent event = MotionEventBuilder(action, source)
+ .displayId(displayId)
+ .eventTime(eventTime)
+ .rawXCursorPosition(cursorPosition.x)
+ .rawYCursorPosition(cursorPosition.y)
+ .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(position.x)
+ .y(position.y))
+ .build();
+
+ // Inject event until dispatch out.
+ return injectMotionEvent(dispatcher, event, injectionTimeout, injectionMode);
+}
+
+static InputEventInjectionResult injectMotionDown(const sp<InputDispatcher>& dispatcher,
+ int32_t source, int32_t displayId,
+ const PointF& location = {100, 200}) {
return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, location);
}
-static int32_t injectMotionUp(const sp<InputDispatcher>& dispatcher, int32_t source,
- int32_t displayId, const PointF& location = {100, 200}) {
+static InputEventInjectionResult injectMotionUp(const sp<InputDispatcher>& dispatcher,
+ int32_t source, int32_t displayId,
+ const PointF& location = {100, 200}) {
return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, location);
}
static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) {
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid key event.
- NotifyKeyArgs args(/* id */ 0, currentTime, DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId,
- POLICY_FLAG_PASS_TO_USER, action, /* flags */ 0, AKEYCODE_A, KEY_A,
- AMETA_NONE, currentTime);
+ NotifyKeyArgs args(/* id */ 0, currentTime, 0 /*readTime*/, DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
+ displayId, POLICY_FLAG_PASS_TO_USER, action, /* flags */ 0, AKEYCODE_A,
+ KEY_A, AMETA_NONE, currentTime);
return args;
}
@@ -1017,7 +1331,7 @@
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid motion event.
- NotifyMotionArgs args(/* id */ 0, currentTime, DEVICE_ID, source, displayId,
+ NotifyMotionArgs args(/* id */ 0, currentTime, 0 /*readTime*/, DEVICE_ID, source, displayId,
POLICY_FLAG_PASS_TO_USER, action, /* actionButton */ 0, /* flags */ 0,
AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, pointerProperties,
@@ -1032,15 +1346,19 @@
return generateMotionArgs(action, source, displayId, {PointF{100, 200}});
}
+static NotifyPointerCaptureChangedArgs generatePointerCaptureChangedArgs(bool enabled) {
+ return NotifyPointerCaptureChangedArgs(/* id */ 0, systemTime(SYSTEM_TIME_MONOTONIC), enabled);
+}
+
TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
- sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window",
- ADISPLAY_ID_DEFAULT);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Window should receive motion event.
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -1055,17 +1373,17 @@
* called twice.
*/
TEST_F(InputDispatcherTest, SetInputWindowOnce_SingleWindowTouch) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
window->setFrame(Rect(0, 0, 100, 100));
- window->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+ window->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{50, 50}))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Window should receive motion event.
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -1078,18 +1396,18 @@
* when finding touched windows.
*/
TEST_F(InputDispatcherTest, SetInputWindowTwice_SingleWindowTouch) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
window->setFrame(Rect(0, 0, 100, 100));
- window->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+ window->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{50, 50}))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Window should receive motion event.
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -1097,107 +1415,225 @@
// The foreground window should receive the first touch down event.
TEST_F(InputDispatcherTest, SetInputWindow_MultiWindowsTouch) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
- sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top",
- ADISPLAY_ID_DEFAULT);
- sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
- ADISPLAY_ID_DEFAULT);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> windowTop =
+ new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> windowSecond =
+ new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Top window should receive the touch down event. Second window should not receive anything.
windowTop->consumeMotionDown(ADISPLAY_ID_DEFAULT);
windowSecond->assertNoEvents();
}
-TEST_F(InputDispatcherTest, SetInputWindow_FocusedWindow) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
- sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top",
- ADISPLAY_ID_DEFAULT);
- sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
- ADISPLAY_ID_DEFAULT);
+TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> windowLeft =
+ new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+ windowLeft->setFrame(Rect(0, 0, 600, 800));
+ windowLeft->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+ sp<FakeWindowHandle> windowRight =
+ new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ windowRight->setFrame(Rect(600, 0, 1200, 800));
+ windowRight->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
- // Set focused application.
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- // Display should have only one focused window
- windowSecond->setFocus(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowLeft, windowRight}}});
- windowSecond->consumeFocusEvent(true);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
- << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ // Start cursor position in right window so that we can move the cursor to left window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(900)
+ .y(400))
+ .build()));
+ windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
- // Focused window should receive event.
- windowTop->assertNoEvents();
- windowSecond->consumeKeyDown(ADISPLAY_ID_NONE);
+ // Move cursor into left window
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+
+ // Inject a series of mouse events for a mouse click
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ windowLeft->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ AINPUT_SOURCE_MOUSE)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ AINPUT_SOURCE_MOUSE)
+ .buttonState(0)
+ .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
+ .buttonState(0)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ windowLeft->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+
+ // Move mouse cursor back to right window
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(900)
+ .y(400))
+ .build()));
+ windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
}
-TEST_F(InputDispatcherTest, SetInputWindow_FocusPriority) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
- sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top",
- ADISPLAY_ID_DEFAULT);
- sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
- ADISPLAY_ID_DEFAULT);
+// This test is different from the test above that HOVER_ENTER and HOVER_EXIT events are injected
+// directly in this test.
+TEST_F(InputDispatcherTest, HoverEnterMouseClickAndHoverExit) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 1200, 800));
+ window->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
- // Set focused application.
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- // Display has two focused windows. Add them to inputWindowsHandles in z-order (top most first)
- windowTop->setFocus(true);
- windowSecond->setFocus(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
- windowTop->consumeFocusEvent(true);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
- << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
- // Top focused window should receive event.
- windowTop->consumeKeyDown(ADISPLAY_ID_NONE);
- windowSecond->assertNoEvents();
-}
+ // Inject a series of mouse events for a mouse click
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
-TEST_F(InputDispatcherTest, SetInputWindow_InputWindowInfo) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ AINPUT_SOURCE_MOUSE)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
- sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top",
- ADISPLAY_ID_DEFAULT);
- sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
- ADISPLAY_ID_DEFAULT);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ AINPUT_SOURCE_MOUSE)
+ .buttonState(0)
+ .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
- // Set focused application.
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
+ .buttonState(0)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ window->consumeMotionUp(ADISPLAY_ID_DEFAULT);
- windowTop->setFocus(true);
- windowSecond->setFocus(true);
- // Release channel for window is no longer valid.
- windowTop->releaseChannel();
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
- windowSecond->consumeFocusEvent(true);
-
- // Test inject a key down, should dispatch to a valid window.
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
- << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
-
- // Top window is invalid, so it should not receive any input event.
- windowTop->assertNoEvents();
- windowSecond->consumeKeyDown(ADISPLAY_ID_NONE);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_EXIT,
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
}
TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> windowLeft =
new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
windowLeft->setFrame(Rect(0, 0, 600, 800));
- windowLeft->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+ windowLeft->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
sp<FakeWindowHandle> windowRight =
new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
windowRight->setFrame(Rect(600, 0, 1200, 800));
- windowRight->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+ windowRight->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
@@ -1205,7 +1641,7 @@
// Inject an event with coordinate in the area of right window, with mouse cursor in the area of
// left window. This event should be dispatched to the left window.
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE,
ADISPLAY_ID_DEFAULT, {610, 400}, {599, 400}));
windowLeft->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -1213,12 +1649,14 @@
}
TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsKeyStream) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
- window->setFocus(true);
+ window->setFocusable(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ setFocusedWindow(window);
+
window->consumeFocusEvent(true);
NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
@@ -1236,7 +1674,7 @@
}
TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
@@ -1259,20 +1697,21 @@
}
TEST_F(InputDispatcherTest, TransferTouchFocus_OnePointer) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
// Create a couple of windows
- sp<FakeWindowHandle> firstWindow = new FakeWindowHandle(application, mDispatcher,
- "First Window", ADISPLAY_ID_DEFAULT);
- sp<FakeWindowHandle> secondWindow = new FakeWindowHandle(application, mDispatcher,
- "Second Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> firstWindow =
+ new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> secondWindow =
+ new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT);
// Add the windows to the dispatcher
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
// Send down to the first window
- NotifyMotionArgs downMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT);
+ NotifyMotionArgs downMotionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT);
mDispatcher->notifyMotion(&downMotionArgs);
// Only the first window should get the down event
firstWindow->consumeMotionDown();
@@ -1285,8 +1724,9 @@
secondWindow->consumeMotionDown();
// Send up event to the second window
- NotifyMotionArgs upMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT);
+ NotifyMotionArgs upMotionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT);
mDispatcher->notifyMotion(&upMotionArgs);
// The first window gets no events and the second gets up
firstWindow->assertNoEvents();
@@ -1294,31 +1734,34 @@
}
TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointerNoSplitTouch) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
PointF touchPoint = {10, 10};
// Create a couple of windows
- sp<FakeWindowHandle> firstWindow = new FakeWindowHandle(application, mDispatcher,
- "First Window", ADISPLAY_ID_DEFAULT);
- sp<FakeWindowHandle> secondWindow = new FakeWindowHandle(application, mDispatcher,
- "Second Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> firstWindow =
+ new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> secondWindow =
+ new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT);
// Add the windows to the dispatcher
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
// Send down to the first window
- NotifyMotionArgs downMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchPoint});
+ NotifyMotionArgs downMotionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {touchPoint});
mDispatcher->notifyMotion(&downMotionArgs);
// Only the first window should get the down event
firstWindow->consumeMotionDown();
secondWindow->assertNoEvents();
// Send pointer down to the first window
- NotifyMotionArgs pointerDownMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN
- | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchPoint, touchPoint});
+ NotifyMotionArgs pointerDownMotionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {touchPoint, touchPoint});
mDispatcher->notifyMotion(&pointerDownMotionArgs);
// Only the first window should get the pointer down event
firstWindow->consumeMotionPointerDown(1);
@@ -1332,17 +1775,20 @@
secondWindow->consumeMotionPointerDown(1);
// Send pointer up to the second window
- NotifyMotionArgs pointerUpMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP
- | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchPoint, touchPoint});
+ NotifyMotionArgs pointerUpMotionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP |
+ (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {touchPoint, touchPoint});
mDispatcher->notifyMotion(&pointerUpMotionArgs);
// The first window gets nothing and the second gets pointer up
firstWindow->assertNoEvents();
secondWindow->consumeMotionPointerUp(1);
// Send up event to the second window
- NotifyMotionArgs upMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT);
+ NotifyMotionArgs upMotionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT);
mDispatcher->notifyMotion(&upMotionArgs);
// The first window gets nothing and the second gets up
firstWindow->assertNoEvents();
@@ -1350,21 +1796,21 @@
}
TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
// Create a non touch modal window that supports split touch
- sp<FakeWindowHandle> firstWindow = new FakeWindowHandle(application, mDispatcher,
- "First Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> firstWindow =
+ new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT);
firstWindow->setFrame(Rect(0, 0, 600, 400));
- firstWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL
- | InputWindowInfo::FLAG_SPLIT_TOUCH);
+ firstWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+ InputWindowInfo::Flag::SPLIT_TOUCH);
// Create a non touch modal window that supports split touch
- sp<FakeWindowHandle> secondWindow = new FakeWindowHandle(application, mDispatcher,
- "Second Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> secondWindow =
+ new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT);
secondWindow->setFrame(Rect(0, 400, 600, 800));
- secondWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL
- | InputWindowInfo::FLAG_SPLIT_TOUCH);
+ secondWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+ InputWindowInfo::Flag::SPLIT_TOUCH);
// Add the windows to the dispatcher
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
@@ -1373,17 +1819,20 @@
PointF pointInSecond = {300, 600};
// Send down to the first window
- NotifyMotionArgs firstDownMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {pointInFirst});
+ NotifyMotionArgs firstDownMotionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {pointInFirst});
mDispatcher->notifyMotion(&firstDownMotionArgs);
// Only the first window should get the down event
firstWindow->consumeMotionDown();
secondWindow->assertNoEvents();
// Send down to the second window
- NotifyMotionArgs secondDownMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN
- | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {pointInFirst, pointInSecond});
+ NotifyMotionArgs secondDownMotionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {pointInFirst, pointInSecond});
mDispatcher->notifyMotion(&secondDownMotionArgs);
// The first window gets a move and the second a down
firstWindow->consumeMotionMove();
@@ -1396,17 +1845,20 @@
secondWindow->consumeMotionPointerDown(1);
// Send pointer up to the second window
- NotifyMotionArgs pointerUpMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP
- | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {pointInFirst, pointInSecond});
+ NotifyMotionArgs pointerUpMotionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP |
+ (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {pointInFirst, pointInSecond});
mDispatcher->notifyMotion(&pointerUpMotionArgs);
// The first window gets nothing and the second gets pointer up
firstWindow->assertNoEvents();
secondWindow->consumeMotionPointerUp(1);
// Send up event to the second window
- NotifyMotionArgs upMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT);
+ NotifyMotionArgs upMotionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT);
mDispatcher->notifyMotion(&upMotionArgs);
// The first window gets nothing and the second gets up
firstWindow->assertNoEvents();
@@ -1414,12 +1866,13 @@
}
TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
- window->setFocus(true);
+ window->setFocusable(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ setFocusedWindow(window);
window->consumeFocusEvent(true);
@@ -1431,7 +1884,7 @@
}
TEST_F(InputDispatcherTest, UnfocusedWindow_DoesNotReceiveFocusEventOrKeyEvent) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
@@ -1446,7 +1899,7 @@
// If a window is touchable, but does not have focus, it should receive motion events, but not keys
TEST_F(InputDispatcherTest, UnfocusedWindow_ReceivesMotionsButNotKeys) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
@@ -1466,14 +1919,93 @@
window->assertNoEvents(); // Key event or focus event will not be received
}
+TEST_F(InputDispatcherTest, PointerCancel_SendCancelWhenSplitTouch) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ // Create first non touch modal window that supports split touch
+ sp<FakeWindowHandle> firstWindow =
+ new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT);
+ firstWindow->setFrame(Rect(0, 0, 600, 400));
+ firstWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+ InputWindowInfo::Flag::SPLIT_TOUCH);
+
+ // Create second non touch modal window that supports split touch
+ sp<FakeWindowHandle> secondWindow =
+ new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT);
+ secondWindow->setFrame(Rect(0, 400, 600, 800));
+ secondWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+ InputWindowInfo::Flag::SPLIT_TOUCH);
+
+ // Add the windows to the dispatcher
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
+
+ PointF pointInFirst = {300, 200};
+ PointF pointInSecond = {300, 600};
+
+ // Send down to the first window
+ NotifyMotionArgs firstDownMotionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {pointInFirst});
+ mDispatcher->notifyMotion(&firstDownMotionArgs);
+ // Only the first window should get the down event
+ firstWindow->consumeMotionDown();
+ secondWindow->assertNoEvents();
+
+ // Send down to the second window
+ NotifyMotionArgs secondDownMotionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {pointInFirst, pointInSecond});
+ mDispatcher->notifyMotion(&secondDownMotionArgs);
+ // The first window gets a move and the second a down
+ firstWindow->consumeMotionMove();
+ secondWindow->consumeMotionDown();
+
+ // Send pointer cancel to the second window
+ NotifyMotionArgs pointerUpMotionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP |
+ (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {pointInFirst, pointInSecond});
+ pointerUpMotionArgs.flags |= AMOTION_EVENT_FLAG_CANCELED;
+ mDispatcher->notifyMotion(&pointerUpMotionArgs);
+ // The first window gets move and the second gets cancel.
+ firstWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_CANCELED);
+ secondWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_CANCELED);
+
+ // Send up event.
+ NotifyMotionArgs upMotionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyMotion(&upMotionArgs);
+ // The first window gets up and the second gets nothing.
+ firstWindow->consumeMotionUp();
+ secondWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, SendTimeline_DoesNotCrashDispatcher) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
+ graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 2;
+ graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = 3;
+
+ window->sendTimeline(1 /*inputEventId*/, graphicsTimeline);
+ window->assertNoEvents();
+ mDispatcher->waitForIdle();
+}
+
class FakeMonitorReceiver {
public:
FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name,
int32_t displayId, bool isGestureMonitor = false) {
- sp<InputChannel> serverChannel, clientChannel;
- InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
- mInputReceiver = std::make_unique<FakeInputReceiver>(clientChannel, name);
- dispatcher->registerInputMonitor(serverChannel, displayId, isGestureMonitor);
+ base::Result<std::unique_ptr<InputChannel>> channel =
+ dispatcher->createInputMonitor(displayId, isGestureMonitor, name, MONITOR_PID);
+ mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name);
}
sp<IBinder> getToken() { return mInputReceiver->getToken(); }
@@ -1505,7 +2037,7 @@
// Tests for gesture monitors
TEST_F(InputDispatcherTest, GestureMonitor_ReceivesMotionEvents) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
@@ -1513,35 +2045,37 @@
FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
true /*isGestureMonitor*/);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
}
TEST_F(InputDispatcherTest, GestureMonitor_DoesNotReceiveKeyEvents) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- window->setFocus(true);
+ window->setFocusable(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ setFocusedWindow(window);
+
window->consumeFocusEvent(true);
FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
true /*isGestureMonitor*/);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
- << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
monitor.assertNoEvents();
}
TEST_F(InputDispatcherTest, GestureMonitor_CanPilferAfterWindowIsRemovedMidStream) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
@@ -1549,9 +2083,9 @@
FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
true /*isGestureMonitor*/);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -1559,9 +2093,9 @@
mDispatcher->pilferPointers(monitor.getToken());
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT);
}
@@ -1570,18 +2104,19 @@
FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT,
true /*isGestureMonitor*/);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
std::optional<uint32_t> consumeSeq = monitor.receiveEvent();
ASSERT_TRUE(consumeSeq);
- mFakePolicy->assertNotifyAnrWasCalled(DISPATCHING_TIMEOUT, nullptr, monitor.getToken());
+ mFakePolicy->assertNotifyMonitorUnresponsiveWasCalled(DISPATCHING_TIMEOUT);
monitor.finishEvent(*consumeSeq);
ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertNotifyMonitorResponsiveWasCalled();
}
TEST_F(InputDispatcherTest, TestMoveEvent) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
@@ -1612,52 +2147,58 @@
* and the action of enabling / disabling.
*/
TEST_F(InputDispatcherTest, TouchModeState_IsSentToApps) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
// Set focused application.
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- window->setFocus(true);
+ window->setFocusable(true);
SCOPED_TRACE("Check default value of touch mode");
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ setFocusedWindow(window);
+
window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
SCOPED_TRACE("Remove the window to trigger focus loss");
- window->setFocus(false);
+ window->setFocusable(false);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
window->consumeFocusEvent(false /*hasFocus*/, true /*inTouchMode*/);
SCOPED_TRACE("Disable touch mode");
mDispatcher->setInTouchMode(false);
- window->setFocus(true);
+ window->setFocusable(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ setFocusedWindow(window);
window->consumeFocusEvent(true /*hasFocus*/, false /*inTouchMode*/);
SCOPED_TRACE("Remove the window to trigger focus loss");
- window->setFocus(false);
+ window->setFocusable(false);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
window->consumeFocusEvent(false /*hasFocus*/, false /*inTouchMode*/);
SCOPED_TRACE("Enable touch mode again");
mDispatcher->setInTouchMode(true);
- window->setFocus(true);
+ window->setFocusable(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ setFocusedWindow(window);
window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
window->assertNoEvents();
}
TEST_F(InputDispatcherTest, VerifyInputEvent_KeyEvent) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- window->setFocus(true);
+ window->setFocusable(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ setFocusedWindow(window);
+
window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN);
@@ -1687,7 +2228,7 @@
}
TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
@@ -1723,12 +2264,337 @@
EXPECT_EQ(motionArgs.buttonState, verifiedMotion.buttonState);
}
+TEST_F(InputDispatcherTest, NonPointerMotionEvent_NotTransformed) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
+ const std::string name = window->getName();
+
+ // Window gets transformed by offset values.
+ window->setWindowOffset(500.0f, 500.0f);
+
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ window->setFocusable(true);
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ // First, we set focused window so that focusedWindowHandle is not null.
+ setFocusedWindow(window);
+
+ // Second, we consume focus event if it is right or wrong according to onFocusChangedLocked.
+ window->consumeFocusEvent(true);
+
+ constexpr const std::array nonPointerSources = {AINPUT_SOURCE_TRACKBALL,
+ AINPUT_SOURCE_MOUSE_RELATIVE,
+ AINPUT_SOURCE_JOYSTICK};
+ for (const int source : nonPointerSources) {
+ // Notify motion with a non-pointer source.
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, source, ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ InputEvent* event = window->consume();
+ ASSERT_NE(event, nullptr);
+ ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType())
+ << name.c_str() << "expected " << inputEventTypeToString(AINPUT_EVENT_TYPE_MOTION)
+ << " event, got " << inputEventTypeToString(event->getType()) << " event";
+
+ const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
+ EXPECT_EQ(AMOTION_EVENT_ACTION_MOVE, motionEvent.getAction());
+ EXPECT_EQ(motionArgs.pointerCount, motionEvent.getPointerCount());
+
+ float expectedX = motionArgs.pointerCoords[0].getX();
+ float expectedY = motionArgs.pointerCoords[0].getY();
+
+ // Ensure the axis values from the final motion event are not transformed.
+ EXPECT_EQ(expectedX, motionEvent.getX(0))
+ << "expected " << expectedX << " for x coord of " << name.c_str() << ", got "
+ << motionEvent.getX(0);
+ EXPECT_EQ(expectedY, motionEvent.getY(0))
+ << "expected " << expectedY << " for y coord of " << name.c_str() << ", got "
+ << motionEvent.getY(0);
+ // Ensure the raw and transformed axis values for the motion event are the same.
+ EXPECT_EQ(motionEvent.getRawX(0), motionEvent.getX(0))
+ << "expected raw and transformed X-axis values to be equal";
+ EXPECT_EQ(motionEvent.getRawY(0), motionEvent.getY(0))
+ << "expected raw and transformed Y-axis values to be equal";
+ }
+}
+
+/**
+ * Ensure that separate calls to sign the same data are generating the same key.
+ * We avoid asserting against INVALID_HMAC. Since the key is random, there is a non-zero chance
+ * that a specific key and data combination would produce INVALID_HMAC, which would cause flaky
+ * tests.
+ */
+TEST_F(InputDispatcherTest, GeneratedHmac_IsConsistent) {
+ KeyEvent event = getTestKeyEvent();
+ VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEvent(event);
+
+ std::array<uint8_t, 32> hmac1 = mDispatcher->sign(verifiedEvent);
+ std::array<uint8_t, 32> hmac2 = mDispatcher->sign(verifiedEvent);
+ ASSERT_EQ(hmac1, hmac2);
+}
+
+/**
+ * Ensure that changes in VerifiedKeyEvent produce a different hmac.
+ */
+TEST_F(InputDispatcherTest, GeneratedHmac_ChangesWhenFieldsChange) {
+ KeyEvent event = getTestKeyEvent();
+ VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEvent(event);
+ std::array<uint8_t, 32> initialHmac = mDispatcher->sign(verifiedEvent);
+
+ verifiedEvent.deviceId += 1;
+ ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+
+ verifiedEvent.source += 1;
+ ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+
+ verifiedEvent.eventTimeNanos += 1;
+ ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+
+ verifiedEvent.displayId += 1;
+ ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+
+ verifiedEvent.action += 1;
+ ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+
+ verifiedEvent.downTimeNanos += 1;
+ ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+
+ verifiedEvent.flags += 1;
+ ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+
+ verifiedEvent.keyCode += 1;
+ ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+
+ verifiedEvent.scanCode += 1;
+ ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+
+ verifiedEvent.metaState += 1;
+ ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+
+ verifiedEvent.repeatCount += 1;
+ ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+}
+
+TEST_F(InputDispatcherTest, SetFocusedWindow) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> windowTop =
+ new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> windowSecond =
+ new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+ // Top window is also focusable but is not granted focus.
+ windowTop->setFocusable(true);
+ windowSecond->setFocusable(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
+ setFocusedWindow(windowSecond);
+
+ windowSecond->consumeFocusEvent(true);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+
+ // Focused window should receive event.
+ windowSecond->consumeKeyDown(ADISPLAY_ID_NONE);
+ windowTop->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, SetFocusedWindow_DropRequestInvalidChannel) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+ window->setFocusable(true);
+ // Release channel for window is no longer valid.
+ window->releaseChannel();
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ setFocusedWindow(window);
+
+ // Test inject a key down, should timeout.
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
+
+ // window channel is invalid, so it should not receive any input event.
+ window->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, SetFocusedWindow_DropRequestNoFocusableWindow) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+ // Window is not focusable.
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ setFocusedWindow(window);
+
+ // Test inject a key down, should timeout.
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
+
+ // window is invalid, so it should not receive any input event.
+ window->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, SetFocusedWindow_CheckFocusedToken) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> windowTop =
+ new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> windowSecond =
+ new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+ windowTop->setFocusable(true);
+ windowSecond->setFocusable(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
+ setFocusedWindow(windowTop);
+ windowTop->consumeFocusEvent(true);
+
+ setFocusedWindow(windowSecond, windowTop);
+ windowSecond->consumeFocusEvent(true);
+ windowTop->consumeFocusEvent(false);
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+
+ // Focused window should receive event.
+ windowSecond->consumeKeyDown(ADISPLAY_ID_NONE);
+}
+
+TEST_F(InputDispatcherTest, SetFocusedWindow_DropRequestFocusTokenNotFocused) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> windowTop =
+ new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> windowSecond =
+ new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+ windowTop->setFocusable(true);
+ windowSecond->setFocusable(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
+ setFocusedWindow(windowSecond, windowTop);
+
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
+
+ // Event should be dropped.
+ windowTop->assertNoEvents();
+ windowSecond->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, SetFocusedWindow_DeferInvisibleWindow) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> previousFocusedWindow =
+ new FakeWindowHandle(application, mDispatcher, "previousFocusedWindow",
+ ADISPLAY_ID_DEFAULT);
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+ window->setFocusable(true);
+ previousFocusedWindow->setFocusable(true);
+ window->setVisible(false);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, previousFocusedWindow}}});
+ setFocusedWindow(previousFocusedWindow);
+ previousFocusedWindow->consumeFocusEvent(true);
+
+ // Requesting focus on invisible window takes focus from currently focused window.
+ setFocusedWindow(window);
+ previousFocusedWindow->consumeFocusEvent(false);
+
+ // Injected key goes to pending queue.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */,
+ ADISPLAY_ID_DEFAULT, InputEventInjectionSync::NONE));
+
+ // Window does not get focus event or key down.
+ window->assertNoEvents();
+
+ // Window becomes visible.
+ window->setVisible(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ // Window receives focus event.
+ window->consumeFocusEvent(true);
+ // Focused window receives key down.
+ window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+}
+
+/**
+ * Launch two windows, with different owners. One window (slipperyExitWindow) has Flag::SLIPPERY,
+ * and overlaps the other window, slipperyEnterWindow. The window 'slipperyExitWindow' is on top
+ * of the 'slipperyEnterWindow'.
+ *
+ * Inject touch down into the top window. Upon receipt of the DOWN event, move the window in such
+ * a way so that the touched location is no longer covered by the top window.
+ *
+ * Next, inject a MOVE event. Because the top window already moved earlier, this event is now
+ * positioned over the bottom (slipperyEnterWindow) only. And because the top window had
+ * Flag::SLIPPERY, this will cause the top window to lose the touch event (it will receive
+ * ACTION_CANCEL instead), and the bottom window will receive a newly generated gesture (starting
+ * with ACTION_DOWN).
+ * Thus, the touch has been transferred from the top window into the bottom window, because the top
+ * window moved itself away from the touched location and had Flag::SLIPPERY.
+ *
+ * Even though the top window moved away from the touched location, it is still obscuring the bottom
+ * window. It's just not obscuring it at the touched location. That means, FLAG_WINDOW_IS_PARTIALLY_
+ * OBSCURED should be set for the MotionEvent that reaches the bottom window.
+ *
+ * In this test, we ensure that the event received by the bottom window has
+ * FLAG_WINDOW_IS_PARTIALLY_OBSCURED.
+ */
+TEST_F(InputDispatcherTest, SlipperyWindow_SetsFlagPartiallyObscured) {
+ constexpr int32_t SLIPPERY_PID = INJECTOR_PID + 1;
+ constexpr int32_t SLIPPERY_UID = INJECTOR_UID + 1;
+
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+ sp<FakeWindowHandle> slipperyExitWindow =
+ new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
+ slipperyExitWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+ InputWindowInfo::Flag::SLIPPERY);
+ // Make sure this one overlaps the bottom window
+ slipperyExitWindow->setFrame(Rect(25, 25, 75, 75));
+ // Change the owner uid/pid of the window so that it is considered to be occluding the bottom
+ // one. Windows with the same owner are not considered to be occluding each other.
+ slipperyExitWindow->setOwnerInfo(SLIPPERY_PID, SLIPPERY_UID);
+
+ sp<FakeWindowHandle> slipperyEnterWindow =
+ new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
+ slipperyExitWindow->setFrame(Rect(0, 0, 100, 100));
+
+ mDispatcher->setInputWindows(
+ {{ADISPLAY_ID_DEFAULT, {slipperyExitWindow, slipperyEnterWindow}}});
+
+ // Use notifyMotion instead of injecting to avoid dealing with injection permissions
+ NotifyMotionArgs args = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {{50, 50}});
+ mDispatcher->notifyMotion(&args);
+ slipperyExitWindow->consumeMotionDown();
+ slipperyExitWindow->setFrame(Rect(70, 70, 100, 100));
+ mDispatcher->setInputWindows(
+ {{ADISPLAY_ID_DEFAULT, {slipperyExitWindow, slipperyEnterWindow}}});
+
+ args = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {{51, 51}});
+ mDispatcher->notifyMotion(&args);
+
+ slipperyExitWindow->consumeMotionCancel();
+
+ slipperyEnterWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT,
+ AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED);
+}
+
class InputDispatcherKeyRepeatTest : public InputDispatcherTest {
protected:
static constexpr nsecs_t KEY_REPEAT_TIMEOUT = 40 * 1000000; // 40 ms
static constexpr nsecs_t KEY_REPEAT_DELAY = 40 * 1000000; // 40 ms
- sp<FakeApplicationHandle> mApp;
+ std::shared_ptr<FakeApplicationHandle> mApp;
sp<FakeWindowHandle> mWindow;
virtual void SetUp() override {
@@ -1742,17 +2608,18 @@
}
void setUpWindow() {
- mApp = new FakeApplicationHandle();
+ mApp = std::make_shared<FakeApplicationHandle>();
mWindow = new FakeWindowHandle(mApp, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
- mWindow->setFocus(true);
+ mWindow->setFocusable(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
-
+ setFocusedWindow(mWindow);
mWindow->consumeFocusEvent(true);
}
- void sendAndConsumeKeyDown() {
+ void sendAndConsumeKeyDown(int32_t deviceId) {
NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+ keyArgs.deviceId = deviceId;
keyArgs.policyFlags |= POLICY_FLAG_TRUSTED; // Otherwise it won't generate repeat event
mDispatcher->notifyKey(&keyArgs);
@@ -1774,8 +2641,9 @@
EXPECT_EQ(repeatCount, repeatKeyEvent->getRepeatCount());
}
- void sendAndConsumeKeyUp() {
+ void sendAndConsumeKeyUp(int32_t deviceId) {
NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
+ keyArgs.deviceId = deviceId;
keyArgs.policyFlags |= POLICY_FLAG_TRUSTED; // Unless it won't generate repeat event
mDispatcher->notifyKey(&keyArgs);
@@ -1786,21 +2654,59 @@
};
TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_ReceivesKeyRepeat) {
- sendAndConsumeKeyDown();
+ sendAndConsumeKeyDown(1 /* deviceId */);
+ for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
+ expectKeyRepeatOnce(repeatCount);
+ }
+}
+
+TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_ReceivesKeyRepeatFromTwoDevices) {
+ sendAndConsumeKeyDown(1 /* deviceId */);
+ for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
+ expectKeyRepeatOnce(repeatCount);
+ }
+ sendAndConsumeKeyDown(2 /* deviceId */);
+ /* repeatCount will start from 1 for deviceId 2 */
for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
expectKeyRepeatOnce(repeatCount);
}
}
TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_StopsKeyRepeatAfterUp) {
- sendAndConsumeKeyDown();
+ sendAndConsumeKeyDown(1 /* deviceId */);
expectKeyRepeatOnce(1 /*repeatCount*/);
- sendAndConsumeKeyUp();
+ sendAndConsumeKeyUp(1 /* deviceId */);
+ mWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_KeyRepeatAfterStaleDeviceKeyUp) {
+ sendAndConsumeKeyDown(1 /* deviceId */);
+ expectKeyRepeatOnce(1 /*repeatCount*/);
+ sendAndConsumeKeyDown(2 /* deviceId */);
+ expectKeyRepeatOnce(1 /*repeatCount*/);
+ // Stale key up from device 1.
+ sendAndConsumeKeyUp(1 /* deviceId */);
+ // Device 2 is still down, keep repeating
+ expectKeyRepeatOnce(2 /*repeatCount*/);
+ expectKeyRepeatOnce(3 /*repeatCount*/);
+ // Device 2 key up
+ sendAndConsumeKeyUp(2 /* deviceId */);
+ mWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_KeyRepeatStopsAfterRepeatingKeyUp) {
+ sendAndConsumeKeyDown(1 /* deviceId */);
+ expectKeyRepeatOnce(1 /*repeatCount*/);
+ sendAndConsumeKeyDown(2 /* deviceId */);
+ expectKeyRepeatOnce(1 /*repeatCount*/);
+ // Device 2 which holds the key repeating goes up, expect the repeating to stop.
+ sendAndConsumeKeyUp(2 /* deviceId */);
+ // Device 1 still holds key down, but the repeating was already stopped
mWindow->assertNoEvents();
}
TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseEventIdFromInputDispatcher) {
- sendAndConsumeKeyDown();
+ sendAndConsumeKeyDown(1 /* deviceId */);
for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
InputEvent* repeatEvent = mWindow->consume();
ASSERT_NE(nullptr, repeatEvent) << "Didn't receive event with repeat count " << repeatCount;
@@ -1810,7 +2716,7 @@
}
TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseUniqueEventId) {
- sendAndConsumeKeyDown();
+ sendAndConsumeKeyDown(1 /* deviceId */);
std::unordered_set<int32_t> idSet;
for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
@@ -1829,84 +2735,87 @@
virtual void SetUp() override {
InputDispatcherTest::SetUp();
- application1 = new FakeApplicationHandle();
- windowInPrimary = new FakeWindowHandle(application1, mDispatcher, "D_1",
- ADISPLAY_ID_DEFAULT);
+ application1 = std::make_shared<FakeApplicationHandle>();
+ windowInPrimary =
+ new FakeWindowHandle(application1, mDispatcher, "D_1", ADISPLAY_ID_DEFAULT);
// Set focus window for primary display, but focused display would be second one.
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application1);
- windowInPrimary->setFocus(true);
+ windowInPrimary->setFocusable(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowInPrimary}}});
+ setFocusedWindow(windowInPrimary);
windowInPrimary->consumeFocusEvent(true);
- application2 = new FakeApplicationHandle();
- windowInSecondary = new FakeWindowHandle(application2, mDispatcher, "D_2",
- SECOND_DISPLAY_ID);
+ application2 = std::make_shared<FakeApplicationHandle>();
+ windowInSecondary =
+ new FakeWindowHandle(application2, mDispatcher, "D_2", SECOND_DISPLAY_ID);
// Set focus to second display window.
// Set focus display to second one.
mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID);
// Set focus window for second display.
mDispatcher->setFocusedApplication(SECOND_DISPLAY_ID, application2);
- windowInSecondary->setFocus(true);
+ windowInSecondary->setFocusable(true);
mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {windowInSecondary}}});
+ setFocusedWindow(windowInSecondary);
windowInSecondary->consumeFocusEvent(true);
}
virtual void TearDown() override {
InputDispatcherTest::TearDown();
- application1.clear();
+ application1.reset();
windowInPrimary.clear();
- application2.clear();
+ application2.reset();
windowInSecondary.clear();
}
protected:
- sp<FakeApplicationHandle> application1;
+ std::shared_ptr<FakeApplicationHandle> application1;
sp<FakeWindowHandle> windowInPrimary;
- sp<FakeApplicationHandle> application2;
+ std::shared_ptr<FakeApplicationHandle> application2;
sp<FakeWindowHandle> windowInSecondary;
};
TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayTouch) {
// Test touch down on primary display.
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
windowInSecondary->assertNoEvents();
// Test touch down on second display.
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
- AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
}
TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) {
// Test inject a key down with display id specified.
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
- << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectKeyDownNoRepeat(mDispatcher, ADISPLAY_ID_DEFAULT))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->consumeKeyDown(ADISPLAY_ID_DEFAULT);
windowInSecondary->assertNoEvents();
// Test inject a key down without display id specified.
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
- << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
// Remove all windows in secondary display.
mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {}}});
- // Expect old focus should receive a cancel event.
+ // Old focus should receive a cancel event.
windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_NONE,
AKEY_EVENT_FLAG_CANCELED);
// Test inject a key down, should timeout because of no target window.
- ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher))
- << "Inject key event should return INPUT_EVENT_INJECTION_TIMED_OUT";
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDownNoRepeat(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
windowInPrimary->assertNoEvents();
windowInSecondary->consumeFocusEvent(false);
windowInSecondary->assertNoEvents();
@@ -1920,18 +2829,18 @@
FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
// Test touch down on primary display.
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
monitorInPrimary.consumeMotionDown(ADISPLAY_ID_DEFAULT);
windowInSecondary->assertNoEvents();
monitorInSecondary.assertNoEvents();
// Test touch down on second display.
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
- AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
monitorInPrimary.assertNoEvents();
windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
@@ -1940,9 +2849,9 @@
// Test inject a non-pointer motion event.
// If specific a display, it will dispatch to the focused window of particular display,
// or it will dispatch to the focused window of focused display.
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
- AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_NONE))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_NONE))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
monitorInPrimary.assertNoEvents();
windowInSecondary->consumeMotionDown(ADISPLAY_ID_NONE);
@@ -1951,21 +2860,38 @@
// Test per-display input monitors for key event.
TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorKeyEvent_MultiDisplay) {
- //Input monitor per display.
+ // Input monitor per display.
FakeMonitorReceiver monitorInPrimary =
FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
FakeMonitorReceiver monitorInSecondary =
FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
// Test inject a key down.
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
- << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
monitorInPrimary.assertNoEvents();
windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
monitorInSecondary.consumeKeyDown(ADISPLAY_ID_NONE);
}
+TEST_F(InputDispatcherFocusOnTwoDisplaysTest, CanFocusWindowOnUnfocusedDisplay) {
+ sp<FakeWindowHandle> secondWindowInPrimary =
+ new FakeWindowHandle(application1, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT);
+ secondWindowInPrimary->setFocusable(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowInPrimary, secondWindowInPrimary}}});
+ setFocusedWindow(secondWindowInPrimary);
+ windowInPrimary->consumeFocusEvent(false);
+ secondWindowInPrimary->consumeFocusEvent(true);
+
+ // Test inject a key down.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+ windowInPrimary->assertNoEvents();
+ windowInSecondary->assertNoEvents();
+ secondWindowInPrimary->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+}
+
class InputFilterTest : public InputDispatcherTest {
protected:
static constexpr int32_t SECOND_DISPLAY_ID = 1;
@@ -1973,11 +2899,11 @@
void testNotifyMotion(int32_t displayId, bool expectToBeFiltered) {
NotifyMotionArgs motionArgs;
- motionArgs = generateMotionArgs(
- AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, displayId);
+ motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, displayId);
mDispatcher->notifyMotion(&motionArgs);
- motionArgs = generateMotionArgs(
- AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, displayId);
+ motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, displayId);
mDispatcher->notifyMotion(&motionArgs);
ASSERT_TRUE(mDispatcher->waitForIdle());
if (expectToBeFiltered) {
@@ -2043,25 +2969,27 @@
virtual void SetUp() override {
InputDispatcherTest::SetUp();
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
- mUnfocusedWindow = new FakeWindowHandle(application, mDispatcher, "Top",
- ADISPLAY_ID_DEFAULT);
+ std::shared_ptr<FakeApplicationHandle> application =
+ std::make_shared<FakeApplicationHandle>();
+ mUnfocusedWindow =
+ new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
mUnfocusedWindow->setFrame(Rect(0, 0, 30, 30));
// Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
// window.
- mUnfocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+ mUnfocusedWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
mFocusedWindow =
new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
mFocusedWindow->setFrame(Rect(50, 50, 100, 100));
- mFocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+ mFocusedWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
// Set focused application.
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- mFocusedWindow->setFocus(true);
+ mFocusedWindow->setFocusable(true);
// Expect one focus window exist in display.
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+ setFocusedWindow(mFocusedWindow);
mFocusedWindow->consumeFocusEvent(true);
}
@@ -2082,10 +3010,10 @@
// DOWN on the window that doesn't have focus. Ensure the window that didn't have focus received
// the onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_Success) {
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{20, 20}))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mUnfocusedWindow->consumeMotionDown();
ASSERT_TRUE(mDispatcher->waitForIdle());
@@ -2096,9 +3024,9 @@
// DOWN on the window that doesn't have focus. Ensure no window received the
// onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonPointerSource) {
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_DEFAULT, {20, 20}))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mFocusedWindow->consumeMotionDown();
ASSERT_TRUE(mDispatcher->waitForIdle());
@@ -2108,8 +3036,9 @@
// Have two windows, one with focus. Inject KeyEvent with action DOWN on the window that doesn't
// have focus. Ensure no window received the onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonMotionFailure) {
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
- << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectKeyDownNoRepeat(mDispatcher, ADISPLAY_ID_DEFAULT))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mFocusedWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
ASSERT_TRUE(mDispatcher->waitForIdle());
@@ -2119,12 +3048,11 @@
// Have two windows, one with focus. Inject MotionEvent with source TOUCHSCREEN and action
// DOWN on the window that already has focus. Ensure no window received the
// onPointerDownOutsideFocus callback.
-TEST_F(InputDispatcherOnPointerDownOutsideFocus,
- OnPointerDownOutsideFocus_OnAlreadyFocusedWindow) {
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_OnAlreadyFocusedWindow) {
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
FOCUSED_WINDOW_TOUCH_POINT))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mFocusedWindow->consumeMotionDown();
ASSERT_TRUE(mDispatcher->waitForIdle());
@@ -2137,19 +3065,20 @@
virtual void SetUp() override {
InputDispatcherTest::SetUp();
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application =
+ std::make_shared<FakeApplicationHandle>();
mWindow1 = new FakeWindowHandle(application, mDispatcher, "Fake Window 1",
ADISPLAY_ID_DEFAULT);
// Adding FLAG_NOT_TOUCH_MODAL otherwise all taps will go to the top most window.
// We also need FLAG_SPLIT_TOUCH or we won't be able to get touches for both windows.
- mWindow1->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL |
- InputWindowInfo::FLAG_SPLIT_TOUCH);
+ mWindow1->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+ InputWindowInfo::Flag::SPLIT_TOUCH);
mWindow1->setFrame(Rect(0, 0, 100, 100));
mWindow2 = new FakeWindowHandle(application, mDispatcher, "Fake Window 2",
ADISPLAY_ID_DEFAULT, mWindow1->getToken());
- mWindow2->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL |
- InputWindowInfo::FLAG_SPLIT_TOUCH);
+ mWindow2->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+ InputWindowInfo::Flag::SPLIT_TOUCH);
mWindow2->setFrame(Rect(100, 100, 200, 200));
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow1, mWindow2}}});
@@ -2161,9 +3090,8 @@
// Helper function to convert the point from screen coordinates into the window's space
static PointF getPointInWindow(const InputWindowInfo* windowInfo, const PointF& point) {
- float x = windowInfo->windowXScale * (point.x - windowInfo->frameLeft);
- float y = windowInfo->windowYScale * (point.y - windowInfo->frameTop);
- return {x, y};
+ vec2 vals = windowInfo->transform.transform(point.x, point.y);
+ return {vals.x, vals.y};
}
void consumeMotionEvent(const sp<FakeWindowHandle>& window, int32_t expectedAction,
@@ -2193,133 +3121,123 @@
<< ", got " << motionEvent.getY(i);
}
}
+
+ void touchAndAssertPositions(int32_t action, std::vector<PointF> touchedPoints,
+ std::vector<PointF> expectedPoints) {
+ NotifyMotionArgs motionArgs = generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ // Always consume from window1 since it's the window that has the InputReceiver
+ consumeMotionEvent(mWindow1, action, expectedPoints);
+ }
};
TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchSameScale) {
// Touch Window 1
PointF touchedPoint = {10, 10};
PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint);
-
- NotifyMotionArgs motionArgs =
- generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {touchedPoint});
- mDispatcher->notifyMotion(&motionArgs);
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
// Release touch on Window 1
- motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {touchedPoint});
- mDispatcher->notifyMotion(&motionArgs);
- // consume the UP event
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_UP, {expectedPoint});
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint});
// Touch Window 2
touchedPoint = {150, 150};
expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint);
-
- motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {touchedPoint});
- mDispatcher->notifyMotion(&motionArgs);
-
- // Consuming from window1 since it's the window that has the InputReceiver
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
}
-TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentScale) {
+TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentTransform) {
+ // Set scale value for window2
mWindow2->setWindowScale(0.5f, 0.5f);
// Touch Window 1
PointF touchedPoint = {10, 10};
PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint);
-
- NotifyMotionArgs motionArgs =
- generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {touchedPoint});
- mDispatcher->notifyMotion(&motionArgs);
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
-
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
// Release touch on Window 1
- motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {touchedPoint});
- mDispatcher->notifyMotion(&motionArgs);
- // consume the UP event
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_UP, {expectedPoint});
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint});
// Touch Window 2
touchedPoint = {150, 150};
expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint);
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint});
- motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {touchedPoint});
- mDispatcher->notifyMotion(&motionArgs);
-
- // Consuming from window1 since it's the window that has the InputReceiver
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
+ // Update the transform so rotation is set
+ mWindow2->setWindowTransform(0, -1, 1, 0);
+ expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint);
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
}
-TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentScale) {
+TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentTransform) {
mWindow2->setWindowScale(0.5f, 0.5f);
// Touch Window 1
std::vector<PointF> touchedPoints = {PointF{10, 10}};
std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
-
- NotifyMotionArgs motionArgs =
- generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints);
// Touch Window 2
int32_t actionPointerDown =
AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- touchedPoints.emplace_back(PointF{150, 150});
- expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+ touchedPoints.push_back(PointF{150, 150});
+ expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+ touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints);
- motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
+ // Release Window 2
+ int32_t actionPointerUp =
+ AMOTION_EVENT_ACTION_POINTER_UP + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ touchAndAssertPositions(actionPointerUp, touchedPoints, expectedPoints);
+ expectedPoints.pop_back();
- // Consuming from window1 since it's the window that has the InputReceiver
- consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
+ // Update the transform so rotation is set for Window 2
+ mWindow2->setWindowTransform(0, -1, 1, 0);
+ expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+ touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints);
}
-TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentScale) {
+TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentTransform) {
mWindow2->setWindowScale(0.5f, 0.5f);
// Touch Window 1
std::vector<PointF> touchedPoints = {PointF{10, 10}};
std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
-
- NotifyMotionArgs motionArgs =
- generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints);
// Touch Window 2
int32_t actionPointerDown =
AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- touchedPoints.emplace_back(PointF{150, 150});
- expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+ touchedPoints.push_back(PointF{150, 150});
+ expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
- motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
-
- // Consuming from window1 since it's the window that has the InputReceiver
- consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
+ touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints);
// Move both windows
touchedPoints = {{20, 20}, {175, 175}};
expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]),
getPointInWindow(mWindow2->getInfo(), touchedPoints[1])};
- motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints);
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints);
+ // Release Window 2
+ int32_t actionPointerUp =
+ AMOTION_EVENT_ACTION_POINTER_UP + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ touchAndAssertPositions(actionPointerUp, touchedPoints, expectedPoints);
+ expectedPoints.pop_back();
+
+ // Touch Window 2
+ mWindow2->setWindowTransform(0, -1, 1, 0);
+ expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+ touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints);
+
+ // Move both windows
+ touchedPoints = {{20, 20}, {175, 175}};
+ expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]),
+ getPointInWindow(mWindow2->getInfo(), touchedPoints[1])};
+
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints);
}
TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithScale) {
@@ -2328,57 +3246,44 @@
// Touch Window 1
std::vector<PointF> touchedPoints = {PointF{10, 10}};
std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
-
- NotifyMotionArgs motionArgs =
- generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints);
// Touch Window 2
int32_t actionPointerDown =
AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- touchedPoints.emplace_back(PointF{150, 150});
- expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+ touchedPoints.push_back(PointF{150, 150});
+ expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
- motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
-
- // Consuming from window1 since it's the window that has the InputReceiver
- consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
+ touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints);
// Move both windows
touchedPoints = {{20, 20}, {175, 175}};
expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]),
getPointInWindow(mWindow2->getInfo(), touchedPoints[1])};
- motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
-
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints);
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints);
}
class InputDispatcherSingleWindowAnr : public InputDispatcherTest {
virtual void SetUp() override {
InputDispatcherTest::SetUp();
- mApplication = new FakeApplicationHandle();
+ mApplication = std::make_shared<FakeApplicationHandle>();
mApplication->setDispatchingTimeout(20ms);
mWindow =
new FakeWindowHandle(mApplication, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
mWindow->setFrame(Rect(0, 0, 30, 30));
- mWindow->setDispatchingTimeout(10ms);
- mWindow->setFocus(true);
+ mWindow->setDispatchingTimeout(30ms);
+ mWindow->setFocusable(true);
// Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
// window.
- mWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+ mWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
// Set focused application.
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ setFocusedWindow(mWindow);
mWindow->consumeFocusEvent(true);
}
@@ -2388,15 +3293,15 @@
}
protected:
- sp<FakeApplicationHandle> mApplication;
+ std::shared_ptr<FakeApplicationHandle> mApplication;
sp<FakeWindowHandle> mWindow;
static constexpr PointF WINDOW_LOCATION = {20, 20};
void tapOnWindow() {
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
}
@@ -2413,51 +3318,72 @@
// Send a regular key and respond, which should not cause an ANR.
TEST_F(InputDispatcherSingleWindowAnr, WhenKeyIsConsumed_NoAnr) {
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(mDispatcher));
mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertNotifyAnrWasNotCalled();
}
+TEST_F(InputDispatcherSingleWindowAnr, WhenFocusedApplicationChanges_NoAnr) {
+ mWindow->setFocusable(false);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mWindow->consumeFocusEvent(false);
+
+ InputEventInjectionResult result =
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT,
+ InputEventInjectionSync::NONE, 10ms /*injectionTimeout*/,
+ false /* allowKeyRepeat */);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
+ // Key will not go to window because we have no focused window.
+ // The 'no focused window' ANR timer should start instead.
+
+ // Now, the focused application goes away.
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, nullptr);
+ // The key should get dropped and there should be no ANR.
+
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertNotifyAnrWasNotCalled();
+}
+
// Send an event to the app and have the app not respond right away.
// When ANR is raised, policy will tell the dispatcher to cancel the events for that window.
// So InputDispatcher will enqueue ACTION_CANCEL event as well.
TEST_F(InputDispatcherSingleWindowAnr, OnPointerDown_BasicAnr) {
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
std::optional<uint32_t> sequenceNum = mWindow->receiveEvent(); // ACTION_DOWN
ASSERT_TRUE(sequenceNum);
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
- // The remaining lines are not really needed for the test, but kept as a sanity check
mWindow->finishEvent(*sequenceNum);
mWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL,
ADISPLAY_ID_DEFAULT, 0 /*flags*/);
ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
}
// Send a key to the app and have the app not respond right away.
TEST_F(InputDispatcherSingleWindowAnr, OnKeyDown_BasicAnr) {
// Inject a key, and don't respond - expect that ANR is called.
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(mDispatcher));
std::optional<uint32_t> sequenceNum = mWindow->receiveEvent();
ASSERT_TRUE(sequenceNum);
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
ASSERT_TRUE(mDispatcher->waitForIdle());
}
// We have a focused application, but no focused window
TEST_F(InputDispatcherSingleWindowAnr, FocusedApplication_NoFocusedWindow) {
- mWindow->setFocus(false);
+ mWindow->setFocusable(false);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
mWindow->consumeFocusEvent(false);
// taps on the window work as normal
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionDown());
@@ -2467,62 +3393,59 @@
// Once a focused event arrives, we get an ANR for this application
// We specify the injection timeout to be smaller than the application timeout, to ensure that
// injection times out (instead of failing).
- const int32_t result =
+ const InputEventInjectionResult result =
injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
- INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms);
- ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result);
+ InputEventInjectionSync::WAIT_FOR_RESULT, 10ms, false /* allowKeyRepeat */);
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/);
+ mFakePolicy->assertNotifyNoFocusedWindowAnrWasCalled(timeout, mApplication);
ASSERT_TRUE(mDispatcher->waitForIdle());
}
// We have a focused application, but no focused window
-// If the policy wants to keep waiting on the focused window to be added, make sure
-// that this timeout extension is honored and ANR is raised again.
-TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_ExtendsAnr) {
- mWindow->setFocus(false);
+// Make sure that we don't notify policy twice about the same ANR.
+TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_DoesNotSendDuplicateAnr) {
+ mWindow->setFocusable(false);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
mWindow->consumeFocusEvent(false);
- const std::chrono::duration timeout = 5ms;
- mFakePolicy->setAnrTimeout(timeout);
// Once a focused event arrives, we get an ANR for this application
// We specify the injection timeout to be smaller than the application timeout, to ensure that
// injection times out (instead of failing).
- const int32_t result =
+ const InputEventInjectionResult result =
injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
- INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms);
- ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result);
+ InputEventInjectionSync::WAIT_FOR_RESULT, 10ms, false /* allowKeyRepeat */);
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
const std::chrono::duration appTimeout =
mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyAnrWasCalled(appTimeout, mApplication, nullptr /*windowToken*/);
+ mFakePolicy->assertNotifyNoFocusedWindowAnrWasCalled(appTimeout, mApplication);
- // After the extended time has passed, ANR should be raised again
- mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/);
+ std::this_thread::sleep_for(appTimeout);
+ // ANR should not be raised again. It is up to policy to do that if it desires.
+ mFakePolicy->assertNotifyAnrWasNotCalled();
- // If we stop extending the timeout, dispatcher should go to idle.
- // Another ANR may be raised during this time
- mFakePolicy->setAnrTimeout(0ms);
+ // If we now get a focused window, the ANR should stop, but the policy handles that via
+ // 'notifyFocusChanged' callback. This is implemented in the policy so we can't test it here.
ASSERT_TRUE(mDispatcher->waitForIdle());
}
// We have a focused application, but no focused window
TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_DropsFocusedEvents) {
- mWindow->setFocus(false);
+ mWindow->setFocusable(false);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
mWindow->consumeFocusEvent(false);
// Once a focused event arrives, we get an ANR for this application
- const int32_t result =
+ const InputEventInjectionResult result =
injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
- INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms);
- ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result);
+ InputEventInjectionSync::WAIT_FOR_RESULT, 10ms);
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/);
+ mFakePolicy->assertNotifyNoFocusedWindowAnrWasCalled(timeout, mApplication);
// Future focused events get dropped right away
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, injectKeyDown(mDispatcher));
+ ASSERT_EQ(InputEventInjectionResult::FAILED, injectKeyDown(mDispatcher));
ASSERT_TRUE(mDispatcher->waitForIdle());
mWindow->assertNoEvents();
}
@@ -2542,19 +3465,19 @@
ADISPLAY_ID_DEFAULT, WINDOW_LOCATION,
{AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION},
- 500ms, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, currentTime);
+ 500ms, InputEventInjectionSync::WAIT_FOR_RESULT, currentTime);
// Now send ACTION_UP, with identical timestamp
injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, WINDOW_LOCATION,
{AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION},
- 500ms, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, currentTime);
+ 500ms, InputEventInjectionSync::WAIT_FOR_RESULT, currentTime);
// We have now sent down and up. Let's consume first event and then ANR on the second.
mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
}
// If an app is not responding to a key event, gesture monitors should continue to receive
@@ -2564,13 +3487,14 @@
FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT,
true /*isGestureMonitor*/);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT));
mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyUp(mDispatcher, ADISPLAY_ID_DEFAULT));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher, ADISPLAY_ID_DEFAULT));
// Stuck on the ACTION_UP
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr, mWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
// New tap will go to the gesture monitor, but not to the window
tapOnWindow();
@@ -2579,6 +3503,7 @@
mWindow->consumeKeyUp(ADISPLAY_ID_DEFAULT); // still the previous motion
mDispatcher->waitForIdle();
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
mWindow->assertNoEvents();
monitor.assertNoEvents();
}
@@ -2597,7 +3522,7 @@
mWindow->consumeMotionDown();
// Stuck on the ACTION_UP
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr, mWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
// New tap will go to the gesture monitor, but not to the window
tapOnWindow();
@@ -2606,6 +3531,7 @@
mWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); // still the previous motion
mDispatcher->waitForIdle();
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
mWindow->assertNoEvents();
monitor.assertNoEvents();
}
@@ -2622,46 +3548,43 @@
mWindow->consumeMotionDown();
// Block on ACTION_UP
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
mWindow->consumeMotionUp(); // Now the connection should be healthy again
mDispatcher->waitForIdle();
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
mWindow->assertNoEvents();
tapOnWindow();
mWindow->consumeMotionDown();
- mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
mWindow->consumeMotionUp();
mDispatcher->waitForIdle();
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
+ mFakePolicy->assertNotifyAnrWasNotCalled();
mWindow->assertNoEvents();
}
-// If the policy tells us to raise ANR again after some time, ensure that the timeout extension
-// is honored
-TEST_F(InputDispatcherSingleWindowAnr, Policy_CanExtendTimeout) {
- const std::chrono::duration timeout = 5ms;
- mFakePolicy->setAnrTimeout(timeout);
-
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+// If a connection remains unresponsive for a while, make sure policy is only notified once about
+// it.
+TEST_F(InputDispatcherSingleWindowAnr, Policy_DoesNotGetDuplicateAnr) {
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
const std::chrono::duration windowTimeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyAnrWasCalled(windowTimeout, nullptr /*application*/,
- mWindow->getToken());
-
- // Since the policy wanted to extend ANR, make sure it is called again after the extension
- mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
- mFakePolicy->setAnrTimeout(0ms);
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(windowTimeout, mWindow->getToken());
std::this_thread::sleep_for(windowTimeout);
- // We are not checking if ANR has been called, because it may have been called again by the
- // time we set the timeout to 0
-
- // When the policy finally says stop, we should get ACTION_CANCEL
+ // 'notifyConnectionUnresponsive' should only be called once per connection
+ mFakePolicy->assertNotifyAnrWasNotCalled();
+ // When the ANR happened, dispatcher should abort the current event stream via ACTION_CANCEL
mWindow->consumeMotionDown();
mWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL,
ADISPLAY_ID_DEFAULT, 0 /*flags*/);
mWindow->assertNoEvents();
+ mDispatcher->waitForIdle();
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
+ mFakePolicy->assertNotifyAnrWasNotCalled();
}
/**
@@ -2690,10 +3613,10 @@
// window even if motions are still being processed. But because the injection timeout is short,
// we will receive INJECTION_TIMED_OUT as the result.
- int32_t result =
+ InputEventInjectionResult result =
injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
- INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms);
- ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result);
+ InputEventInjectionSync::WAIT_FOR_RESULT, 10ms);
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
// Key will not be sent to the window, yet, because the window is still processing events
// and the key remains pending, waiting for the touch events to be processed
std::optional<uint32_t> keySequenceNum = mWindow->receiveEvent();
@@ -2725,9 +3648,9 @@
ASSERT_TRUE(upSequenceNum);
// Don't finish the events yet, and send a key
// Injection is async, so it will succeed
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */,
- ADISPLAY_ID_DEFAULT, INPUT_EVENT_INJECTION_SYNC_NONE));
+ ADISPLAY_ID_DEFAULT, InputEventInjectionSync::NONE));
// At this point, key is still pending, and should not be sent to the application yet.
std::optional<uint32_t> keySequenceNum = mWindow->receiveEvent();
ASSERT_FALSE(keySequenceNum);
@@ -2747,7 +3670,7 @@
virtual void SetUp() override {
InputDispatcherTest::SetUp();
- mApplication = new FakeApplicationHandle();
+ mApplication = std::make_shared<FakeApplicationHandle>();
mApplication->setDispatchingTimeout(10ms);
mUnfocusedWindow =
new FakeWindowHandle(mApplication, mDispatcher, "Unfocused", ADISPLAY_ID_DEFAULT);
@@ -2755,23 +3678,24 @@
// Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
// window.
// Adding FLAG_WATCH_OUTSIDE_TOUCH to receive ACTION_OUTSIDE when another window is tapped
- mUnfocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL |
- InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH |
- InputWindowInfo::FLAG_SPLIT_TOUCH);
+ mUnfocusedWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+ InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH |
+ InputWindowInfo::Flag::SPLIT_TOUCH);
mFocusedWindow =
new FakeWindowHandle(mApplication, mDispatcher, "Focused", ADISPLAY_ID_DEFAULT);
- mFocusedWindow->setDispatchingTimeout(10ms);
+ mFocusedWindow->setDispatchingTimeout(30ms);
mFocusedWindow->setFrame(Rect(50, 50, 100, 100));
- mFocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL |
- InputWindowInfo::FLAG_SPLIT_TOUCH);
+ mFocusedWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+ InputWindowInfo::Flag::SPLIT_TOUCH);
// Set focused application.
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication);
- mFocusedWindow->setFocus(true);
+ mFocusedWindow->setFocusable(true);
// Expect one focus window exist in display.
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+ setFocusedWindow(mFocusedWindow);
mFocusedWindow->consumeFocusEvent(true);
}
@@ -2783,7 +3707,7 @@
}
protected:
- sp<FakeApplicationHandle> mApplication;
+ std::shared_ptr<FakeApplicationHandle> mApplication;
sp<FakeWindowHandle> mUnfocusedWindow;
sp<FakeWindowHandle> mFocusedWindow;
static constexpr PointF UNFOCUSED_WINDOW_LOCATION = {20, 20};
@@ -2796,10 +3720,10 @@
private:
void tap(const PointF& location) {
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
location));
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
location));
}
@@ -2808,10 +3732,10 @@
// If we have 2 windows that are both unresponsive, the one with the shortest timeout
// should be ANR'd first.
TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsive) {
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
FOCUSED_WINDOW_LOCATION))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mFocusedWindow->consumeMotionDown();
mUnfocusedWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_OUTSIDE,
ADISPLAY_ID_DEFAULT, 0 /*flags*/);
@@ -2819,22 +3743,27 @@
ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertNotifyAnrWasNotCalled();
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
FOCUSED_WINDOW_LOCATION));
std::optional<uint32_t> unfocusedSequenceNum = mUnfocusedWindow->receiveEvent();
ASSERT_TRUE(unfocusedSequenceNum);
- std::optional<uint32_t> focusedSequenceNum = mFocusedWindow->receiveEvent();
- ASSERT_TRUE(focusedSequenceNum);
const std::chrono::duration timeout =
mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/,
- mFocusedWindow->getToken());
-
- mFocusedWindow->finishEvent(*focusedSequenceNum);
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mFocusedWindow->getToken());
+ // Because we injected two DOWN events in a row, CANCEL is enqueued for the first event
+ // sequence to make it consistent
+ mFocusedWindow->consumeMotionCancel();
mUnfocusedWindow->finishEvent(*unfocusedSequenceNum);
+ mFocusedWindow->consumeMotionDown();
+ // This cancel is generated because the connection was unresponsive
+ mFocusedWindow->consumeMotionCancel();
+ mFocusedWindow->assertNoEvents();
+ mUnfocusedWindow->assertNoEvents();
ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(mFocusedWindow->getToken());
+ mFakePolicy->assertNotifyAnrWasNotCalled();
}
// If we have 2 windows with identical timeouts that are both unresponsive,
@@ -2847,19 +3776,31 @@
tapOnFocusedWindow();
// we should have ACTION_DOWN/ACTION_UP on focused window and ACTION_OUTSIDE on unfocused window
- std::pair<sp<InputApplicationHandle>, sp<IBinder>> anrData1 =
- mFakePolicy->getNotifyAnrData(10ms);
- std::pair<sp<InputApplicationHandle>, sp<IBinder>> anrData2 =
- mFakePolicy->getNotifyAnrData(0ms);
+ sp<IBinder> anrConnectionToken1 = mFakePolicy->getUnresponsiveWindowToken(10ms);
+ sp<IBinder> anrConnectionToken2 = mFakePolicy->getUnresponsiveWindowToken(0ms);
// We don't know which window will ANR first. But both of them should happen eventually.
- ASSERT_TRUE(mFocusedWindow->getToken() == anrData1.second ||
- mFocusedWindow->getToken() == anrData2.second);
- ASSERT_TRUE(mUnfocusedWindow->getToken() == anrData1.second ||
- mUnfocusedWindow->getToken() == anrData2.second);
+ ASSERT_TRUE(mFocusedWindow->getToken() == anrConnectionToken1 ||
+ mFocusedWindow->getToken() == anrConnectionToken2);
+ ASSERT_TRUE(mUnfocusedWindow->getToken() == anrConnectionToken1 ||
+ mUnfocusedWindow->getToken() == anrConnectionToken2);
ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertNotifyAnrWasNotCalled();
+
+ mFocusedWindow->consumeMotionDown();
+ mFocusedWindow->consumeMotionUp();
+ mUnfocusedWindow->consumeMotionOutside();
+
+ sp<IBinder> responsiveToken1 = mFakePolicy->getResponsiveWindowToken();
+ sp<IBinder> responsiveToken2 = mFakePolicy->getResponsiveWindowToken();
+
+ // Both applications should be marked as responsive, in any order
+ ASSERT_TRUE(mFocusedWindow->getToken() == responsiveToken1 ||
+ mFocusedWindow->getToken() == responsiveToken2);
+ ASSERT_TRUE(mUnfocusedWindow->getToken() == responsiveToken1 ||
+ mUnfocusedWindow->getToken() == responsiveToken2);
+ mFakePolicy->assertNotifyAnrWasNotCalled();
}
// If a window is already not responding, the second tap on the same window should be ignored.
@@ -2876,15 +3817,14 @@
ASSERT_TRUE(upEventSequenceNum);
const std::chrono::duration timeout =
mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/,
- mFocusedWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mFocusedWindow->getToken());
// Tap once again
// We cannot use "tapOnFocusedWindow" because it asserts the injection result to be success
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
FOCUSED_WINDOW_LOCATION));
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
FOCUSED_WINDOW_LOCATION));
// Unfocused window does not receive ACTION_OUTSIDE because the tapped window is not a
@@ -2897,13 +3837,14 @@
ASSERT_TRUE(mDispatcher->waitForIdle());
// The second tap did not go to the focused window
mFocusedWindow->assertNoEvents();
- // should not have another ANR after the window just became healthy again
+ // Since all events are finished, connection should be deemed healthy again
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(mFocusedWindow->getToken());
mFakePolicy->assertNotifyAnrWasNotCalled();
}
// If you tap outside of all windows, there will not be ANR
TEST_F(InputDispatcherMultiWindowAnr, TapOutsideAllWindows_DoesNotAnr) {
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
LOCATION_OUTSIDE_ALL_WINDOWS));
ASSERT_TRUE(mDispatcher->waitForIdle());
@@ -2915,7 +3856,7 @@
mFocusedWindow->setPaused(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
FOCUSED_WINDOW_LOCATION));
@@ -2955,19 +3896,20 @@
// Injection will succeed because we will eventually give up and send the key to the focused
// window even if motions are still being processed.
- int32_t result =
+ InputEventInjectionResult result =
injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT,
- INPUT_EVENT_INJECTION_SYNC_NONE, 10ms /*injectionTimeout*/);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, result);
+ InputEventInjectionSync::NONE, 10ms /*injectionTimeout*/);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
// Key will not be sent to the window, yet, because the window is still processing events
// and the key remains pending, waiting for the touch events to be processed
std::optional<uint32_t> keySequenceNum = mFocusedWindow->receiveEvent();
ASSERT_FALSE(keySequenceNum);
// Switch the focus to the "unfocused" window that we tapped. Expect the key to go there
- mFocusedWindow->setFocus(false);
- mUnfocusedWindow->setFocus(true);
+ mFocusedWindow->setFocusable(false);
+ mUnfocusedWindow->setFocusable(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+ setFocusedWindow(mUnfocusedWindow);
// Focus events should precede the key events
mUnfocusedWindow->consumeFocusEvent(true);
@@ -2982,6 +3924,7 @@
mUnfocusedWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
mFocusedWindow->assertNoEvents();
mUnfocusedWindow->assertNoEvents();
+ mFakePolicy->assertNotifyAnrWasNotCalled();
}
// When the touch stream is split across 2 windows, and one of them does not respond,
@@ -3007,8 +3950,7 @@
const std::chrono::duration timeout =
mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/,
- mFocusedWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mFocusedWindow->getToken());
mUnfocusedWindow->consumeMotionDown();
mFocusedWindow->consumeMotionDown();
@@ -3026,10 +3968,12 @@
} else {
ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionEvent.getAction());
}
-
ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(mFocusedWindow->getToken());
+
mUnfocusedWindow->assertNoEvents();
mFocusedWindow->assertNoEvents();
+ mFakePolicy->assertNotifyAnrWasNotCalled();
}
/**
@@ -3044,11 +3988,12 @@
* but in some cases the policy may not update the focused application.
*/
TEST_F(InputDispatcherMultiWindowAnr, FocusedWindowWithoutSetFocusedApplication_NoAnr) {
- sp<FakeApplicationHandle> focusedApplication = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> focusedApplication =
+ std::make_shared<FakeApplicationHandle>();
focusedApplication->setDispatchingTimeout(60ms);
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, focusedApplication);
// The application that owns 'mFocusedWindow' and 'mUnfocusedWindow' is not focused.
- mFocusedWindow->setFocus(false);
+ mFocusedWindow->setFocusable(false);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
mFocusedWindow->consumeFocusEvent(false);
@@ -3056,10 +4001,11 @@
// Send a key. The ANR timer should start because there is no focused window.
// 'focusedApplication' will get blamed if this timer completes.
// Key will not be sent anywhere because we have no focused window. It will remain pending.
- int32_t result =
+ InputEventInjectionResult result =
injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT,
- INPUT_EVENT_INJECTION_SYNC_NONE, 10ms /*injectionTimeout*/);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, result);
+ InputEventInjectionSync::NONE, 10ms /*injectionTimeout*/,
+ false /* allowKeyRepeat */);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
// Wait until dispatcher starts the "no focused window" timer. If we don't wait here,
// then the injected touches won't cause the focused event to get dropped.
@@ -3079,8 +4025,9 @@
// We do not consume the motion right away, because that would require dispatcher to first
// process (== drop) the key event, and by that time, ANR will be raised.
// Set the focused window first.
- mFocusedWindow->setFocus(true);
+ mFocusedWindow->setFocusable(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+ setFocusedWindow(mFocusedWindow);
mFocusedWindow->consumeFocusEvent(true);
// We do not call "setFocusedApplication" here, even though the newly focused window belongs
// to another application. This could be a bug / behaviour in the policy.
@@ -3092,4 +4039,956 @@
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertNotifyAnrWasNotCalled());
}
+// These tests ensure we cannot send touch events to a window that's positioned behind a window
+// that has feature NO_INPUT_CHANNEL.
+// Layout:
+// Top (closest to user)
+// mNoInputWindow (above all windows)
+// mBottomWindow
+// Bottom (furthest from user)
+class InputDispatcherMultiWindowOcclusionTests : public InputDispatcherTest {
+ virtual void SetUp() override {
+ InputDispatcherTest::SetUp();
+
+ mApplication = std::make_shared<FakeApplicationHandle>();
+ mNoInputWindow = new FakeWindowHandle(mApplication, mDispatcher,
+ "Window without input channel", ADISPLAY_ID_DEFAULT,
+ std::make_optional<sp<IBinder>>(nullptr) /*token*/);
+
+ mNoInputWindow->setInputFeatures(InputWindowInfo::Feature::NO_INPUT_CHANNEL);
+ mNoInputWindow->setFrame(Rect(0, 0, 100, 100));
+ // It's perfectly valid for this window to not have an associated input channel
+
+ mBottomWindow = new FakeWindowHandle(mApplication, mDispatcher, "Bottom window",
+ ADISPLAY_ID_DEFAULT);
+ mBottomWindow->setFrame(Rect(0, 0, 100, 100));
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mNoInputWindow, mBottomWindow}}});
+ }
+
+protected:
+ std::shared_ptr<FakeApplicationHandle> mApplication;
+ sp<FakeWindowHandle> mNoInputWindow;
+ sp<FakeWindowHandle> mBottomWindow;
+};
+
+TEST_F(InputDispatcherMultiWindowOcclusionTests, NoInputChannelFeature_DropsTouches) {
+ PointF touchedPoint = {10, 10};
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {touchedPoint});
+ mDispatcher->notifyMotion(&motionArgs);
+
+ mNoInputWindow->assertNoEvents();
+ // Even though the window 'mNoInputWindow' positioned above 'mBottomWindow' does not have
+ // an input channel, it is not marked as FLAG_NOT_TOUCHABLE,
+ // and therefore should prevent mBottomWindow from receiving touches
+ mBottomWindow->assertNoEvents();
+}
+
+/**
+ * If a window has feature NO_INPUT_CHANNEL, and somehow (by mistake) still has an input channel,
+ * ensure that this window does not receive any touches, and blocks touches to windows underneath.
+ */
+TEST_F(InputDispatcherMultiWindowOcclusionTests,
+ NoInputChannelFeature_DropsTouchesWithValidChannel) {
+ mNoInputWindow = new FakeWindowHandle(mApplication, mDispatcher,
+ "Window with input channel and NO_INPUT_CHANNEL",
+ ADISPLAY_ID_DEFAULT);
+
+ mNoInputWindow->setInputFeatures(InputWindowInfo::Feature::NO_INPUT_CHANNEL);
+ mNoInputWindow->setFrame(Rect(0, 0, 100, 100));
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mNoInputWindow, mBottomWindow}}});
+
+ PointF touchedPoint = {10, 10};
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {touchedPoint});
+ mDispatcher->notifyMotion(&motionArgs);
+
+ mNoInputWindow->assertNoEvents();
+ mBottomWindow->assertNoEvents();
+}
+
+class InputDispatcherMirrorWindowFocusTests : public InputDispatcherTest {
+protected:
+ std::shared_ptr<FakeApplicationHandle> mApp;
+ sp<FakeWindowHandle> mWindow;
+ sp<FakeWindowHandle> mMirror;
+
+ virtual void SetUp() override {
+ InputDispatcherTest::SetUp();
+ mApp = std::make_shared<FakeApplicationHandle>();
+ mWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+ mMirror = new FakeWindowHandle(mApp, mDispatcher, "TestWindowMirror", ADISPLAY_ID_DEFAULT,
+ mWindow->getToken());
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
+ mWindow->setFocusable(true);
+ mMirror->setFocusable(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+ }
+};
+
+TEST_F(InputDispatcherMirrorWindowFocusTests, CanGetFocus) {
+ // Request focus on a mirrored window
+ setFocusedWindow(mMirror);
+
+ // window gets focused
+ mWindow->consumeFocusEvent(true);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+ mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+}
+
+// A focused & mirrored window remains focused only if the window and its mirror are both
+// focusable.
+TEST_F(InputDispatcherMirrorWindowFocusTests, FocusedIfAllWindowsFocusable) {
+ setFocusedWindow(mMirror);
+
+ // window gets focused
+ mWindow->consumeFocusEvent(true);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+ mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+ mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
+
+ mMirror->setFocusable(false);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+
+ // window loses focus since one of the windows associated with the token in not focusable
+ mWindow->consumeFocusEvent(false);
+
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
+ mWindow->assertNoEvents();
+}
+
+// A focused & mirrored window remains focused until the window and its mirror both become
+// invisible.
+TEST_F(InputDispatcherMirrorWindowFocusTests, FocusedIfAnyWindowVisible) {
+ setFocusedWindow(mMirror);
+
+ // window gets focused
+ mWindow->consumeFocusEvent(true);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+ mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+ mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
+
+ mMirror->setVisible(false);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+ mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+ mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
+
+ mWindow->setVisible(false);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+
+ // window loses focus only after all windows associated with the token become invisible.
+ mWindow->consumeFocusEvent(false);
+
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
+ mWindow->assertNoEvents();
+}
+
+// A focused & mirrored window remains focused until both windows are removed.
+TEST_F(InputDispatcherMirrorWindowFocusTests, FocusedWhileWindowsAlive) {
+ setFocusedWindow(mMirror);
+
+ // window gets focused
+ mWindow->consumeFocusEvent(true);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+ mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+ mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
+
+ // single window is removed but the window token remains focused
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mMirror}}});
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+ mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+ mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
+
+ // Both windows are removed
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {}}});
+ mWindow->consumeFocusEvent(false);
+
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
+ mWindow->assertNoEvents();
+}
+
+// Focus request can be pending until one window becomes visible.
+TEST_F(InputDispatcherMirrorWindowFocusTests, DeferFocusWhenInvisible) {
+ // Request focus on an invisible mirror.
+ mWindow->setVisible(false);
+ mMirror->setVisible(false);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+ setFocusedWindow(mMirror);
+
+ // Injected key goes to pending queue.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */,
+ ADISPLAY_ID_DEFAULT, InputEventInjectionSync::NONE));
+
+ mMirror->setVisible(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+
+ // window gets focused
+ mWindow->consumeFocusEvent(true);
+ // window gets the pending key event
+ mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+}
+
+class InputDispatcherPointerCaptureTests : public InputDispatcherTest {
+protected:
+ std::shared_ptr<FakeApplicationHandle> mApp;
+ sp<FakeWindowHandle> mWindow;
+ sp<FakeWindowHandle> mSecondWindow;
+
+ void SetUp() override {
+ InputDispatcherTest::SetUp();
+ mApp = std::make_shared<FakeApplicationHandle>();
+ mWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+ mWindow->setFocusable(true);
+ mSecondWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow2", ADISPLAY_ID_DEFAULT);
+ mSecondWindow->setFocusable(true);
+
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}});
+
+ setFocusedWindow(mWindow);
+ mWindow->consumeFocusEvent(true);
+ }
+
+ void notifyPointerCaptureChanged(bool enabled) {
+ const NotifyPointerCaptureChangedArgs args = generatePointerCaptureChangedArgs(enabled);
+ mDispatcher->notifyPointerCaptureChanged(&args);
+ }
+
+ void requestAndVerifyPointerCapture(const sp<FakeWindowHandle>& window, bool enabled) {
+ mDispatcher->requestPointerCapture(window->getToken(), enabled);
+ mFakePolicy->waitForSetPointerCapture(enabled);
+ notifyPointerCaptureChanged(enabled);
+ window->consumeCaptureEvent(enabled);
+ }
+};
+
+TEST_F(InputDispatcherPointerCaptureTests, EnablePointerCaptureWhenFocused) {
+ // Ensure that capture cannot be obtained for unfocused windows.
+ mDispatcher->requestPointerCapture(mSecondWindow->getToken(), true);
+ mFakePolicy->assertSetPointerCaptureNotCalled();
+ mSecondWindow->assertNoEvents();
+
+ // Ensure that capture can be enabled from the focus window.
+ requestAndVerifyPointerCapture(mWindow, true);
+
+ // Ensure that capture cannot be disabled from a window that does not have capture.
+ mDispatcher->requestPointerCapture(mSecondWindow->getToken(), false);
+ mFakePolicy->assertSetPointerCaptureNotCalled();
+
+ // Ensure that capture can be disabled from the window with capture.
+ requestAndVerifyPointerCapture(mWindow, false);
+}
+
+TEST_F(InputDispatcherPointerCaptureTests, DisablesPointerCaptureAfterWindowLosesFocus) {
+ requestAndVerifyPointerCapture(mWindow, true);
+
+ setFocusedWindow(mSecondWindow);
+
+ // Ensure that the capture disabled event was sent first.
+ mWindow->consumeCaptureEvent(false);
+ mWindow->consumeFocusEvent(false);
+ mSecondWindow->consumeFocusEvent(true);
+ mFakePolicy->waitForSetPointerCapture(false);
+
+ // Ensure that additional state changes from InputReader are not sent to the window.
+ notifyPointerCaptureChanged(false);
+ notifyPointerCaptureChanged(true);
+ notifyPointerCaptureChanged(false);
+ mWindow->assertNoEvents();
+ mSecondWindow->assertNoEvents();
+ mFakePolicy->assertSetPointerCaptureNotCalled();
+}
+
+TEST_F(InputDispatcherPointerCaptureTests, UnexpectedStateChangeDisablesPointerCapture) {
+ requestAndVerifyPointerCapture(mWindow, true);
+
+ // InputReader unexpectedly disables and enables pointer capture.
+ notifyPointerCaptureChanged(false);
+ notifyPointerCaptureChanged(true);
+
+ // Ensure that Pointer Capture is disabled.
+ mFakePolicy->waitForSetPointerCapture(false);
+ mWindow->consumeCaptureEvent(false);
+ mWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherPointerCaptureTests, OutOfOrderRequests) {
+ requestAndVerifyPointerCapture(mWindow, true);
+
+ // The first window loses focus.
+ setFocusedWindow(mSecondWindow);
+ mFakePolicy->waitForSetPointerCapture(false);
+ mWindow->consumeCaptureEvent(false);
+
+ // Request Pointer Capture from the second window before the notification from InputReader
+ // arrives.
+ mDispatcher->requestPointerCapture(mSecondWindow->getToken(), true);
+ mFakePolicy->waitForSetPointerCapture(true);
+
+ // InputReader notifies Pointer Capture was disabled (because of the focus change).
+ notifyPointerCaptureChanged(false);
+
+ // InputReader notifies Pointer Capture was enabled (because of mSecondWindow's request).
+ notifyPointerCaptureChanged(true);
+
+ mSecondWindow->consumeFocusEvent(true);
+ mSecondWindow->consumeCaptureEvent(true);
+}
+
+class InputDispatcherUntrustedTouchesTest : public InputDispatcherTest {
+protected:
+ constexpr static const float MAXIMUM_OBSCURING_OPACITY = 0.8;
+
+ constexpr static const float OPACITY_ABOVE_THRESHOLD = 0.9;
+ static_assert(OPACITY_ABOVE_THRESHOLD > MAXIMUM_OBSCURING_OPACITY);
+
+ constexpr static const float OPACITY_BELOW_THRESHOLD = 0.7;
+ static_assert(OPACITY_BELOW_THRESHOLD < MAXIMUM_OBSCURING_OPACITY);
+
+ // When combined twice, ie 1 - (1 - 0.5)*(1 - 0.5) = 0.75 < 8, is still below the threshold
+ constexpr static const float OPACITY_FAR_BELOW_THRESHOLD = 0.5;
+ static_assert(OPACITY_FAR_BELOW_THRESHOLD < MAXIMUM_OBSCURING_OPACITY);
+ static_assert(1 - (1 - OPACITY_FAR_BELOW_THRESHOLD) * (1 - OPACITY_FAR_BELOW_THRESHOLD) <
+ MAXIMUM_OBSCURING_OPACITY);
+
+ static const int32_t TOUCHED_APP_UID = 10001;
+ static const int32_t APP_B_UID = 10002;
+ static const int32_t APP_C_UID = 10003;
+
+ sp<FakeWindowHandle> mTouchWindow;
+
+ virtual void SetUp() override {
+ InputDispatcherTest::SetUp();
+ mTouchWindow = getWindow(TOUCHED_APP_UID, "Touched");
+ mDispatcher->setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode::BLOCK);
+ mDispatcher->setMaximumObscuringOpacityForTouch(MAXIMUM_OBSCURING_OPACITY);
+ }
+
+ virtual void TearDown() override {
+ InputDispatcherTest::TearDown();
+ mTouchWindow.clear();
+ }
+
+ sp<FakeWindowHandle> getOccludingWindow(int32_t uid, std::string name,
+ os::TouchOcclusionMode mode, float alpha = 1.0f) {
+ sp<FakeWindowHandle> window = getWindow(uid, name);
+ window->setFlags(InputWindowInfo::Flag::NOT_TOUCHABLE);
+ window->setTouchOcclusionMode(mode);
+ window->setAlpha(alpha);
+ return window;
+ }
+
+ sp<FakeWindowHandle> getWindow(int32_t uid, std::string name) {
+ std::shared_ptr<FakeApplicationHandle> app = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(app, mDispatcher, name, ADISPLAY_ID_DEFAULT);
+ // Generate an arbitrary PID based on the UID
+ window->setOwnerInfo(1777 + (uid % 10000), uid);
+ return window;
+ }
+
+ void touch(const std::vector<PointF>& points = {PointF{100, 200}}) {
+ NotifyMotionArgs args =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, points);
+ mDispatcher->notifyMotion(&args);
+ }
+};
+
+TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithBlockUntrustedOcclusionMode_BlocksTouch) {
+ const sp<FakeWindowHandle>& w =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest,
+ WindowWithBlockUntrustedOcclusionModeWithOpacityBelowThreshold_BlocksTouch) {
+ const sp<FakeWindowHandle>& w =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED, 0.7f);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest,
+ WindowWithBlockUntrustedOcclusionMode_DoesNotReceiveTouch) {
+ const sp<FakeWindowHandle>& w =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+
+ touch();
+
+ w->assertNoEvents();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithAllowOcclusionMode_AllowsTouch) {
+ const sp<FakeWindowHandle>& w = getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::ALLOW);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->consumeAnyMotionDown();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest, TouchOutsideOccludingWindow_AllowsTouch) {
+ const sp<FakeWindowHandle>& w =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED);
+ w->setFrame(Rect(0, 0, 50, 50));
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+
+ touch({PointF{100, 100}});
+
+ mTouchWindow->consumeAnyMotionDown();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest, WindowFromSameUid_AllowsTouch) {
+ const sp<FakeWindowHandle>& w =
+ getOccludingWindow(TOUCHED_APP_UID, "A", TouchOcclusionMode::BLOCK_UNTRUSTED);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->consumeAnyMotionDown();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithZeroOpacity_AllowsTouch) {
+ const sp<FakeWindowHandle>& w =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED, 0.0f);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->consumeAnyMotionDown();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithZeroOpacity_DoesNotReceiveTouch) {
+ const sp<FakeWindowHandle>& w =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED, 0.0f);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+
+ touch();
+
+ w->assertNoEvents();
+}
+
+/**
+ * This is important to make sure apps can't indirectly learn the position of touches (outside vs
+ * inside) while letting them pass-through. Note that even though touch passes through the occluding
+ * window, the occluding window will still receive ACTION_OUTSIDE event.
+ */
+TEST_F(InputDispatcherUntrustedTouchesTest,
+ WindowWithZeroOpacityAndWatchOutside_ReceivesOutsideEvent) {
+ const sp<FakeWindowHandle>& w =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED, 0.0f);
+ w->addFlags(InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+
+ touch();
+
+ w->consumeMotionOutside();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest, OutsideEvent_HasZeroCoordinates) {
+ const sp<FakeWindowHandle>& w =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED, 0.0f);
+ w->addFlags(InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+
+ touch();
+
+ InputEvent* event = w->consume();
+ ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType());
+ MotionEvent& motionEvent = static_cast<MotionEvent&>(*event);
+ EXPECT_EQ(0.0f, motionEvent.getRawPointerCoords(0)->getX());
+ EXPECT_EQ(0.0f, motionEvent.getRawPointerCoords(0)->getY());
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithOpacityBelowThreshold_AllowsTouch) {
+ const sp<FakeWindowHandle>& w =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_BELOW_THRESHOLD);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->consumeAnyMotionDown();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithOpacityAtThreshold_AllowsTouch) {
+ const sp<FakeWindowHandle>& w =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
+ MAXIMUM_OBSCURING_OPACITY);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->consumeAnyMotionDown();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithOpacityAboveThreshold_BlocksTouch) {
+ const sp<FakeWindowHandle>& w =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_ABOVE_THRESHOLD);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest, WindowsWithCombinedOpacityAboveThreshold_BlocksTouch) {
+ // Resulting opacity = 1 - (1 - 0.7)*(1 - 0.7) = .91
+ const sp<FakeWindowHandle>& w1 =
+ getOccludingWindow(APP_B_UID, "B1", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_BELOW_THRESHOLD);
+ const sp<FakeWindowHandle>& w2 =
+ getOccludingWindow(APP_B_UID, "B2", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_BELOW_THRESHOLD);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w1, w2, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest, WindowsWithCombinedOpacityBelowThreshold_AllowsTouch) {
+ // Resulting opacity = 1 - (1 - 0.5)*(1 - 0.5) = .75
+ const sp<FakeWindowHandle>& w1 =
+ getOccludingWindow(APP_B_UID, "B1", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_FAR_BELOW_THRESHOLD);
+ const sp<FakeWindowHandle>& w2 =
+ getOccludingWindow(APP_B_UID, "B2", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_FAR_BELOW_THRESHOLD);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w1, w2, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->consumeAnyMotionDown();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest,
+ WindowsFromDifferentAppsEachBelowThreshold_AllowsTouch) {
+ const sp<FakeWindowHandle>& wB =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_BELOW_THRESHOLD);
+ const sp<FakeWindowHandle>& wC =
+ getOccludingWindow(APP_C_UID, "C", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_BELOW_THRESHOLD);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wB, wC, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->consumeAnyMotionDown();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest, WindowsFromDifferentAppsOneAboveThreshold_BlocksTouch) {
+ const sp<FakeWindowHandle>& wB =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_BELOW_THRESHOLD);
+ const sp<FakeWindowHandle>& wC =
+ getOccludingWindow(APP_C_UID, "C", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_ABOVE_THRESHOLD);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wB, wC, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest,
+ WindowWithOpacityAboveThresholdAndSelfWindow_BlocksTouch) {
+ const sp<FakeWindowHandle>& wA =
+ getOccludingWindow(TOUCHED_APP_UID, "T", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_BELOW_THRESHOLD);
+ const sp<FakeWindowHandle>& wB =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_ABOVE_THRESHOLD);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wA, wB, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest,
+ WindowWithOpacityBelowThresholdAndSelfWindow_AllowsTouch) {
+ const sp<FakeWindowHandle>& wA =
+ getOccludingWindow(TOUCHED_APP_UID, "T", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_ABOVE_THRESHOLD);
+ const sp<FakeWindowHandle>& wB =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_BELOW_THRESHOLD);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wA, wB, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->consumeAnyMotionDown();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest, SelfWindowWithOpacityAboveThreshold_AllowsTouch) {
+ const sp<FakeWindowHandle>& w =
+ getOccludingWindow(TOUCHED_APP_UID, "T", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_ABOVE_THRESHOLD);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->consumeAnyMotionDown();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest, SelfWindowWithBlockUntrustedMode_AllowsTouch) {
+ const sp<FakeWindowHandle>& w =
+ getOccludingWindow(TOUCHED_APP_UID, "T", TouchOcclusionMode::BLOCK_UNTRUSTED);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->consumeAnyMotionDown();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest,
+ OpacityThresholdIs0AndWindowAboveThreshold_BlocksTouch) {
+ mDispatcher->setMaximumObscuringOpacityForTouch(0.0f);
+ const sp<FakeWindowHandle>& w =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY, 0.1f);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest, OpacityThresholdIs0AndWindowAtThreshold_AllowsTouch) {
+ mDispatcher->setMaximumObscuringOpacityForTouch(0.0f);
+ const sp<FakeWindowHandle>& w =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY, 0.0f);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->consumeAnyMotionDown();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest,
+ OpacityThresholdIs1AndWindowBelowThreshold_AllowsTouch) {
+ mDispatcher->setMaximumObscuringOpacityForTouch(1.0f);
+ const sp<FakeWindowHandle>& w =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_ABOVE_THRESHOLD);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->consumeAnyMotionDown();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest,
+ WindowWithBlockUntrustedModeAndWindowWithOpacityBelowFromSameApp_BlocksTouch) {
+ const sp<FakeWindowHandle>& w1 =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED,
+ OPACITY_BELOW_THRESHOLD);
+ const sp<FakeWindowHandle>& w2 =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_BELOW_THRESHOLD);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w1, w2, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->assertNoEvents();
+}
+
+/**
+ * Window B of BLOCK_UNTRUSTED occlusion mode is enough to block the touch, we're testing that the
+ * addition of another window (C) of USE_OPACITY occlusion mode and opacity below the threshold
+ * (which alone would result in allowing touches) does not affect the blocking behavior.
+ */
+TEST_F(InputDispatcherUntrustedTouchesTest,
+ WindowWithBlockUntrustedModeAndWindowWithOpacityBelowFromDifferentApps_BlocksTouch) {
+ const sp<FakeWindowHandle>& wB =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED,
+ OPACITY_BELOW_THRESHOLD);
+ const sp<FakeWindowHandle>& wC =
+ getOccludingWindow(APP_C_UID, "C", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_BELOW_THRESHOLD);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wB, wC, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->assertNoEvents();
+}
+
+/**
+ * This test is testing that a window from a different UID but with same application token doesn't
+ * block the touch. Apps can share the application token for close UI collaboration for example.
+ */
+TEST_F(InputDispatcherUntrustedTouchesTest,
+ WindowWithSameApplicationTokenFromDifferentApp_AllowsTouch) {
+ const sp<FakeWindowHandle>& w =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED);
+ w->setApplicationToken(mTouchWindow->getApplicationToken());
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->consumeAnyMotionDown();
+}
+
+class InputDispatcherDragTests : public InputDispatcherTest {
+protected:
+ std::shared_ptr<FakeApplicationHandle> mApp;
+ sp<FakeWindowHandle> mWindow;
+ sp<FakeWindowHandle> mSecondWindow;
+ sp<FakeWindowHandle> mDragWindow;
+
+ void SetUp() override {
+ InputDispatcherTest::SetUp();
+ mApp = std::make_shared<FakeApplicationHandle>();
+ mWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+ mWindow->setFrame(Rect(0, 0, 100, 100));
+ mWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+
+ mSecondWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow2", ADISPLAY_ID_DEFAULT);
+ mSecondWindow->setFrame(Rect(100, 0, 200, 100));
+ mSecondWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}});
+ }
+
+ // Start performing drag, we will create a drag window and transfer touch to it.
+ void performDrag() {
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ // Window should receive motion event.
+ mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+ // The drag window covers the entire display
+ mDragWindow = new FakeWindowHandle(mApp, mDispatcher, "DragWindow", ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows(
+ {{ADISPLAY_ID_DEFAULT, {mDragWindow, mWindow, mSecondWindow}}});
+
+ // Transfer touch focus to the drag window
+ mDispatcher->transferTouchFocus(mWindow->getToken(), mDragWindow->getToken(),
+ true /* isDragDrop */);
+ mWindow->consumeMotionCancel();
+ mDragWindow->consumeMotionDown();
+ }
+
+ // Start performing drag, we will create a drag window and transfer touch to it.
+ void performStylusDrag() {
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
+ AINPUT_SOURCE_STYLUS)
+ .buttonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)
+ .pointer(PointerBuilder(0,
+ AMOTION_EVENT_TOOL_TYPE_STYLUS)
+ .x(50)
+ .y(50))
+ .build()));
+ mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+ // The drag window covers the entire display
+ mDragWindow = new FakeWindowHandle(mApp, mDispatcher, "DragWindow", ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows(
+ {{ADISPLAY_ID_DEFAULT, {mDragWindow, mWindow, mSecondWindow}}});
+
+ // Transfer touch focus to the drag window
+ mDispatcher->transferTouchFocus(mWindow->getToken(), mDragWindow->getToken(),
+ true /* isDragDrop */);
+ mWindow->consumeMotionCancel();
+ mDragWindow->consumeMotionDown();
+ }
+};
+
+TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) {
+ performDrag();
+
+ // Move on window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeDragEvent(false, 50, 50);
+ mSecondWindow->assertNoEvents();
+
+ // Move to another window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {150, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeDragEvent(true, 150, 50);
+ mSecondWindow->consumeDragEvent(false, 50, 50);
+
+ // Move back to original window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeDragEvent(false, 50, 50);
+ mSecondWindow->consumeDragEvent(true, -50, 50);
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+ mWindow->assertNoEvents();
+ mSecondWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherDragTests, DragAndDrop) {
+ performDrag();
+
+ // Move on window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeDragEvent(false, 50, 50);
+ mSecondWindow->assertNoEvents();
+
+ // Move to another window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {150, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeDragEvent(true, 150, 50);
+ mSecondWindow->consumeDragEvent(false, 50, 50);
+
+ // drop to another window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {150, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+ mFakePolicy->assertDropTargetEquals(mSecondWindow->getToken());
+ mWindow->assertNoEvents();
+ mSecondWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherDragTests, StylusDragAndDrop) {
+ performStylusDrag();
+
+ // Move on window and keep button pressed.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .buttonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS)
+ .x(50)
+ .y(50))
+ .build()))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeDragEvent(false, 50, 50);
+ mSecondWindow->assertNoEvents();
+
+ // Move to another window and release button, expect to drop item.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .buttonState(0)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS)
+ .x(150)
+ .y(50))
+ .build()))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ mWindow->assertNoEvents();
+ mSecondWindow->assertNoEvents();
+ mFakePolicy->assertDropTargetEquals(mSecondWindow->getToken());
+
+ // nothing to the window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_STYLUS)
+ .buttonState(0)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS)
+ .x(150)
+ .y(50))
+ .build()))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+ mWindow->assertNoEvents();
+ mSecondWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherDragTests, DragAndDrop_InvalidWindow) {
+ performDrag();
+
+ // Set second window invisible.
+ mSecondWindow->setVisible(false);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mDragWindow, mWindow, mSecondWindow}}});
+
+ // Move on window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeDragEvent(false, 50, 50);
+ mSecondWindow->assertNoEvents();
+
+ // Move to another window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {150, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeDragEvent(true, 150, 50);
+ mSecondWindow->assertNoEvents();
+
+ // drop to another window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {150, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+ mFakePolicy->assertDropTargetEquals(nullptr);
+ mWindow->assertNoEvents();
+ mSecondWindow->assertNoEvents();
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputFlingerService_test.cpp b/services/inputflinger/tests/InputFlingerService_test.cpp
new file mode 100644
index 0000000..c368e79
--- /dev/null
+++ b/services/inputflinger/tests/InputFlingerService_test.cpp
@@ -0,0 +1,462 @@
+/*
+ * Copyright (C) 2020 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 <BnInputFlingerQuery.h>
+#include <IInputFlingerQuery.h>
+
+#include <android/os/BnInputFlinger.h>
+#include <android/os/BnSetInputWindowsListener.h>
+#include <android/os/IInputFlinger.h>
+#include <android/os/ISetInputWindowsListener.h>
+
+#include <binder/Binder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/ProcessState.h>
+
+#include <input/Input.h>
+#include <input/InputTransport.h>
+#include <input/InputWindow.h>
+
+#include <gtest/gtest.h>
+#include <inttypes.h>
+#include <linux/uinput.h>
+#include <log/log.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+#include <chrono>
+#include <thread>
+#include <unordered_map>
+
+#define TAG "InputFlingerServiceTest"
+
+using android::os::BnInputFlinger;
+using android::os::BnSetInputWindowsListener;
+using android::os::IInputFlinger;
+using android::os::ISetInputWindowsListener;
+
+using std::chrono_literals::operator""ms;
+using std::chrono_literals::operator""s;
+
+namespace android {
+
+static const sp<IBinder> TestInfoToken = new BBinder();
+static const sp<IBinder> FocusedTestInfoToken = new BBinder();
+static constexpr int32_t TestInfoId = 1;
+static const std::string TestInfoName = "InputFlingerServiceTestInputWindowInfo";
+static constexpr Flags<InputWindowInfo::Flag> TestInfoFlags = InputWindowInfo::Flag::NOT_FOCUSABLE;
+static constexpr InputWindowInfo::Type TestInfoType = InputWindowInfo::Type::INPUT_METHOD;
+static constexpr std::chrono::duration TestInfoDispatchingTimeout = 2532ms;
+static constexpr int32_t TestInfoFrameLeft = 93;
+static constexpr int32_t TestInfoFrameTop = 34;
+static constexpr int32_t TestInfoFrameRight = 16;
+static constexpr int32_t TestInfoFrameBottom = 19;
+static constexpr int32_t TestInfoSurfaceInset = 17;
+static constexpr float TestInfoGlobalScaleFactor = 0.3;
+static constexpr float TestInfoWindowXScale = 0.4;
+static constexpr float TestInfoWindowYScale = 0.5;
+static const Rect TestInfoTouchableRegionRect = {100 /* left */, 150 /* top */, 400 /* right */,
+ 450 /* bottom */};
+static const Region TestInfoTouchableRegion(TestInfoTouchableRegionRect);
+static constexpr bool TestInfoVisible = false;
+static constexpr bool TestInfoTrustedOverlay = true;
+static constexpr bool TestInfoFocusable = false;
+static constexpr bool TestInfoHasWallpaper = false;
+static constexpr bool TestInfoPaused = false;
+static constexpr int32_t TestInfoOwnerPid = 19;
+static constexpr int32_t TestInfoOwnerUid = 24;
+static constexpr InputWindowInfo::Feature TestInfoInputFeatures =
+ InputWindowInfo::Feature::NO_INPUT_CHANNEL;
+static constexpr int32_t TestInfoDisplayId = 34;
+static constexpr int32_t TestInfoPortalToDisplayId = 2;
+static constexpr bool TestInfoReplaceTouchableRegionWithCrop = true;
+static const sp<IBinder> TestInfoTouchableRegionCropHandle = new BBinder();
+
+static const std::string TestAppInfoName = "InputFlingerServiceTestInputApplicationInfo";
+static const sp<IBinder> TestAppInfoToken = new BBinder();
+static constexpr std::chrono::duration TestAppInfoDispatchingTimeout = 12345678ms;
+
+static const String16 kTestServiceName = String16("InputFlingerService");
+static const String16 kQueryServiceName = String16("InputFlingerQueryService");
+
+struct SetInputWindowsListener;
+// --- InputFlingerServiceTest ---
+class InputFlingerServiceTest : public testing::Test {
+public:
+ void SetUp() override;
+ void TearDown() override;
+
+protected:
+ void InitializeInputFlinger();
+ void setInputWindowsByInfos(const std::vector<InputWindowInfo>& infos);
+ void setFocusedWindow(const sp<IBinder> token, const sp<IBinder> focusedToken,
+ nsecs_t timestampNanos);
+
+ void setInputWindowsFinished();
+ void verifyInputWindowInfo(const InputWindowInfo& info) const;
+ InputWindowInfo& getInfo() const { return const_cast<InputWindowInfo&>(mInfo); }
+
+ sp<IInputFlinger> mService;
+ sp<IInputFlingerQuery> mQuery;
+
+private:
+ sp<SetInputWindowsListener> mSetInputWindowsListener;
+ std::unique_ptr<InputChannel> mServerChannel, mClientChannel;
+ InputWindowInfo mInfo;
+ std::mutex mLock;
+ std::condition_variable mSetInputWindowsFinishedCondition;
+};
+
+struct SetInputWindowsListener : BnSetInputWindowsListener {
+ explicit SetInputWindowsListener(std::function<void()> cbFunc) : mCbFunc(cbFunc) {}
+
+ binder::Status onSetInputWindowsFinished() override;
+
+ std::function<void()> mCbFunc;
+};
+
+class TestInputManager : public BnInputFlinger {
+protected:
+ virtual ~TestInputManager(){};
+
+public:
+ TestInputManager(){};
+
+ binder::Status getInputWindows(std::vector<::android::InputWindowInfo>* inputHandles);
+ binder::Status getInputChannels(std::vector<::android::InputChannel>* channels);
+ binder::Status getLastFocusRequest(FocusRequest*);
+
+ status_t dump(int fd, const Vector<String16>& args) override;
+
+ binder::Status setInputWindows(
+ const std::vector<InputWindowInfo>& handles,
+ const sp<ISetInputWindowsListener>& setInputWindowsListener) override;
+
+ binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override;
+ binder::Status removeInputChannel(const sp<IBinder>& connectionToken) override;
+ binder::Status setFocusedWindow(const FocusRequest&) override;
+
+ void reset();
+
+private:
+ mutable Mutex mLock;
+ std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mHandlesPerDisplay;
+ std::vector<std::shared_ptr<InputChannel>> mInputChannels;
+ FocusRequest mFocusRequest;
+};
+
+class TestInputQuery : public BnInputFlingerQuery {
+public:
+ TestInputQuery(sp<android::TestInputManager> manager) : mManager(manager){};
+ binder::Status getInputWindows(std::vector<::android::InputWindowInfo>* inputHandles) override;
+ binder::Status getInputChannels(std::vector<::android::InputChannel>* channels) override;
+ binder::Status getLastFocusRequest(FocusRequest*) override;
+ binder::Status resetInputManager() override;
+
+private:
+ sp<android::TestInputManager> mManager;
+};
+
+binder::Status TestInputQuery::getInputWindows(
+ std::vector<::android::InputWindowInfo>* inputHandles) {
+ return mManager->getInputWindows(inputHandles);
+}
+
+binder::Status TestInputQuery::getInputChannels(std::vector<::android::InputChannel>* channels) {
+ return mManager->getInputChannels(channels);
+}
+
+binder::Status TestInputQuery::getLastFocusRequest(FocusRequest* request) {
+ return mManager->getLastFocusRequest(request);
+}
+
+binder::Status TestInputQuery::resetInputManager() {
+ mManager->reset();
+ return binder::Status::ok();
+}
+
+binder::Status SetInputWindowsListener::onSetInputWindowsFinished() {
+ if (mCbFunc != nullptr) {
+ mCbFunc();
+ }
+ return binder::Status::ok();
+}
+
+binder::Status TestInputManager::setInputWindows(
+ const std::vector<InputWindowInfo>& infos,
+ const sp<ISetInputWindowsListener>& setInputWindowsListener) {
+ AutoMutex _l(mLock);
+
+ for (const auto& info : infos) {
+ mHandlesPerDisplay.emplace(info.displayId, std::vector<sp<InputWindowHandle>>());
+ mHandlesPerDisplay[info.displayId].push_back(new InputWindowHandle(info));
+ }
+ if (setInputWindowsListener) {
+ setInputWindowsListener->onSetInputWindowsFinished();
+ }
+ return binder::Status::ok();
+}
+
+binder::Status TestInputManager::createInputChannel(const std::string& name,
+ InputChannel* outChannel) {
+ AutoMutex _l(mLock);
+ std::unique_ptr<InputChannel> serverChannel;
+ std::unique_ptr<InputChannel> clientChannel;
+ InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
+
+ clientChannel->copyTo(*outChannel);
+
+ mInputChannels.emplace_back(std::move(serverChannel));
+
+ return binder::Status::ok();
+}
+
+binder::Status TestInputManager::removeInputChannel(const sp<IBinder>& connectionToken) {
+ AutoMutex _l(mLock);
+
+ auto it = std::find_if(mInputChannels.begin(), mInputChannels.end(),
+ [&](std::shared_ptr<InputChannel>& c) {
+ return c->getConnectionToken() == connectionToken;
+ });
+ if (it != mInputChannels.end()) {
+ mInputChannels.erase(it);
+ }
+
+ return binder::Status::ok();
+}
+
+status_t TestInputManager::dump(int fd, const Vector<String16>& args) {
+ std::string dump;
+
+ dump += " InputFlinger dump\n";
+
+ ::write(fd, dump.c_str(), dump.size());
+ return NO_ERROR;
+}
+
+binder::Status TestInputManager::getInputWindows(
+ std::vector<::android::InputWindowInfo>* inputInfos) {
+ for (auto& [displayId, inputHandles] : mHandlesPerDisplay) {
+ for (auto& inputHandle : inputHandles) {
+ inputInfos->push_back(*inputHandle->getInfo());
+ }
+ }
+ return binder::Status::ok();
+}
+
+binder::Status TestInputManager::getInputChannels(std::vector<::android::InputChannel>* channels) {
+ channels->clear();
+ for (std::shared_ptr<InputChannel>& channel : mInputChannels) {
+ channels->push_back(*channel);
+ }
+ return binder::Status::ok();
+}
+
+binder::Status TestInputManager::getLastFocusRequest(FocusRequest* request) {
+ *request = mFocusRequest;
+ return binder::Status::ok();
+}
+
+binder::Status TestInputManager::setFocusedWindow(const FocusRequest& request) {
+ mFocusRequest = request;
+ return binder::Status::ok();
+}
+
+void TestInputManager::reset() {
+ mHandlesPerDisplay.clear();
+ mInputChannels.clear();
+ mFocusRequest = FocusRequest();
+}
+
+void InputFlingerServiceTest::SetUp() {
+ mSetInputWindowsListener = new SetInputWindowsListener([&]() {
+ std::unique_lock<std::mutex> lock(mLock);
+ mSetInputWindowsFinishedCondition.notify_all();
+ });
+ InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel);
+
+ mInfo.token = TestInfoToken;
+ mInfo.id = TestInfoId;
+ mInfo.name = TestInfoName;
+ mInfo.flags = TestInfoFlags;
+ mInfo.type = TestInfoType;
+ mInfo.dispatchingTimeout = TestInfoDispatchingTimeout;
+ mInfo.frameLeft = TestInfoFrameLeft;
+ mInfo.frameTop = TestInfoFrameTop;
+ mInfo.frameRight = TestInfoFrameRight;
+ mInfo.frameBottom = TestInfoFrameBottom;
+ mInfo.surfaceInset = TestInfoSurfaceInset;
+ mInfo.globalScaleFactor = TestInfoGlobalScaleFactor;
+ mInfo.transform.set({TestInfoWindowXScale, 0, TestInfoFrameLeft, 0, TestInfoWindowYScale,
+ TestInfoFrameTop, 0, 0, 1});
+ mInfo.touchableRegion = TestInfoTouchableRegion;
+ mInfo.visible = TestInfoVisible;
+ mInfo.trustedOverlay = TestInfoTrustedOverlay;
+ mInfo.focusable = TestInfoFocusable;
+
+ mInfo.hasWallpaper = TestInfoHasWallpaper;
+ mInfo.paused = TestInfoPaused;
+ mInfo.ownerPid = TestInfoOwnerPid;
+ mInfo.ownerUid = TestInfoOwnerUid;
+ mInfo.inputFeatures = TestInfoInputFeatures;
+ mInfo.displayId = TestInfoDisplayId;
+ mInfo.portalToDisplayId = TestInfoPortalToDisplayId;
+ mInfo.replaceTouchableRegionWithCrop = TestInfoReplaceTouchableRegionWithCrop;
+ mInfo.touchableRegionCropHandle = TestInfoTouchableRegionCropHandle;
+
+ mInfo.applicationInfo.name = TestAppInfoName;
+ mInfo.applicationInfo.token = TestAppInfoToken;
+ mInfo.applicationInfo.dispatchingTimeoutMillis =
+ std::chrono::duration_cast<std::chrono::milliseconds>(TestAppInfoDispatchingTimeout)
+ .count();
+
+ InitializeInputFlinger();
+}
+
+void InputFlingerServiceTest::TearDown() {
+ mQuery->resetInputManager();
+}
+
+void InputFlingerServiceTest::verifyInputWindowInfo(const InputWindowInfo& info) const {
+ EXPECT_EQ(mInfo, info);
+}
+
+void InputFlingerServiceTest::InitializeInputFlinger() {
+ sp<IBinder> input(defaultServiceManager()->waitForService(kTestServiceName));
+ ASSERT_TRUE(input != nullptr);
+ mService = interface_cast<IInputFlinger>(input);
+
+ input = defaultServiceManager()->waitForService(kQueryServiceName);
+ ASSERT_TRUE(input != nullptr);
+ mQuery = interface_cast<IInputFlingerQuery>(input);
+}
+
+void InputFlingerServiceTest::setInputWindowsByInfos(const std::vector<InputWindowInfo>& infos) {
+ std::unique_lock<std::mutex> lock(mLock);
+ mService->setInputWindows(infos, mSetInputWindowsListener);
+ // Verify listener call
+ EXPECT_NE(mSetInputWindowsFinishedCondition.wait_for(lock, 1s), std::cv_status::timeout);
+}
+
+void InputFlingerServiceTest::setFocusedWindow(const sp<IBinder> token,
+ const sp<IBinder> focusedToken,
+ nsecs_t timestampNanos) {
+ FocusRequest request;
+ request.token = TestInfoToken;
+ request.focusedToken = focusedToken;
+ request.timestamp = timestampNanos;
+ mService->setFocusedWindow(request);
+ // call set input windows and wait for the callback to drain the queue.
+ setInputWindowsByInfos(std::vector<InputWindowInfo>());
+}
+
+/**
+ * Test InputFlinger service interface SetInputWindows
+ */
+TEST_F(InputFlingerServiceTest, InputWindow_SetInputWindows) {
+ std::vector<InputWindowInfo> infos = {getInfo()};
+ setInputWindowsByInfos(infos);
+
+ // Verify input windows from service
+ std::vector<::android::InputWindowInfo> windowInfos;
+ mQuery->getInputWindows(&windowInfos);
+ for (const ::android::InputWindowInfo& windowInfo : windowInfos) {
+ verifyInputWindowInfo(windowInfo);
+ }
+}
+
+/**
+ * Test InputFlinger service interface createInputChannel
+ */
+TEST_F(InputFlingerServiceTest, CreateInputChannelReturnsUnblockedFd) {
+ // Test that the unblocked file descriptor flag is kept across processes over binder
+ // transactions.
+
+ InputChannel channel;
+ ASSERT_TRUE(mService->createInputChannel("testchannels", &channel).isOk());
+
+ const base::unique_fd& fd = channel.getFd();
+ ASSERT_TRUE(fd.ok());
+
+ const int result = fcntl(fd, F_GETFL);
+ EXPECT_NE(result, -1);
+ EXPECT_EQ(result & O_NONBLOCK, O_NONBLOCK);
+}
+
+TEST_F(InputFlingerServiceTest, InputWindow_CreateInputChannel) {
+ InputChannel channel;
+ ASSERT_TRUE(mService->createInputChannel("testchannels", &channel).isOk());
+
+ std::vector<::android::InputChannel> channels;
+ mQuery->getInputChannels(&channels);
+ ASSERT_EQ(channels.size(), 1UL);
+ EXPECT_EQ(channels[0].getConnectionToken(), channel.getConnectionToken());
+
+ mService->removeInputChannel(channel.getConnectionToken());
+ mQuery->getInputChannels(&channels);
+ EXPECT_EQ(channels.size(), 0UL);
+}
+
+TEST_F(InputFlingerServiceTest, InputWindow_setFocusedWindow) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ setFocusedWindow(TestInfoToken, nullptr /* focusedToken */, now);
+
+ FocusRequest request;
+ mQuery->getLastFocusRequest(&request);
+
+ EXPECT_EQ(request.token, TestInfoToken);
+ EXPECT_EQ(request.focusedToken, nullptr);
+ EXPECT_EQ(request.timestamp, now);
+}
+
+TEST_F(InputFlingerServiceTest, InputWindow_setFocusedWindowWithFocusedToken) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ setFocusedWindow(TestInfoToken, FocusedTestInfoToken, now);
+
+ FocusRequest request;
+ mQuery->getLastFocusRequest(&request);
+
+ EXPECT_EQ(request.token, TestInfoToken);
+ EXPECT_EQ(request.focusedToken, FocusedTestInfoToken);
+ EXPECT_EQ(request.timestamp, now);
+}
+
+} // namespace android
+
+int main(int argc, char** argv) {
+ pid_t forkPid = fork();
+
+ if (forkPid == 0) {
+ // Server process
+ android::sp<android::TestInputManager> manager = new android::TestInputManager();
+ android::sp<android::TestInputQuery> query = new android::TestInputQuery(manager);
+
+ android::defaultServiceManager()->addService(android::kTestServiceName, manager,
+ false /*allowIsolated*/);
+ android::defaultServiceManager()->addService(android::kQueryServiceName, query,
+ false /*allowIsolated*/);
+ android::ProcessState::self()->startThreadPool();
+ android::IPCThreadState::self()->joinThreadPool();
+ } else {
+ android::ProcessState::self()->startThreadPool();
+ ::testing::InitGoogleTest(&argc, argv);
+ int result = RUN_ALL_TESTS();
+ kill(forkPid, SIGKILL);
+ return result;
+ }
+ return 0;
+}
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index e9bb169..5eaca71 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -22,27 +22,35 @@
#include <InputReaderFactory.h>
#include <KeyboardInputMapper.h>
#include <MultiTouchInputMapper.h>
+#include <PeripheralController.h>
+#include <SensorInputMapper.h>
#include <SingleTouchInputMapper.h>
#include <SwitchInputMapper.h>
#include <TestInputListener.h>
#include <TouchInputMapper.h>
#include <UinputDevice.h>
+#include <VibratorInputMapper.h>
#include <android-base/thread_annotations.h>
#include <gtest/gtest.h>
#include <inttypes.h>
#include <math.h>
#include <memory>
+#include <regex>
+#include "input/DisplayViewport.h"
+#include "input/Input.h"
namespace android {
using std::chrono_literals::operator""ms;
+using namespace android::flag_operators;
// Timeout for waiting for an expected event
static constexpr std::chrono::duration WAIT_TIMEOUT = 100ms;
// An arbitrary time value.
-static const nsecs_t ARBITRARY_TIME = 1234;
+static constexpr nsecs_t ARBITRARY_TIME = 1234;
+static constexpr nsecs_t READ_TIME = 4321;
// Arbitrary display properties.
static constexpr int32_t DISPLAY_ID = 0;
@@ -62,6 +70,12 @@
static constexpr int32_t FIRST_TRACKING_ID = 0;
static constexpr int32_t SECOND_TRACKING_ID = 1;
static constexpr int32_t THIRD_TRACKING_ID = 2;
+static constexpr int32_t DEFAULT_BATTERY = 1;
+static constexpr int32_t BATTERY_STATUS = 4;
+static constexpr int32_t BATTERY_CAPACITY = 66;
+static constexpr int32_t LIGHT_BRIGHTNESS = 0x55000000;
+static constexpr int32_t LIGHT_COLOR = 0x7F448866;
+static constexpr int32_t LIGHT_PLAYER_ID = 2;
// Error tolerance for floating point assertions.
static const float EPSILON = 0.001f;
@@ -75,6 +89,10 @@
return (x + y) / 2;
}
+// Mapping for light color name and the light color
+const std::unordered_map<std::string, LightColor> LIGHT_COLORS = {{"red", LightColor::RED},
+ {"green", LightColor::GREEN},
+ {"blue", LightColor::BLUE}};
// --- FakePointerController ---
@@ -101,29 +119,23 @@
mMaxY = maxY;
}
- virtual void setPosition(float x, float y) {
+ void setPosition(float x, float y) override {
mX = x;
mY = y;
}
- virtual void setButtonState(int32_t buttonState) {
- mButtonState = buttonState;
- }
+ void setButtonState(int32_t buttonState) override { mButtonState = buttonState; }
- virtual int32_t getButtonState() const {
- return mButtonState;
- }
+ int32_t getButtonState() const override { return mButtonState; }
- virtual void getPosition(float* outX, float* outY) const {
+ void getPosition(float* outX, float* outY) const override {
*outX = mX;
*outY = mY;
}
- virtual int32_t getDisplayId() const {
- return mDisplayId;
- }
+ int32_t getDisplayId() const override { return mDisplayId; }
- virtual void setDisplayViewport(const DisplayViewport& viewport) {
+ void setDisplayViewport(const DisplayViewport& viewport) override {
mDisplayId = viewport.displayId;
}
@@ -132,7 +144,7 @@
}
private:
- virtual bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const {
+ bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const override {
*outMinX = mMinX;
*outMinY = mMinY;
*outMaxX = mMaxX;
@@ -140,7 +152,7 @@
return mHaveBounds;
}
- virtual void move(float deltaX, float deltaY) {
+ void move(float deltaX, float deltaY) override {
mX += deltaX;
if (mX < mMinX) mX = mMinX;
if (mX > mMaxX) mX = mMaxX;
@@ -149,17 +161,14 @@
if (mY > mMaxY) mY = mMaxY;
}
- virtual void fade(Transition) {
- }
+ void fade(Transition) override {}
- virtual void unfade(Transition) {
- }
+ void unfade(Transition) override {}
- virtual void setPresentation(Presentation) {
- }
+ void setPresentation(Presentation) override {}
- virtual void setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits,
- int32_t displayId) {
+ void setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits,
+ int32_t displayId) override {
std::vector<int32_t> newSpots;
// Add spots for fingers that are down.
for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) {
@@ -170,8 +179,7 @@
mSpotsByDisplay[displayId] = newSpots;
}
- virtual void clearSpots() {
- }
+ void clearSpots() override {}
std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay;
};
@@ -191,7 +199,7 @@
TouchAffineTransformation transform;
protected:
- virtual ~FakeInputReaderPolicy() { }
+ virtual ~FakeInputReaderPolicy() {}
public:
FakeInputReaderPolicy() {
@@ -230,10 +238,11 @@
}
void addDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation,
- const std::string& uniqueId, std::optional<uint8_t> physicalPort,
- ViewportType viewportType) {
- const DisplayViewport viewport = createDisplayViewport(displayId, width, height,
- orientation, uniqueId, physicalPort, viewportType);
+ bool isActive, const std::string& uniqueId,
+ std::optional<uint8_t> physicalPort, ViewportType viewportType) {
+ const DisplayViewport viewport =
+ createDisplayViewport(displayId, width, height, orientation, isActive, uniqueId,
+ physicalPort, viewportType);
mViewports.push_back(viewport);
mConfig.setDisplayViewports(mViewports);
}
@@ -260,6 +269,11 @@
mConfig.portAssociations.insert({inputPort, displayPort});
}
+ void addInputUniqueIdAssociation(const std::string& inputUniqueId,
+ const std::string& displayUniqueId) {
+ mConfig.uniqueIdAssociations.insert({inputUniqueId, displayUniqueId});
+ }
+
void addDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.insert(deviceId); }
void removeDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.erase(deviceId); }
@@ -301,8 +315,9 @@
private:
DisplayViewport createDisplayViewport(int32_t displayId, int32_t width, int32_t height,
- int32_t orientation, const std::string& uniqueId, std::optional<uint8_t> physicalPort,
- ViewportType type) {
+ int32_t orientation, bool isActive,
+ const std::string& uniqueId,
+ std::optional<uint8_t> physicalPort, ViewportType type) {
bool isRotated = (orientation == DISPLAY_ORIENTATION_90
|| orientation == DISPLAY_ORIENTATION_270);
DisplayViewport v;
@@ -318,34 +333,34 @@
v.physicalBottom = isRotated ? width : height;
v.deviceWidth = isRotated ? height : width;
v.deviceHeight = isRotated ? width : height;
+ v.isActive = isActive;
v.uniqueId = uniqueId;
v.physicalPort = physicalPort;
v.type = type;
return v;
}
- virtual void getReaderConfiguration(InputReaderConfiguration* outConfig) {
+ void getReaderConfiguration(InputReaderConfiguration* outConfig) override {
*outConfig = mConfig;
}
- virtual std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId) {
+ std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId) override {
return mPointerControllers[deviceId];
}
- virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) {
+ void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override {
std::scoped_lock<std::mutex> lock(mLock);
mInputDevices = inputDevices;
mInputDevicesChanged = true;
mDevicesChangedCondition.notify_all();
}
- virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier&) {
+ std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
+ const InputDeviceIdentifier&) override {
return nullptr;
}
- virtual std::string getDeviceAlias(const InputDeviceIdentifier&) {
- return "";
- }
+ std::string getDeviceAlias(const InputDeviceIdentifier&) override { return ""; }
void waitForInputDevices(std::function<void(bool)> processDevicesChanged) {
std::unique_lock<std::mutex> lock(mLock);
@@ -368,9 +383,14 @@
uint32_t flags;
};
+ struct SensorInfo {
+ InputDeviceSensorType sensorType;
+ int32_t sensorDataIndex;
+ };
+
struct Device {
InputDeviceIdentifier identifier;
- uint32_t classes;
+ Flags<InputDeviceClass> classes;
PropertyMap configuration;
KeyedVector<int, RawAbsoluteAxisInfo> absoluteAxes;
KeyedVector<int, bool> relativeAxes;
@@ -381,6 +401,8 @@
KeyedVector<int32_t, KeyInfo> keysByScanCode;
KeyedVector<int32_t, KeyInfo> keysByUsageCode;
KeyedVector<int32_t, bool> leds;
+ std::unordered_map<int32_t, SensorInfo> sensorsByAbsCode;
+ BitArray<MSC_MAX> mscBitmask;
std::vector<VirtualKeyDefinition> virtualKeys;
bool enabled;
@@ -394,9 +416,7 @@
return OK;
}
- explicit Device(uint32_t classes) :
- classes(classes), enabled(true) {
- }
+ explicit Device(Flags<InputDeviceClass> classes) : classes(classes), enabled(true) {}
};
std::mutex mLock;
@@ -404,8 +424,15 @@
KeyedVector<int32_t, Device*> mDevices;
std::vector<std::string> mExcludedDevices;
- List<RawEvent> mEvents GUARDED_BY(mLock);
+ std::vector<RawEvent> mEvents GUARDED_BY(mLock);
std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> mVideoFrames;
+ std::vector<int32_t> mVibrators = {0, 1};
+ std::unordered_map<int32_t, RawLightInfo> mRawLightInfos;
+ // Simulates a device light brightness, from light id to light brightness.
+ std::unordered_map<int32_t /* lightId */, int32_t /* brightness*/> mLightBrightness;
+ // Simulates a device light intensities, from light id to light intensities map.
+ std::unordered_map<int32_t /* lightId */, std::unordered_map<LightColor, int32_t>>
+ mLightIntensities;
public:
virtual ~FakeEventHub() {
@@ -416,19 +443,19 @@
FakeEventHub() { }
- void addDevice(int32_t deviceId, const std::string& name, uint32_t classes) {
+ void addDevice(int32_t deviceId, const std::string& name, Flags<InputDeviceClass> classes) {
Device* device = new Device(classes);
device->identifier.name = name;
mDevices.add(deviceId, device);
- enqueueEvent(ARBITRARY_TIME, deviceId, EventHubInterface::DEVICE_ADDED, 0, 0);
+ enqueueEvent(ARBITRARY_TIME, READ_TIME, deviceId, EventHubInterface::DEVICE_ADDED, 0, 0);
}
void removeDevice(int32_t deviceId) {
delete mDevices.valueFor(deviceId);
mDevices.removeItem(deviceId);
- enqueueEvent(ARBITRARY_TIME, deviceId, EventHubInterface::DEVICE_REMOVED, 0, 0);
+ enqueueEvent(ARBITRARY_TIME, READ_TIME, deviceId, EventHubInterface::DEVICE_REMOVED, 0, 0);
}
bool isDeviceEnabled(int32_t deviceId) {
@@ -469,7 +496,7 @@
}
void finishDeviceScan() {
- enqueueEvent(ARBITRARY_TIME, 0, EventHubInterface::FINISHED_DEVICE_SCAN, 0, 0);
+ enqueueEvent(ARBITRARY_TIME, READ_TIME, 0, EventHubInterface::FINISHED_DEVICE_SCAN, 0, 0);
}
void addConfigurationProperty(int32_t deviceId, const String8& key, const String8& value) {
@@ -540,6 +567,35 @@
device->leds.add(led, initialState);
}
+ void addSensorAxis(int32_t deviceId, int32_t absCode, InputDeviceSensorType sensorType,
+ int32_t sensorDataIndex) {
+ Device* device = getDevice(deviceId);
+ SensorInfo info;
+ info.sensorType = sensorType;
+ info.sensorDataIndex = sensorDataIndex;
+ device->sensorsByAbsCode.emplace(absCode, info);
+ }
+
+ void setMscEvent(int32_t deviceId, int32_t mscEvent) {
+ Device* device = getDevice(deviceId);
+ typename BitArray<MSC_MAX>::Buffer buffer;
+ buffer[mscEvent / 32] = 1 << mscEvent % 32;
+ device->mscBitmask.loadFromBuffer(buffer);
+ }
+
+ void addRawLightInfo(int32_t rawId, RawLightInfo&& info) {
+ mRawLightInfos.emplace(rawId, std::move(info));
+ }
+
+ void fakeLightBrightness(int32_t rawId, int32_t brightness) {
+ mLightBrightness.emplace(rawId, brightness);
+ }
+
+ void fakeLightIntensities(int32_t rawId,
+ const std::unordered_map<LightColor, int32_t> intensities) {
+ mLightIntensities.emplace(rawId, std::move(intensities));
+ }
+
bool getLedState(int32_t deviceId, int32_t led) {
Device* device = getDevice(deviceId);
return device->leds.valueFor(led);
@@ -554,11 +610,12 @@
device->virtualKeys.push_back(definition);
}
- void enqueueEvent(nsecs_t when, int32_t deviceId, int32_t type,
- int32_t code, int32_t value) {
+ void enqueueEvent(nsecs_t when, nsecs_t readTime, int32_t deviceId, int32_t type, int32_t code,
+ int32_t value) {
std::scoped_lock<std::mutex> lock(mLock);
RawEvent event;
event.when = when;
+ event.readTime = readTime;
event.deviceId = deviceId;
event.type = type;
event.code = code;
@@ -592,29 +649,27 @@
return index >= 0 ? mDevices.valueAt(index) : nullptr;
}
- virtual uint32_t getDeviceClasses(int32_t deviceId) const {
+ Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const override {
Device* device = getDevice(deviceId);
- return device ? device->classes : 0;
+ return device ? device->classes : Flags<InputDeviceClass>(0);
}
- virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const {
+ InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override {
Device* device = getDevice(deviceId);
return device ? device->identifier : InputDeviceIdentifier();
}
- virtual int32_t getDeviceControllerNumber(int32_t) const {
- return 0;
- }
+ int32_t getDeviceControllerNumber(int32_t) const override { return 0; }
- virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const {
+ void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override {
Device* device = getDevice(deviceId);
if (device) {
*outConfiguration = device->configuration;
}
}
- virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
- RawAbsoluteAxisInfo* outAxisInfo) const {
+ status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
+ RawAbsoluteAxisInfo* outAxisInfo) const override {
Device* device = getDevice(deviceId);
if (device && device->enabled) {
ssize_t index = device->absoluteAxes.indexOfKey(axis);
@@ -627,7 +682,7 @@
return -1;
}
- virtual bool hasRelativeAxis(int32_t deviceId, int axis) const {
+ bool hasRelativeAxis(int32_t deviceId, int axis) const override {
Device* device = getDevice(deviceId);
if (device) {
return device->relativeAxes.indexOfKey(axis) >= 0;
@@ -635,13 +690,18 @@
return false;
}
- virtual bool hasInputProperty(int32_t, int) const {
+ bool hasInputProperty(int32_t, int) const override { return false; }
+
+ bool hasMscEvent(int32_t deviceId, int mscEvent) const override final {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ return mscEvent >= 0 && mscEvent <= MSC_MAX ? device->mscBitmask.test(mscEvent) : false;
+ }
return false;
}
- virtual status_t mapKey(int32_t deviceId,
- int32_t scanCode, int32_t usageCode, int32_t metaState,
- int32_t* outKeycode, int32_t *outMetaState, uint32_t* outFlags) const {
+ status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
+ int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const override {
Device* device = getDevice(deviceId);
if (device) {
const KeyInfo* key = getKey(device, scanCode, usageCode);
@@ -677,27 +737,38 @@
return nullptr;
}
- virtual status_t mapAxis(int32_t, int32_t, AxisInfo*) const {
- return NAME_NOT_FOUND;
+ status_t mapAxis(int32_t, int32_t, AxisInfo*) const override { return NAME_NOT_FOUND; }
+
+ base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(int32_t deviceId,
+ int32_t absCode) {
+ Device* device = getDevice(deviceId);
+ if (!device) {
+ return Errorf("Sensor device not found.");
+ }
+ auto it = device->sensorsByAbsCode.find(absCode);
+ if (it == device->sensorsByAbsCode.end()) {
+ return Errorf("Sensor map not found.");
+ }
+ const SensorInfo& info = it->second;
+ return std::make_pair(info.sensorType, info.sensorDataIndex);
}
- virtual void setExcludedDevices(const std::vector<std::string>& devices) {
+ void setExcludedDevices(const std::vector<std::string>& devices) override {
mExcludedDevices = devices;
}
- virtual size_t getEvents(int, RawEvent* buffer, size_t) {
- std::scoped_lock<std::mutex> lock(mLock);
- if (mEvents.empty()) {
- return 0;
- }
+ size_t getEvents(int, RawEvent* buffer, size_t bufferSize) override {
+ std::scoped_lock lock(mLock);
- *buffer = *mEvents.begin();
- mEvents.erase(mEvents.begin());
+ const size_t filledSize = std::min(mEvents.size(), bufferSize);
+ std::copy(mEvents.begin(), mEvents.begin() + filledSize, buffer);
+
+ mEvents.erase(mEvents.begin(), mEvents.begin() + filledSize);
mEventsCondition.notify_all();
- return 1;
+ return filledSize;
}
- virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) {
+ std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override {
auto it = mVideoFrames.find(deviceId);
if (it != mVideoFrames.end()) {
std::vector<TouchVideoFrame> frames = std::move(it->second);
@@ -707,7 +778,7 @@
return {};
}
- virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const {
+ int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override {
Device* device = getDevice(deviceId);
if (device) {
ssize_t index = device->scanCodeStates.indexOfKey(scanCode);
@@ -718,7 +789,7 @@
return AKEY_STATE_UNKNOWN;
}
- virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const {
+ int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override {
Device* device = getDevice(deviceId);
if (device) {
ssize_t index = device->keyCodeStates.indexOfKey(keyCode);
@@ -729,7 +800,7 @@
return AKEY_STATE_UNKNOWN;
}
- virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const {
+ int32_t getSwitchState(int32_t deviceId, int32_t sw) const override {
Device* device = getDevice(deviceId);
if (device) {
ssize_t index = device->switchStates.indexOfKey(sw);
@@ -740,8 +811,8 @@
return AKEY_STATE_UNKNOWN;
}
- virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
- int32_t* outValue) const {
+ status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
+ int32_t* outValue) const override {
Device* device = getDevice(deviceId);
if (device) {
ssize_t index = device->absoluteAxisValue.indexOfKey(axis);
@@ -754,22 +825,22 @@
return -1;
}
- virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
- uint8_t* outFlags) const {
+ // Return true if the device has non-empty key layout.
+ bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
+ uint8_t* outFlags) const override {
bool result = false;
Device* device = getDevice(deviceId);
if (device) {
+ result = device->keysByScanCode.size() > 0 || device->keysByUsageCode.size() > 0;
for (size_t i = 0; i < numCodes; i++) {
for (size_t j = 0; j < device->keysByScanCode.size(); j++) {
if (keyCodes[i] == device->keysByScanCode.valueAt(j).keyCode) {
outFlags[i] = 1;
- result = true;
}
}
for (size_t j = 0; j < device->keysByUsageCode.size(); j++) {
if (keyCodes[i] == device->keysByUsageCode.valueAt(j).keyCode) {
outFlags[i] = 1;
- result = true;
}
}
}
@@ -777,7 +848,7 @@
return result;
}
- virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const {
+ bool hasScanCode(int32_t deviceId, int32_t scanCode) const override {
Device* device = getDevice(deviceId);
if (device) {
ssize_t index = device->keysByScanCode.indexOfKey(scanCode);
@@ -786,12 +857,12 @@
return false;
}
- virtual bool hasLed(int32_t deviceId, int32_t led) const {
+ bool hasLed(int32_t deviceId, int32_t led) const override {
Device* device = getDevice(deviceId);
return device && device->leds.indexOfKey(led) >= 0;
}
- virtual void setLedState(int32_t deviceId, int32_t led, bool on) {
+ void setLedState(int32_t deviceId, int32_t led, bool on) override {
Device* device = getDevice(deviceId);
if (device) {
ssize_t index = device->leds.indexOfKey(led);
@@ -805,8 +876,8 @@
}
}
- virtual void getVirtualKeyDefinitions(int32_t deviceId,
- std::vector<VirtualKeyDefinition>& outVirtualKeys) const {
+ void getVirtualKeyDefinitions(
+ int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const override {
outVirtualKeys.clear();
Device* device = getDevice(deviceId);
@@ -815,146 +886,89 @@
}
}
- virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t) const {
+ const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap(int32_t) const override {
return nullptr;
}
- virtual bool setKeyboardLayoutOverlay(int32_t, const sp<KeyCharacterMap>&) {
+ bool setKeyboardLayoutOverlay(int32_t, std::shared_ptr<KeyCharacterMap>) override {
return false;
}
- virtual void vibrate(int32_t, nsecs_t) {
+ void vibrate(int32_t, const VibrationElement&) override {}
+
+ void cancelVibrate(int32_t) override {}
+
+ std::vector<int32_t> getVibratorIds(int32_t deviceId) override { return mVibrators; };
+
+ std::optional<int32_t> getBatteryCapacity(int32_t, int32_t) const override {
+ return BATTERY_CAPACITY;
}
- virtual void cancelVibrate(int32_t) {
+ std::optional<int32_t> getBatteryStatus(int32_t, int32_t) const override {
+ return BATTERY_STATUS;
}
+ const std::vector<int32_t> getRawBatteryIds(int32_t deviceId) { return {}; }
+
+ std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId, int32_t batteryId) {
+ return std::nullopt;
+ }
+
+ const std::vector<int32_t> getRawLightIds(int32_t deviceId) override {
+ std::vector<int32_t> ids;
+ for (const auto& [rawId, info] : mRawLightInfos) {
+ ids.push_back(rawId);
+ }
+ return ids;
+ }
+
+ std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId, int32_t lightId) override {
+ auto it = mRawLightInfos.find(lightId);
+ if (it == mRawLightInfos.end()) {
+ return std::nullopt;
+ }
+ return it->second;
+ }
+
+ void setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) override {
+ mLightBrightness.emplace(lightId, brightness);
+ }
+
+ void setLightIntensities(int32_t deviceId, int32_t lightId,
+ std::unordered_map<LightColor, int32_t> intensities) override {
+ mLightIntensities.emplace(lightId, intensities);
+ };
+
+ std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) override {
+ auto lightIt = mLightBrightness.find(lightId);
+ if (lightIt == mLightBrightness.end()) {
+ return std::nullopt;
+ }
+ return lightIt->second;
+ }
+
+ std::optional<std::unordered_map<LightColor, int32_t>> getLightIntensities(
+ int32_t deviceId, int32_t lightId) override {
+ auto lightIt = mLightIntensities.find(lightId);
+ if (lightIt == mLightIntensities.end()) {
+ return std::nullopt;
+ }
+ return lightIt->second;
+ };
+
virtual bool isExternal(int32_t) const {
return false;
}
- virtual void dump(std::string&) {
- }
+ void dump(std::string&) override {}
- virtual void monitor() {
- }
+ void monitor() override {}
- virtual void requestReopenDevices() {
- }
+ void requestReopenDevices() override {}
- virtual void wake() {
- }
+ void wake() override {}
};
-
-// --- FakeInputReaderContext ---
-
-class FakeInputReaderContext : public InputReaderContext {
- std::shared_ptr<EventHubInterface> mEventHub;
- sp<InputReaderPolicyInterface> mPolicy;
- sp<InputListenerInterface> mListener;
- int32_t mGlobalMetaState;
- bool mUpdateGlobalMetaStateWasCalled;
- int32_t mGeneration;
- int32_t mNextId;
- std::weak_ptr<PointerControllerInterface> mPointerController;
-
-public:
- FakeInputReaderContext(std::shared_ptr<EventHubInterface> eventHub,
- const sp<InputReaderPolicyInterface>& policy,
- const sp<InputListenerInterface>& listener)
- : mEventHub(eventHub),
- mPolicy(policy),
- mListener(listener),
- mGlobalMetaState(0),
- mNextId(1) {}
-
- virtual ~FakeInputReaderContext() { }
-
- void assertUpdateGlobalMetaStateWasCalled() {
- ASSERT_TRUE(mUpdateGlobalMetaStateWasCalled)
- << "Expected updateGlobalMetaState() to have been called.";
- mUpdateGlobalMetaStateWasCalled = false;
- }
-
- void setGlobalMetaState(int32_t state) {
- mGlobalMetaState = state;
- }
-
- uint32_t getGeneration() {
- return mGeneration;
- }
-
- void updatePointerDisplay() {
- std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock();
- if (controller != nullptr) {
- InputReaderConfiguration config;
- mPolicy->getReaderConfiguration(&config);
- auto viewport = config.getDisplayViewportById(config.defaultPointerDisplayId);
- if (viewport) {
- controller->setDisplayViewport(*viewport);
- }
- }
- }
-
-private:
- virtual void updateGlobalMetaState() {
- mUpdateGlobalMetaStateWasCalled = true;
- }
-
- virtual int32_t getGlobalMetaState() {
- return mGlobalMetaState;
- }
-
- virtual EventHubInterface* getEventHub() {
- return mEventHub.get();
- }
-
- virtual InputReaderPolicyInterface* getPolicy() {
- return mPolicy.get();
- }
-
- virtual InputListenerInterface* getListener() {
- return mListener.get();
- }
-
- virtual void disableVirtualKeysUntil(nsecs_t) {
- }
-
- virtual bool shouldDropVirtualKey(nsecs_t, int32_t, int32_t) { return false; }
-
- virtual std::shared_ptr<PointerControllerInterface> getPointerController(int32_t deviceId) {
- std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock();
- if (controller == nullptr) {
- controller = mPolicy->obtainPointerController(deviceId);
- mPointerController = controller;
- updatePointerDisplay();
- }
- return controller;
- }
-
- virtual void fadePointer() {
- }
-
- virtual void requestTimeoutAtTime(nsecs_t) {
- }
-
- virtual int32_t bumpGeneration() {
- return ++mGeneration;
- }
-
- virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) {
-
- }
-
- virtual void dispatchExternalStylusState(const StylusState&) {
-
- }
-
- virtual int32_t getNextId() { return mNextId++; }
-};
-
-
// --- FakeInputMapper ---
class FakeInputMapper : public InputMapper {
@@ -984,7 +998,7 @@
mResetWasCalled(false),
mProcessWasCalled(false) {}
- virtual ~FakeInputMapper() { }
+ virtual ~FakeInputMapper() {}
void setKeyboardType(int32_t keyboardType) {
mKeyboardType = keyboardType;
@@ -1053,11 +1067,9 @@
}
private:
- virtual uint32_t getSources() {
- return mSources;
- }
+ uint32_t getSources() override { return mSources; }
- virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) {
+ void populateDeviceInfo(InputDeviceInfo* deviceInfo) override {
InputMapper::populateDeviceInfo(deviceInfo);
if (mKeyboardType != AINPUT_KEYBOARD_TYPE_NONE) {
@@ -1065,7 +1077,7 @@
}
}
- virtual void configure(nsecs_t, const InputReaderConfiguration* config, uint32_t changes) {
+ void configure(nsecs_t, const InputReaderConfiguration* config, uint32_t changes) override {
std::scoped_lock<std::mutex> lock(mLock);
mConfigureWasCalled = true;
@@ -1078,45 +1090,45 @@
mStateChangedCondition.notify_all();
}
- virtual void reset(nsecs_t) {
+ void reset(nsecs_t) override {
std::scoped_lock<std::mutex> lock(mLock);
mResetWasCalled = true;
mStateChangedCondition.notify_all();
}
- virtual void process(const RawEvent* rawEvent) {
+ void process(const RawEvent* rawEvent) override {
std::scoped_lock<std::mutex> lock(mLock);
mLastEvent = *rawEvent;
mProcessWasCalled = true;
mStateChangedCondition.notify_all();
}
- virtual int32_t getKeyCodeState(uint32_t, int32_t keyCode) {
+ int32_t getKeyCodeState(uint32_t, int32_t keyCode) override {
ssize_t index = mKeyCodeStates.indexOfKey(keyCode);
return index >= 0 ? mKeyCodeStates.valueAt(index) : AKEY_STATE_UNKNOWN;
}
- virtual int32_t getScanCodeState(uint32_t, int32_t scanCode) {
+ int32_t getScanCodeState(uint32_t, int32_t scanCode) override {
ssize_t index = mScanCodeStates.indexOfKey(scanCode);
return index >= 0 ? mScanCodeStates.valueAt(index) : AKEY_STATE_UNKNOWN;
}
- virtual int32_t getSwitchState(uint32_t, int32_t switchCode) {
+ int32_t getSwitchState(uint32_t, int32_t switchCode) override {
ssize_t index = mSwitchStates.indexOfKey(switchCode);
return index >= 0 ? mSwitchStates.valueAt(index) : AKEY_STATE_UNKNOWN;
}
- virtual bool markSupportedKeyCodes(uint32_t, size_t numCodes,
- const int32_t* keyCodes, uint8_t* outFlags) {
- bool result = false;
+ // Return true if the device has non-empty key layout.
+ bool markSupportedKeyCodes(uint32_t, size_t numCodes, const int32_t* keyCodes,
+ uint8_t* outFlags) override {
for (size_t i = 0; i < numCodes; i++) {
for (size_t j = 0; j < mSupportedKeyCodes.size(); j++) {
if (keyCodes[i] == mSupportedKeyCodes[j]) {
outFlags[i] = 1;
- result = true;
}
}
}
+ bool result = mSupportedKeyCodes.size() > 0;
return result;
}
@@ -1139,17 +1151,17 @@
// --- InstrumentedInputReader ---
class InstrumentedInputReader : public InputReader {
- std::shared_ptr<InputDevice> mNextDevice;
+ std::queue<std::shared_ptr<InputDevice>> mNextDevices;
public:
InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub,
const sp<InputReaderPolicyInterface>& policy,
const sp<InputListenerInterface>& listener)
- : InputReader(eventHub, policy, listener), mNextDevice(nullptr) {}
+ : InputReader(eventHub, policy, listener), mFakeContext(this) {}
virtual ~InstrumentedInputReader() {}
- void setNextDevice(std::shared_ptr<InputDevice> device) { mNextDevice = device; }
+ void pushNextDevice(std::shared_ptr<InputDevice> device) { mNextDevices.push(device); }
std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
const std::string& location = "") {
@@ -1157,24 +1169,68 @@
identifier.name = name;
identifier.location = location;
int32_t generation = deviceId + 1;
- return std::make_shared<InputDevice>(&mContext, deviceId, generation, identifier);
+ return std::make_shared<InputDevice>(&mFakeContext, deviceId, generation, identifier);
}
// Make the protected loopOnce method accessible to tests.
using InputReader::loopOnce;
protected:
- virtual std::shared_ptr<InputDevice> createDeviceLocked(
- int32_t eventHubId, const InputDeviceIdentifier& identifier) {
- if (mNextDevice) {
- std::shared_ptr<InputDevice> device(mNextDevice);
- mNextDevice = nullptr;
+ virtual std::shared_ptr<InputDevice> createDeviceLocked(int32_t eventHubId,
+ const InputDeviceIdentifier& identifier)
+ REQUIRES(mLock) {
+ if (!mNextDevices.empty()) {
+ std::shared_ptr<InputDevice> device(std::move(mNextDevices.front()));
+ mNextDevices.pop();
return device;
}
return InputReader::createDeviceLocked(eventHubId, identifier);
}
+ // --- FakeInputReaderContext ---
+ class FakeInputReaderContext : public ContextImpl {
+ int32_t mGlobalMetaState;
+ bool mUpdateGlobalMetaStateWasCalled;
+ int32_t mGeneration;
+
+ public:
+ FakeInputReaderContext(InputReader* reader)
+ : ContextImpl(reader),
+ mGlobalMetaState(0),
+ mUpdateGlobalMetaStateWasCalled(false),
+ mGeneration(1) {}
+
+ virtual ~FakeInputReaderContext() {}
+
+ void assertUpdateGlobalMetaStateWasCalled() {
+ ASSERT_TRUE(mUpdateGlobalMetaStateWasCalled)
+ << "Expected updateGlobalMetaState() to have been called.";
+ mUpdateGlobalMetaStateWasCalled = false;
+ }
+
+ void setGlobalMetaState(int32_t state) { mGlobalMetaState = state; }
+
+ uint32_t getGeneration() { return mGeneration; }
+
+ void updateGlobalMetaState() override {
+ mUpdateGlobalMetaStateWasCalled = true;
+ ContextImpl::updateGlobalMetaState();
+ }
+
+ int32_t getGlobalMetaState() override {
+ return mGlobalMetaState | ContextImpl::getGlobalMetaState();
+ }
+
+ int32_t bumpGeneration() override {
+ mGeneration = ContextImpl::bumpGeneration();
+ return mGeneration;
+ }
+ } mFakeContext;
+
friend class InputReaderTest;
+
+public:
+ FakeInputReaderContext* getContext() { return &mFakeContext; }
};
// --- InputReaderPolicyTest ---
@@ -1182,8 +1238,8 @@
protected:
sp<FakeInputReaderPolicy> mFakePolicy;
- virtual void SetUp() override { mFakePolicy = new FakeInputReaderPolicy(); }
- virtual void TearDown() override { mFakePolicy.clear(); }
+ void SetUp() override { mFakePolicy = new FakeInputReaderPolicy(); }
+ void TearDown() override { mFakePolicy.clear(); }
};
/**
@@ -1198,20 +1254,21 @@
// We didn't add any viewports yet, so there shouldn't be any.
std::optional<DisplayViewport> internalViewport =
- mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
ASSERT_FALSE(internalViewport);
// Add an internal viewport, then clear it
mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, uniqueId, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+ DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId, NO_PORT,
+ ViewportType::INTERNAL);
// Check matching by uniqueId
internalViewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId);
ASSERT_TRUE(internalViewport);
- ASSERT_EQ(ViewportType::VIEWPORT_INTERNAL, internalViewport->type);
+ ASSERT_EQ(ViewportType::INTERNAL, internalViewport->type);
// Check matching by viewport type
- internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
ASSERT_TRUE(internalViewport);
ASSERT_EQ(uniqueId, internalViewport->uniqueId);
@@ -1219,7 +1276,7 @@
// Make sure nothing is found after clear
internalViewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId);
ASSERT_FALSE(internalViewport);
- internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
ASSERT_FALSE(internalViewport);
}
@@ -1233,26 +1290,30 @@
// Add an internal viewport
mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, internalUniqueId, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+ DISPLAY_ORIENTATION_0, true /*isActive*/, internalUniqueId,
+ NO_PORT, ViewportType::INTERNAL);
// Add an external viewport
mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, externalUniqueId, NO_PORT, ViewportType::VIEWPORT_EXTERNAL);
+ DISPLAY_ORIENTATION_0, true /*isActive*/, externalUniqueId,
+ NO_PORT, ViewportType::EXTERNAL);
// Add an virtual viewport
mFakePolicy->addDisplayViewport(virtualDisplayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, virtualUniqueId1, NO_PORT, ViewportType::VIEWPORT_VIRTUAL);
+ DISPLAY_ORIENTATION_0, true /*isActive*/, virtualUniqueId1,
+ NO_PORT, ViewportType::VIRTUAL);
// Add another virtual viewport
mFakePolicy->addDisplayViewport(virtualDisplayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, virtualUniqueId2, NO_PORT, ViewportType::VIEWPORT_VIRTUAL);
+ DISPLAY_ORIENTATION_0, true /*isActive*/, virtualUniqueId2,
+ NO_PORT, ViewportType::VIRTUAL);
// Check matching by type for internal
std::optional<DisplayViewport> internalViewport =
- mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
ASSERT_TRUE(internalViewport);
ASSERT_EQ(internalUniqueId, internalViewport->uniqueId);
// Check matching by type for external
std::optional<DisplayViewport> externalViewport =
- mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_EXTERNAL);
+ mFakePolicy->getDisplayViewportByType(ViewportType::EXTERNAL);
ASSERT_TRUE(externalViewport);
ASSERT_EQ(externalUniqueId, externalViewport->uniqueId);
@@ -1260,7 +1321,7 @@
std::optional<DisplayViewport> virtualViewport1 =
mFakePolicy->getDisplayViewportByUniqueId(virtualUniqueId1);
ASSERT_TRUE(virtualViewport1);
- ASSERT_EQ(ViewportType::VIEWPORT_VIRTUAL, virtualViewport1->type);
+ ASSERT_EQ(ViewportType::VIRTUAL, virtualViewport1->type);
ASSERT_EQ(virtualUniqueId1, virtualViewport1->uniqueId);
ASSERT_EQ(virtualDisplayId1, virtualViewport1->displayId);
@@ -1268,7 +1329,7 @@
std::optional<DisplayViewport> virtualViewport2 =
mFakePolicy->getDisplayViewportByUniqueId(virtualUniqueId2);
ASSERT_TRUE(virtualViewport2);
- ASSERT_EQ(ViewportType::VIEWPORT_VIRTUAL, virtualViewport2->type);
+ ASSERT_EQ(ViewportType::VIRTUAL, virtualViewport2->type);
ASSERT_EQ(virtualUniqueId2, virtualViewport2->uniqueId);
ASSERT_EQ(virtualDisplayId2, virtualViewport2->displayId);
}
@@ -1285,16 +1346,18 @@
constexpr int32_t displayId1 = 2;
constexpr int32_t displayId2 = 3;
- std::vector<ViewportType> types = {ViewportType::VIEWPORT_INTERNAL,
- ViewportType::VIEWPORT_EXTERNAL, ViewportType::VIEWPORT_VIRTUAL};
+ std::vector<ViewportType> types = {ViewportType::INTERNAL, ViewportType::EXTERNAL,
+ ViewportType::VIRTUAL};
for (const ViewportType& type : types) {
mFakePolicy->clearViewports();
// Add a viewport
mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, uniqueId1, NO_PORT, type);
+ DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId1,
+ NO_PORT, type);
// Add another viewport
mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, uniqueId2, NO_PORT, type);
+ DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId2,
+ NO_PORT, type);
// Check that correct display viewport was returned by comparing the display IDs.
std::optional<DisplayViewport> viewport1 =
@@ -1321,10 +1384,51 @@
}
/**
+ * When we have multiple internal displays make sure we always return the default display when
+ * querying by type.
+ */
+TEST_F(InputReaderPolicyTest, Viewports_ByTypeReturnsDefaultForInternal) {
+ const std::string uniqueId1 = "uniqueId1";
+ const std::string uniqueId2 = "uniqueId2";
+ constexpr int32_t nonDefaultDisplayId = 2;
+ static_assert(nonDefaultDisplayId != ADISPLAY_ID_DEFAULT,
+ "Test display ID should not be ADISPLAY_ID_DEFAULT");
+
+ // Add the default display first and ensure it gets returned.
+ mFakePolicy->clearViewports();
+ mFakePolicy->addDisplayViewport(ADISPLAY_ID_DEFAULT, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId1, NO_PORT,
+ ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(nonDefaultDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId2, NO_PORT,
+ ViewportType::INTERNAL);
+
+ std::optional<DisplayViewport> viewport =
+ mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
+ ASSERT_TRUE(viewport);
+ ASSERT_EQ(ADISPLAY_ID_DEFAULT, viewport->displayId);
+ ASSERT_EQ(ViewportType::INTERNAL, viewport->type);
+
+ // Add the default display second to make sure order doesn't matter.
+ mFakePolicy->clearViewports();
+ mFakePolicy->addDisplayViewport(nonDefaultDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId2, NO_PORT,
+ ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(ADISPLAY_ID_DEFAULT, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId1, NO_PORT,
+ ViewportType::INTERNAL);
+
+ viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
+ ASSERT_TRUE(viewport);
+ ASSERT_EQ(ADISPLAY_ID_DEFAULT, viewport->displayId);
+ ASSERT_EQ(ViewportType::INTERNAL, viewport->type);
+}
+
+/**
* Check getDisplayViewportByPort
*/
TEST_F(InputReaderPolicyTest, Viewports_GetByPort) {
- constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL;
+ constexpr ViewportType type = ViewportType::EXTERNAL;
const std::string uniqueId1 = "uniqueId1";
const std::string uniqueId2 = "uniqueId2";
constexpr int32_t displayId1 = 1;
@@ -1336,10 +1440,12 @@
mFakePolicy->clearViewports();
// Add a viewport that's associated with some display port that's not of interest.
mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, uniqueId1, hdmi3, type);
+ DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId1, hdmi3,
+ type);
// Add another viewport, connected to HDMI1 port
mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, uniqueId2, hdmi1, type);
+ DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId2, hdmi1,
+ type);
// Check that correct display viewport was returned by comparing the display ports.
std::optional<DisplayViewport> hdmi1Viewport = mFakePolicy->getDisplayViewportByPort(hdmi1);
@@ -1368,7 +1474,7 @@
std::shared_ptr<FakeEventHub> mFakeEventHub;
std::unique_ptr<InstrumentedInputReader> mReader;
- virtual void SetUp() override {
+ void SetUp() override {
mFakeEventHub = std::make_unique<FakeEventHub>();
mFakePolicy = new FakeInputReaderPolicy();
mFakeListener = new TestInputListener();
@@ -1377,12 +1483,12 @@
mFakeListener);
}
- virtual void TearDown() override {
+ void TearDown() override {
mFakeListener.clear();
mFakePolicy.clear();
}
- void addDevice(int32_t eventHubId, const std::string& name, uint32_t classes,
+ void addDevice(int32_t eventHubId, const std::string& name, Flags<InputDeviceClass> classes,
const PropertyMap* configuration) {
mFakeEventHub->addDevice(eventHubId, name, classes);
@@ -1407,34 +1513,23 @@
}
FakeInputMapper& addDeviceWithFakeInputMapper(int32_t deviceId, int32_t eventHubId,
- const std::string& name, uint32_t classes,
- uint32_t sources,
+ const std::string& name,
+ Flags<InputDeviceClass> classes, uint32_t sources,
const PropertyMap* configuration) {
std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, name);
FakeInputMapper& mapper = device->addMapper<FakeInputMapper>(eventHubId, sources);
- mReader->setNextDevice(device);
+ mReader->pushNextDevice(device);
addDevice(eventHubId, name, classes, configuration);
return mapper;
}
};
-TEST_F(InputReaderTest, GetInputDevices) {
- ASSERT_NO_FATAL_FAILURE(addDevice(1, "keyboard",
- INPUT_DEVICE_CLASS_KEYBOARD, nullptr));
- ASSERT_NO_FATAL_FAILURE(addDevice(2, "ignored",
- 0, nullptr)); // no classes so device will be ignored
+TEST_F(InputReaderTest, ReaderGetInputDevices) {
+ ASSERT_NO_FATAL_FAILURE(addDevice(1, "keyboard", InputDeviceClass::KEYBOARD, nullptr));
+ ASSERT_NO_FATAL_FAILURE(addDevice(2, "ignored", Flags<InputDeviceClass>(0),
+ nullptr)); // no classes so device will be ignored
- std::vector<InputDeviceInfo> inputDevices;
- mReader->getInputDevices(inputDevices);
- ASSERT_EQ(1U, inputDevices.size());
- ASSERT_EQ(END_RESERVED_ID + 1, inputDevices[0].getId());
- ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.c_str());
- ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, inputDevices[0].getKeyboardType());
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, inputDevices[0].getSources());
- ASSERT_EQ(size_t(0), inputDevices[0].getMotionRanges().size());
-
- // Should also have received a notification describing the new input devices.
- inputDevices = mFakePolicy->getInputDevices();
+ const std::vector<InputDeviceInfo> inputDevices = mReader->getInputDevices();
ASSERT_EQ(1U, inputDevices.size());
ASSERT_EQ(END_RESERVED_ID + 1, inputDevices[0].getId());
ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.c_str());
@@ -1443,14 +1538,76 @@
ASSERT_EQ(size_t(0), inputDevices[0].getMotionRanges().size());
}
+TEST_F(InputReaderTest, PolicyGetInputDevices) {
+ ASSERT_NO_FATAL_FAILURE(addDevice(1, "keyboard", InputDeviceClass::KEYBOARD, nullptr));
+ ASSERT_NO_FATAL_FAILURE(addDevice(2, "ignored", Flags<InputDeviceClass>(0),
+ nullptr)); // no classes so device will be ignored
+
+ // Should also have received a notification describing the new input devices.
+ const std::vector<InputDeviceInfo>& inputDevices = mFakePolicy->getInputDevices();
+ ASSERT_EQ(1U, inputDevices.size());
+ ASSERT_EQ(END_RESERVED_ID + 1, inputDevices[0].getId());
+ ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.c_str());
+ ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, inputDevices[0].getKeyboardType());
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, inputDevices[0].getSources());
+ ASSERT_EQ(size_t(0), inputDevices[0].getMotionRanges().size());
+}
+
+TEST_F(InputReaderTest, GetMergedInputDevices) {
+ constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+ constexpr int32_t eventHubIds[2] = {END_RESERVED_ID, END_RESERVED_ID + 1};
+ // Add two subdevices to device
+ std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
+ // Must add at least one mapper or the device will be ignored!
+ device->addMapper<FakeInputMapper>(eventHubIds[0], AINPUT_SOURCE_KEYBOARD);
+ device->addMapper<FakeInputMapper>(eventHubIds[1], AINPUT_SOURCE_KEYBOARD);
+
+ // Push same device instance for next device to be added, so they'll have same identifier.
+ mReader->pushNextDevice(device);
+ mReader->pushNextDevice(device);
+ ASSERT_NO_FATAL_FAILURE(
+ addDevice(eventHubIds[0], "fake1", InputDeviceClass::KEYBOARD, nullptr));
+ ASSERT_NO_FATAL_FAILURE(
+ addDevice(eventHubIds[1], "fake2", InputDeviceClass::KEYBOARD, nullptr));
+
+ // Two devices will be merged to one input device as they have same identifier
+ ASSERT_EQ(1U, mReader->getInputDevices().size());
+}
+
+TEST_F(InputReaderTest, GetMergedInputDevicesEnabled) {
+ constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+ constexpr int32_t eventHubIds[2] = {END_RESERVED_ID, END_RESERVED_ID + 1};
+ // Add two subdevices to device
+ std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
+ // Must add at least one mapper or the device will be ignored!
+ device->addMapper<FakeInputMapper>(eventHubIds[0], AINPUT_SOURCE_KEYBOARD);
+ device->addMapper<FakeInputMapper>(eventHubIds[1], AINPUT_SOURCE_KEYBOARD);
+
+ // Push same device instance for next device to be added, so they'll have same identifier.
+ mReader->pushNextDevice(device);
+ mReader->pushNextDevice(device);
+ // Sensor device is initially disabled
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "fake1",
+ InputDeviceClass::KEYBOARD | InputDeviceClass::SENSOR,
+ nullptr));
+ // Device is disabled because the only sub device is a sensor device and disabled initially.
+ ASSERT_FALSE(mFakeEventHub->isDeviceEnabled(eventHubIds[0]));
+ ASSERT_FALSE(device->isEnabled());
+ ASSERT_NO_FATAL_FAILURE(
+ addDevice(eventHubIds[1], "fake2", InputDeviceClass::KEYBOARD, nullptr));
+ // The merged device is enabled if any sub device is enabled
+ ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(eventHubIds[1]));
+ ASSERT_TRUE(device->isEnabled());
+}
+
TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) {
constexpr int32_t deviceId = END_RESERVED_ID + 1000;
- constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+ constexpr Flags<InputDeviceClass> deviceClass(InputDeviceClass::KEYBOARD);
constexpr int32_t eventHubId = 1;
std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
// Must add at least one mapper or the device will be ignored!
device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
- mReader->setNextDevice(device);
+ mReader->pushNextDevice(device);
ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(nullptr));
@@ -1482,7 +1639,7 @@
TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) {
constexpr int32_t deviceId = END_RESERVED_ID + 1000;
- constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+ constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
constexpr int32_t eventHubId = 1;
FakeInputMapper& mapper =
addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass,
@@ -1515,7 +1672,7 @@
TEST_F(InputReaderTest, GetScanCodeState_ForwardsRequestsToMappers) {
constexpr int32_t deviceId = END_RESERVED_ID + 1000;
- constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+ constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
constexpr int32_t eventHubId = 1;
FakeInputMapper& mapper =
addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass,
@@ -1548,7 +1705,7 @@
TEST_F(InputReaderTest, GetSwitchState_ForwardsRequestsToMappers) {
constexpr int32_t deviceId = END_RESERVED_ID + 1000;
- constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+ constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
constexpr int32_t eventHubId = 1;
FakeInputMapper& mapper =
addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass,
@@ -1581,7 +1738,7 @@
TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) {
constexpr int32_t deviceId = END_RESERVED_ID + 1000;
- constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+ constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
constexpr int32_t eventHubId = 1;
FakeInputMapper& mapper =
addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass,
@@ -1623,7 +1780,7 @@
TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChanged) {
constexpr int32_t eventHubId = 1;
- addDevice(eventHubId, "ignored", INPUT_DEVICE_CLASS_KEYBOARD, nullptr);
+ addDevice(eventHubId, "ignored", InputDeviceClass::KEYBOARD, nullptr);
NotifyConfigurationChangedArgs args;
@@ -1633,19 +1790,22 @@
TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) {
constexpr int32_t deviceId = END_RESERVED_ID + 1000;
- constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+ constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
+ constexpr nsecs_t when = 0;
constexpr int32_t eventHubId = 1;
+ constexpr nsecs_t readTime = 2;
FakeInputMapper& mapper =
addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass,
AINPUT_SOURCE_KEYBOARD, nullptr);
- mFakeEventHub->enqueueEvent(0, eventHubId, EV_KEY, KEY_A, 1);
+ mFakeEventHub->enqueueEvent(when, readTime, eventHubId, EV_KEY, KEY_A, 1);
mReader->loopOnce();
ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty());
RawEvent event;
ASSERT_NO_FATAL_FAILURE(mapper.assertProcessWasCalled(&event));
- ASSERT_EQ(0, event.when);
+ ASSERT_EQ(when, event.when);
+ ASSERT_EQ(readTime, event.readTime);
ASSERT_EQ(eventHubId, event.deviceId);
ASSERT_EQ(EV_KEY, event.type);
ASSERT_EQ(KEY_A, event.code);
@@ -1654,12 +1814,12 @@
TEST_F(InputReaderTest, DeviceReset_RandomId) {
constexpr int32_t deviceId = END_RESERVED_ID + 1000;
- constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+ constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
constexpr int32_t eventHubId = 1;
std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
// Must add at least one mapper or the device will be ignored!
device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
- mReader->setNextDevice(device);
+ mReader->pushNextDevice(device);
ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
NotifyDeviceResetArgs resetArgs;
@@ -1687,12 +1847,12 @@
TEST_F(InputReaderTest, DeviceReset_GenerateIdWithInputReaderSource) {
constexpr int32_t deviceId = 1;
- constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+ constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
constexpr int32_t eventHubId = 1;
std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
// Must add at least one mapper or the device will be ignored!
device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
- mReader->setNextDevice(device);
+ mReader->pushNextDevice(device);
ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr));
NotifyDeviceResetArgs resetArgs;
@@ -1702,13 +1862,13 @@
TEST_F(InputReaderTest, Device_CanDispatchToDisplay) {
constexpr int32_t deviceId = END_RESERVED_ID + 1000;
- constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+ constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
constexpr int32_t eventHubId = 1;
const char* DEVICE_LOCATION = "USB1";
std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
FakeInputMapper& mapper =
device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_TOUCHSCREEN);
- mReader->setNextDevice(device);
+ mReader->pushNextDevice(device);
const uint8_t hdmi1 = 1;
@@ -1718,9 +1878,11 @@
// Add default and second display.
mFakePolicy->clearViewports();
mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, "local:0", NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+ DISPLAY_ORIENTATION_0, true /*isActive*/, "local:0", NO_PORT,
+ ViewportType::INTERNAL);
mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, "local:1", hdmi1, ViewportType::VIEWPORT_EXTERNAL);
+ DISPLAY_ORIENTATION_0, true /*isActive*/, "local:1", hdmi1,
+ ViewportType::EXTERNAL);
mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
mReader->loopOnce();
@@ -1743,6 +1905,221 @@
ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID));
}
+TEST_F(InputReaderTest, WhenEnabledChanges_AllSubdevicesAreUpdated) {
+ constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+ constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
+ constexpr int32_t eventHubIds[2] = {END_RESERVED_ID, END_RESERVED_ID + 1};
+ std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
+ // Must add at least one mapper or the device will be ignored!
+ device->addMapper<FakeInputMapper>(eventHubIds[0], AINPUT_SOURCE_KEYBOARD);
+ device->addMapper<FakeInputMapper>(eventHubIds[1], AINPUT_SOURCE_KEYBOARD);
+ mReader->pushNextDevice(device);
+ mReader->pushNextDevice(device);
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "fake1", deviceClass, nullptr));
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[1], "fake2", deviceClass, nullptr));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(nullptr));
+
+ NotifyDeviceResetArgs resetArgs;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+ ASSERT_EQ(deviceId, resetArgs.deviceId);
+ ASSERT_TRUE(device->isEnabled());
+ ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(eventHubIds[0]));
+ ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(eventHubIds[1]));
+
+ disableDevice(deviceId);
+ mReader->loopOnce();
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+ ASSERT_EQ(deviceId, resetArgs.deviceId);
+ ASSERT_FALSE(device->isEnabled());
+ ASSERT_FALSE(mFakeEventHub->isDeviceEnabled(eventHubIds[0]));
+ ASSERT_FALSE(mFakeEventHub->isDeviceEnabled(eventHubIds[1]));
+
+ enableDevice(deviceId);
+ mReader->loopOnce();
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+ ASSERT_EQ(deviceId, resetArgs.deviceId);
+ ASSERT_TRUE(device->isEnabled());
+ ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(eventHubIds[0]));
+ ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(eventHubIds[1]));
+}
+
+TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToSubdeviceMappers) {
+ constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+ constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
+ constexpr int32_t eventHubIds[2] = {END_RESERVED_ID, END_RESERVED_ID + 1};
+ // Add two subdevices to device
+ std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
+ FakeInputMapper& mapperDevice1 =
+ device->addMapper<FakeInputMapper>(eventHubIds[0], AINPUT_SOURCE_KEYBOARD);
+ FakeInputMapper& mapperDevice2 =
+ device->addMapper<FakeInputMapper>(eventHubIds[1], AINPUT_SOURCE_KEYBOARD);
+ mReader->pushNextDevice(device);
+ mReader->pushNextDevice(device);
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "fake1", deviceClass, nullptr));
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[1], "fake2", deviceClass, nullptr));
+
+ mapperDevice1.setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN);
+ mapperDevice2.setKeyCodeState(AKEYCODE_B, AKEY_STATE_DOWN);
+
+ ASSERT_EQ(AKEY_STATE_DOWN,
+ mReader->getKeyCodeState(deviceId, AINPUT_SOURCE_KEYBOARD, AKEYCODE_A));
+ ASSERT_EQ(AKEY_STATE_DOWN,
+ mReader->getKeyCodeState(deviceId, AINPUT_SOURCE_KEYBOARD, AKEYCODE_B));
+ ASSERT_EQ(AKEY_STATE_UNKNOWN,
+ mReader->getKeyCodeState(deviceId, AINPUT_SOURCE_KEYBOARD, AKEYCODE_C));
+}
+
+TEST_F(InputReaderTest, ChangingPointerCaptureNotifiesInputListener) {
+ NotifyPointerCaptureChangedArgs args;
+
+ mFakePolicy->setPointerCapture(true);
+ mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+ mReader->loopOnce();
+ mFakeListener->assertNotifyCaptureWasCalled(&args);
+ ASSERT_TRUE(args.enabled) << "Pointer Capture should be enabled.";
+
+ mFakePolicy->setPointerCapture(false);
+ mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+ mReader->loopOnce();
+ mFakeListener->assertNotifyCaptureWasCalled(&args);
+ ASSERT_FALSE(args.enabled) << "Pointer Capture should be disabled.";
+
+ // Verify that the Pointer Capture state is re-configured correctly when the configuration value
+ // does not change.
+ mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+ mReader->loopOnce();
+ mFakeListener->assertNotifyCaptureWasCalled(&args);
+ ASSERT_FALSE(args.enabled) << "Pointer Capture should be disabled.";
+}
+
+class FakeVibratorInputMapper : public FakeInputMapper {
+public:
+ FakeVibratorInputMapper(InputDeviceContext& deviceContext, uint32_t sources)
+ : FakeInputMapper(deviceContext, sources) {}
+
+ std::vector<int32_t> getVibratorIds() override { return getDeviceContext().getVibratorIds(); }
+};
+
+TEST_F(InputReaderTest, VibratorGetVibratorIds) {
+ constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+ Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD | InputDeviceClass::VIBRATOR;
+ constexpr int32_t eventHubId = 1;
+ const char* DEVICE_LOCATION = "BLUETOOTH";
+ std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
+ FakeVibratorInputMapper& mapper =
+ device->addMapper<FakeVibratorInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
+ mReader->pushNextDevice(device);
+
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
+ ASSERT_NO_FATAL_FAILURE(mapper.assertConfigureWasCalled());
+
+ ASSERT_EQ(mapper.getVibratorIds().size(), 2U);
+ ASSERT_EQ(mReader->getVibratorIds(deviceId).size(), 2U);
+}
+
+// --- FakePeripheralController ---
+
+class FakePeripheralController : public PeripheralControllerInterface {
+public:
+ FakePeripheralController(InputDeviceContext& deviceContext) : mDeviceContext(deviceContext) {}
+
+ ~FakePeripheralController() override {}
+
+ void populateDeviceInfo(InputDeviceInfo* deviceInfo) override {}
+
+ void dump(std::string& dump) override {}
+
+ std::optional<int32_t> getBatteryCapacity(int32_t batteryId) override {
+ return getDeviceContext().getBatteryCapacity(batteryId);
+ }
+
+ std::optional<int32_t> getBatteryStatus(int32_t batteryId) override {
+ return getDeviceContext().getBatteryStatus(batteryId);
+ }
+
+ bool setLightColor(int32_t lightId, int32_t color) override {
+ getDeviceContext().setLightBrightness(lightId, color >> 24);
+ return true;
+ }
+
+ std::optional<int32_t> getLightColor(int32_t lightId) override {
+ std::optional<int32_t> result = getDeviceContext().getLightBrightness(lightId);
+ if (!result.has_value()) {
+ return std::nullopt;
+ }
+ return result.value() << 24;
+ }
+
+ bool setLightPlayerId(int32_t lightId, int32_t playerId) override { return true; }
+
+ std::optional<int32_t> getLightPlayerId(int32_t lightId) override { return std::nullopt; }
+
+private:
+ InputDeviceContext& mDeviceContext;
+ inline int32_t getDeviceId() { return mDeviceContext.getId(); }
+ inline InputDeviceContext& getDeviceContext() { return mDeviceContext; }
+};
+
+TEST_F(InputReaderTest, BatteryGetCapacity) {
+ constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+ Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD | InputDeviceClass::BATTERY;
+ constexpr int32_t eventHubId = 1;
+ const char* DEVICE_LOCATION = "BLUETOOTH";
+ std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
+ FakePeripheralController& controller =
+ device->addController<FakePeripheralController>(eventHubId);
+ mReader->pushNextDevice(device);
+
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
+
+ ASSERT_EQ(controller.getBatteryCapacity(DEFAULT_BATTERY), BATTERY_CAPACITY);
+ ASSERT_EQ(mReader->getBatteryCapacity(deviceId), BATTERY_CAPACITY);
+}
+
+TEST_F(InputReaderTest, BatteryGetStatus) {
+ constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+ Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD | InputDeviceClass::BATTERY;
+ constexpr int32_t eventHubId = 1;
+ const char* DEVICE_LOCATION = "BLUETOOTH";
+ std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
+ FakePeripheralController& controller =
+ device->addController<FakePeripheralController>(eventHubId);
+ mReader->pushNextDevice(device);
+
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
+
+ ASSERT_EQ(controller.getBatteryStatus(DEFAULT_BATTERY), BATTERY_STATUS);
+ ASSERT_EQ(mReader->getBatteryStatus(deviceId), BATTERY_STATUS);
+}
+
+TEST_F(InputReaderTest, LightGetColor) {
+ constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+ Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD | InputDeviceClass::LIGHT;
+ constexpr int32_t eventHubId = 1;
+ const char* DEVICE_LOCATION = "BLUETOOTH";
+ std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
+ FakePeripheralController& controller =
+ device->addController<FakePeripheralController>(eventHubId);
+ mReader->pushNextDevice(device);
+ RawLightInfo info = {.id = 1,
+ .name = "Mono",
+ .maxBrightness = 255,
+ .flags = InputLightClass::BRIGHTNESS,
+ .path = ""};
+ mFakeEventHub->addRawLightInfo(1 /* rawId */, std::move(info));
+ mFakeEventHub->fakeLightBrightness(1 /* rawId */, 0x55);
+
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
+
+ ASSERT_TRUE(controller.setLightColor(1 /* lightId */, LIGHT_BRIGHTNESS));
+ ASSERT_EQ(controller.getLightColor(1 /* lightId */), LIGHT_BRIGHTNESS);
+ ASSERT_TRUE(mReader->setLightColor(deviceId, 1 /* lightId */, LIGHT_BRIGHTNESS));
+ ASSERT_EQ(mReader->getLightColor(deviceId, 1 /* lightId */), LIGHT_BRIGHTNESS);
+}
+
// --- InputReaderIntegrationTest ---
// These tests create and interact with the InputReader only through its interface.
@@ -1756,7 +2133,7 @@
sp<FakeInputReaderPolicy> mFakePolicy;
sp<InputReaderInterface> mReader;
- virtual void SetUp() override {
+ void SetUp() override {
mFakePolicy = new FakeInputReaderPolicy();
mTestListener = new TestInputListener(2000ms /*eventHappenedTimeout*/,
30ms /*eventDidNotHappenTimeout*/);
@@ -1771,7 +2148,7 @@
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
}
- virtual void TearDown() override {
+ void TearDown() override {
ASSERT_EQ(mReader->stop(), OK);
mTestListener.clear();
mFakePolicy.clear();
@@ -1812,20 +2189,17 @@
ASSERT_EQ(initialNumDevices + 1, mFakePolicy->getInputDevices().size());
// Find the test device by its name.
- std::vector<InputDeviceInfo> inputDevices;
- mReader->getInputDevices(inputDevices);
- InputDeviceInfo* keyboardInfo = nullptr;
- const char* keyboardName = keyboard->getName();
- for (unsigned int i = 0; i < initialNumDevices + 1; i++) {
- if (!strcmp(inputDevices[i].getIdentifier().name.c_str(), keyboardName)) {
- keyboardInfo = &inputDevices[i];
- break;
- }
- }
- ASSERT_NE(keyboardInfo, nullptr);
- ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, keyboardInfo->getKeyboardType());
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, keyboardInfo->getSources());
- ASSERT_EQ(0U, keyboardInfo->getMotionRanges().size());
+ const std::vector<InputDeviceInfo> inputDevices = mReader->getInputDevices();
+ const auto& it =
+ std::find_if(inputDevices.begin(), inputDevices.end(),
+ [&keyboard](const InputDeviceInfo& info) {
+ return info.getIdentifier().name == keyboard->getName();
+ });
+
+ ASSERT_NE(it, inputDevices.end());
+ ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, it->getKeyboardType());
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, it->getSources());
+ ASSERT_EQ(0U, it->getMotionRanges().size());
keyboard.reset();
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
@@ -1850,12 +2224,14 @@
ASSERT_NE(prevId, keyArgs.id);
prevId = keyArgs.id;
ASSERT_LE(prevTimestamp, keyArgs.eventTime);
+ ASSERT_LE(keyArgs.eventTime, keyArgs.readTime);
prevTimestamp = keyArgs.eventTime;
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
ASSERT_NE(prevId, keyArgs.id);
ASSERT_LE(prevTimestamp, keyArgs.eventTime);
+ ASSERT_LE(keyArgs.eventTime, keyArgs.readTime);
}
/**
@@ -1885,12 +2261,12 @@
protected:
const std::string UNIQUE_ID = "local:0";
- virtual void SetUp() override {
+ void SetUp() override {
InputReaderIntegrationTest::SetUp();
// At least add an internal display.
setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
DISPLAY_ORIENTATION_0, UNIQUE_ID, NO_PORT,
- ViewportType::VIEWPORT_INTERNAL);
+ ViewportType::INTERNAL);
mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT));
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
@@ -1901,8 +2277,8 @@
int32_t orientation, const std::string& uniqueId,
std::optional<uint8_t> physicalPort,
ViewportType viewportType) {
- mFakePolicy->addDisplayViewport(displayId, width, height, orientation, uniqueId,
- physicalPort, viewportType);
+ mFakePolicy->addDisplayViewport(displayId, width, height, orientation, true /*isActive*/,
+ uniqueId, physicalPort, viewportType);
mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
}
@@ -2020,33 +2396,32 @@
static const int32_t DEVICE_ID;
static const int32_t DEVICE_GENERATION;
static const int32_t DEVICE_CONTROLLER_NUMBER;
- static const uint32_t DEVICE_CLASSES;
+ static const Flags<InputDeviceClass> DEVICE_CLASSES;
static const int32_t EVENTHUB_ID;
std::shared_ptr<FakeEventHub> mFakeEventHub;
sp<FakeInputReaderPolicy> mFakePolicy;
sp<TestInputListener> mFakeListener;
- FakeInputReaderContext* mFakeContext;
-
+ std::unique_ptr<InstrumentedInputReader> mReader;
std::shared_ptr<InputDevice> mDevice;
- virtual void SetUp() override {
+ void SetUp() override {
mFakeEventHub = std::make_unique<FakeEventHub>();
mFakePolicy = new FakeInputReaderPolicy();
mFakeListener = new TestInputListener();
- mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
-
- mFakeEventHub->addDevice(EVENTHUB_ID, DEVICE_NAME, 0);
+ mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
+ mFakeListener);
InputDeviceIdentifier identifier;
identifier.name = DEVICE_NAME;
identifier.location = DEVICE_LOCATION;
- mDevice = std::make_shared<InputDevice>(mFakeContext, DEVICE_ID, DEVICE_GENERATION,
+ mDevice = std::make_shared<InputDevice>(mReader->getContext(), DEVICE_ID, DEVICE_GENERATION,
identifier);
+ mReader->pushNextDevice(mDevice);
+ mFakeEventHub->addDevice(EVENTHUB_ID, DEVICE_NAME, Flags<InputDeviceClass>(0));
+ mReader->loopOnce();
}
- virtual void TearDown() override {
- mDevice = nullptr;
- delete mFakeContext;
+ void TearDown() override {
mFakeListener.clear();
mFakePolicy.clear();
}
@@ -2057,14 +2432,14 @@
const int32_t InputDeviceTest::DEVICE_ID = END_RESERVED_ID + 1000;
const int32_t InputDeviceTest::DEVICE_GENERATION = 2;
const int32_t InputDeviceTest::DEVICE_CONTROLLER_NUMBER = 0;
-const uint32_t InputDeviceTest::DEVICE_CLASSES = INPUT_DEVICE_CLASS_KEYBOARD
- | INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_JOYSTICK;
+const Flags<InputDeviceClass> InputDeviceTest::DEVICE_CLASSES =
+ InputDeviceClass::KEYBOARD | InputDeviceClass::TOUCH | InputDeviceClass::JOYSTICK;
const int32_t InputDeviceTest::EVENTHUB_ID = 1;
TEST_F(InputDeviceTest, ImmutableProperties) {
ASSERT_EQ(DEVICE_ID, mDevice->getId());
ASSERT_STREQ(DEVICE_NAME, mDevice->getName().c_str());
- ASSERT_EQ(0U, mDevice->getClasses());
+ ASSERT_EQ(Flags<InputDeviceClass>(0), mDevice->getClasses());
}
TEST_F(InputDeviceTest, WhenDeviceCreated_EnabledIsFalse) {
@@ -2233,8 +2608,43 @@
// Prepare displays.
mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, UNIQUE_ID, hdmi,
- ViewportType::VIEWPORT_INTERNAL);
+ DISPLAY_ORIENTATION_0, true /*isActive*/, UNIQUE_ID, hdmi,
+ ViewportType::INTERNAL);
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ ASSERT_TRUE(mDevice->isEnabled());
+
+ // Device should be disabled after set disable.
+ mFakePolicy->addDisabledDevice(mDevice->getId());
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::CHANGE_ENABLED_STATE);
+ ASSERT_FALSE(mDevice->isEnabled());
+
+ // Device should still be disabled even found the associated display.
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ ASSERT_FALSE(mDevice->isEnabled());
+}
+
+TEST_F(InputDeviceTest, Configure_AssignsDisplayUniqueId) {
+ // Device should be enabled by default.
+ mFakePolicy->clearViewports();
+ mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD);
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
+ ASSERT_TRUE(mDevice->isEnabled());
+
+ // Device should be disabled because it is associated with a specific display, but the
+ // corresponding display is not found.
+ const std::string DISPLAY_UNIQUE_ID = "displayUniqueId";
+ mFakePolicy->addInputUniqueIdAssociation(DEVICE_NAME, DISPLAY_UNIQUE_ID);
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ ASSERT_FALSE(mDevice->isEnabled());
+
+ // Device should be enabled when a display is found.
+ mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
+ NO_PORT, ViewportType::INTERNAL);
mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
ASSERT_TRUE(mDevice->isEnabled());
@@ -2260,33 +2670,27 @@
static const int32_t DEVICE_ID;
static const int32_t DEVICE_GENERATION;
static const int32_t DEVICE_CONTROLLER_NUMBER;
- static const uint32_t DEVICE_CLASSES;
+ static const Flags<InputDeviceClass> DEVICE_CLASSES;
static const int32_t EVENTHUB_ID;
std::shared_ptr<FakeEventHub> mFakeEventHub;
sp<FakeInputReaderPolicy> mFakePolicy;
sp<TestInputListener> mFakeListener;
- FakeInputReaderContext* mFakeContext;
- InputDevice* mDevice;
+ std::unique_ptr<InstrumentedInputReader> mReader;
+ std::shared_ptr<InputDevice> mDevice;
- virtual void SetUp(uint32_t classes) {
+ virtual void SetUp(Flags<InputDeviceClass> classes) {
mFakeEventHub = std::make_unique<FakeEventHub>();
mFakePolicy = new FakeInputReaderPolicy();
mFakeListener = new TestInputListener();
- mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
- InputDeviceIdentifier identifier;
- identifier.name = DEVICE_NAME;
- identifier.location = DEVICE_LOCATION;
- mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION, identifier);
-
- mFakeEventHub->addDevice(EVENTHUB_ID, DEVICE_NAME, classes);
+ mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
+ mFakeListener);
+ mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes);
}
- virtual void SetUp() override { SetUp(DEVICE_CLASSES); }
+ void SetUp() override { SetUp(DEVICE_CLASSES); }
- virtual void TearDown() override {
- delete mDevice;
- delete mFakeContext;
+ void TearDown() override {
mFakeListener.clear();
mFakePolicy.clear();
}
@@ -2297,24 +2701,41 @@
void configureDevice(uint32_t changes) {
if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
- mFakeContext->updatePointerDisplay();
+ mReader->requestRefreshConfiguration(changes);
+ mReader->loopOnce();
}
mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
}
+ std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
+ const std::string& location, int32_t eventHubId,
+ Flags<InputDeviceClass> classes) {
+ InputDeviceIdentifier identifier;
+ identifier.name = name;
+ identifier.location = location;
+ std::shared_ptr<InputDevice> device =
+ std::make_shared<InputDevice>(mReader->getContext(), deviceId, DEVICE_GENERATION,
+ identifier);
+ mReader->pushNextDevice(device);
+ mFakeEventHub->addDevice(eventHubId, name, classes);
+ mReader->loopOnce();
+ return device;
+ }
+
template <class T, typename... Args>
T& addMapperAndConfigure(Args... args) {
T& mapper = mDevice->addMapper<T>(EVENTHUB_ID, args...);
configureDevice(0);
mDevice->reset(ARBITRARY_TIME);
+ mapper.reset(ARBITRARY_TIME);
return mapper;
}
void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
int32_t orientation, const std::string& uniqueId,
std::optional<uint8_t> physicalPort, ViewportType viewportType) {
- mFakePolicy->addDisplayViewport(
- displayId, width, height, orientation, uniqueId, physicalPort, viewportType);
+ mFakePolicy->addDisplayViewport(displayId, width, height, orientation, true /*isActive*/,
+ uniqueId, physicalPort, viewportType);
configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
}
@@ -2322,15 +2743,17 @@
mFakePolicy->clearViewports();
}
- static void process(InputMapper& mapper, nsecs_t when, int32_t type, int32_t code,
- int32_t value) {
+ void process(InputMapper& mapper, nsecs_t when, nsecs_t readTime, int32_t type, int32_t code,
+ int32_t value) {
RawEvent event;
event.when = when;
+ event.readTime = readTime;
event.deviceId = mapper.getDeviceContext().getEventHubId();
event.type = type;
event.code = code;
event.value = value;
mapper.process(&event);
+ mReader->loopOnce();
}
static void assertMotionRange(const InputDeviceInfo& info,
@@ -2374,7 +2797,8 @@
const int32_t InputMapperTest::DEVICE_ID = END_RESERVED_ID + 1000;
const int32_t InputMapperTest::DEVICE_GENERATION = 2;
const int32_t InputMapperTest::DEVICE_CONTROLLER_NUMBER = 0;
-const uint32_t InputMapperTest::DEVICE_CLASSES = 0; // not needed for current tests
+const Flags<InputDeviceClass> InputMapperTest::DEVICE_CLASSES =
+ Flags<InputDeviceClass>(0); // not needed for current tests
const int32_t InputMapperTest::EVENTHUB_ID = 1;
// --- SwitchInputMapperTest ---
@@ -2402,10 +2826,10 @@
TEST_F(SwitchInputMapperTest, Process) {
SwitchInputMapper& mapper = addMapperAndConfigure<SwitchInputMapper>();
- process(mapper, ARBITRARY_TIME, EV_SW, SW_LID, 1);
- process(mapper, ARBITRARY_TIME, EV_SW, SW_JACK_PHYSICAL_INSERT, 1);
- process(mapper, ARBITRARY_TIME, EV_SW, SW_HEADPHONE_INSERT, 0);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_LID, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_JACK_PHYSICAL_INSERT, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_HEADPHONE_INSERT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
NotifySwitchArgs args;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifySwitchWasCalled(&args));
@@ -2416,6 +2840,215 @@
ASSERT_EQ(uint32_t(0), args.policyFlags);
}
+// --- VibratorInputMapperTest ---
+class VibratorInputMapperTest : public InputMapperTest {
+protected:
+ void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::VIBRATOR); }
+};
+
+TEST_F(VibratorInputMapperTest, GetSources) {
+ VibratorInputMapper& mapper = addMapperAndConfigure<VibratorInputMapper>();
+
+ ASSERT_EQ(AINPUT_SOURCE_UNKNOWN, mapper.getSources());
+}
+
+TEST_F(VibratorInputMapperTest, GetVibratorIds) {
+ VibratorInputMapper& mapper = addMapperAndConfigure<VibratorInputMapper>();
+
+ ASSERT_EQ(mapper.getVibratorIds().size(), 2U);
+}
+
+TEST_F(VibratorInputMapperTest, Vibrate) {
+ constexpr uint8_t DEFAULT_AMPLITUDE = 192;
+ constexpr int32_t VIBRATION_TOKEN = 100;
+ VibratorInputMapper& mapper = addMapperAndConfigure<VibratorInputMapper>();
+
+ VibrationElement pattern(2);
+ VibrationSequence sequence(2);
+ pattern.duration = std::chrono::milliseconds(200);
+ pattern.channels = {{0 /* vibratorId */, DEFAULT_AMPLITUDE / 2},
+ {1 /* vibratorId */, DEFAULT_AMPLITUDE}};
+ sequence.addElement(pattern);
+ pattern.duration = std::chrono::milliseconds(500);
+ pattern.channels = {{0 /* vibratorId */, DEFAULT_AMPLITUDE / 4},
+ {1 /* vibratorId */, DEFAULT_AMPLITUDE}};
+ sequence.addElement(pattern);
+
+ std::vector<int64_t> timings = {0, 1};
+ std::vector<uint8_t> amplitudes = {DEFAULT_AMPLITUDE, DEFAULT_AMPLITUDE / 2};
+
+ ASSERT_FALSE(mapper.isVibrating());
+ // Start vibrating
+ mapper.vibrate(sequence, -1 /* repeat */, VIBRATION_TOKEN);
+ ASSERT_TRUE(mapper.isVibrating());
+ // Verify vibrator state listener was notified.
+ mReader->loopOnce();
+ NotifyVibratorStateArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyVibratorStateWasCalled(&args));
+ ASSERT_EQ(DEVICE_ID, args.deviceId);
+ ASSERT_TRUE(args.isOn);
+ // Stop vibrating
+ mapper.cancelVibrate(VIBRATION_TOKEN);
+ ASSERT_FALSE(mapper.isVibrating());
+ // Verify vibrator state listener was notified.
+ mReader->loopOnce();
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyVibratorStateWasCalled(&args));
+ ASSERT_EQ(DEVICE_ID, args.deviceId);
+ ASSERT_FALSE(args.isOn);
+}
+
+// --- SensorInputMapperTest ---
+
+class SensorInputMapperTest : public InputMapperTest {
+protected:
+ static const int32_t ACCEL_RAW_MIN;
+ static const int32_t ACCEL_RAW_MAX;
+ static const int32_t ACCEL_RAW_FUZZ;
+ static const int32_t ACCEL_RAW_FLAT;
+ static const int32_t ACCEL_RAW_RESOLUTION;
+
+ static const int32_t GYRO_RAW_MIN;
+ static const int32_t GYRO_RAW_MAX;
+ static const int32_t GYRO_RAW_FUZZ;
+ static const int32_t GYRO_RAW_FLAT;
+ static const int32_t GYRO_RAW_RESOLUTION;
+
+ static const float GRAVITY_MS2_UNIT;
+ static const float DEGREE_RADIAN_UNIT;
+
+ void prepareAccelAxes();
+ void prepareGyroAxes();
+ void setAccelProperties();
+ void setGyroProperties();
+ void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::SENSOR); }
+};
+
+const int32_t SensorInputMapperTest::ACCEL_RAW_MIN = -32768;
+const int32_t SensorInputMapperTest::ACCEL_RAW_MAX = 32768;
+const int32_t SensorInputMapperTest::ACCEL_RAW_FUZZ = 16;
+const int32_t SensorInputMapperTest::ACCEL_RAW_FLAT = 0;
+const int32_t SensorInputMapperTest::ACCEL_RAW_RESOLUTION = 8192;
+
+const int32_t SensorInputMapperTest::GYRO_RAW_MIN = -2097152;
+const int32_t SensorInputMapperTest::GYRO_RAW_MAX = 2097152;
+const int32_t SensorInputMapperTest::GYRO_RAW_FUZZ = 16;
+const int32_t SensorInputMapperTest::GYRO_RAW_FLAT = 0;
+const int32_t SensorInputMapperTest::GYRO_RAW_RESOLUTION = 1024;
+
+const float SensorInputMapperTest::GRAVITY_MS2_UNIT = 9.80665f;
+const float SensorInputMapperTest::DEGREE_RADIAN_UNIT = 0.0174533f;
+
+void SensorInputMapperTest::prepareAccelAxes() {
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_X, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_FUZZ,
+ ACCEL_RAW_FLAT, ACCEL_RAW_RESOLUTION);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_Y, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_FUZZ,
+ ACCEL_RAW_FLAT, ACCEL_RAW_RESOLUTION);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_Z, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_FUZZ,
+ ACCEL_RAW_FLAT, ACCEL_RAW_RESOLUTION);
+}
+
+void SensorInputMapperTest::prepareGyroAxes() {
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_RX, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_FUZZ,
+ GYRO_RAW_FLAT, GYRO_RAW_RESOLUTION);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_RY, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_FUZZ,
+ GYRO_RAW_FLAT, GYRO_RAW_RESOLUTION);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_RZ, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_FUZZ,
+ GYRO_RAW_FLAT, GYRO_RAW_RESOLUTION);
+}
+
+void SensorInputMapperTest::setAccelProperties() {
+ mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 0, InputDeviceSensorType::ACCELEROMETER,
+ /* sensorDataIndex */ 0);
+ mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 1, InputDeviceSensorType::ACCELEROMETER,
+ /* sensorDataIndex */ 1);
+ mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 2, InputDeviceSensorType::ACCELEROMETER,
+ /* sensorDataIndex */ 2);
+ mFakeEventHub->setMscEvent(EVENTHUB_ID, MSC_TIMESTAMP);
+ addConfigurationProperty("sensor.accelerometer.reportingMode", "0");
+ addConfigurationProperty("sensor.accelerometer.maxDelay", "100000");
+ addConfigurationProperty("sensor.accelerometer.minDelay", "5000");
+ addConfigurationProperty("sensor.accelerometer.power", "1.5");
+}
+
+void SensorInputMapperTest::setGyroProperties() {
+ mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 3, InputDeviceSensorType::GYROSCOPE,
+ /* sensorDataIndex */ 0);
+ mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 4, InputDeviceSensorType::GYROSCOPE,
+ /* sensorDataIndex */ 1);
+ mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 5, InputDeviceSensorType::GYROSCOPE,
+ /* sensorDataIndex */ 2);
+ mFakeEventHub->setMscEvent(EVENTHUB_ID, MSC_TIMESTAMP);
+ addConfigurationProperty("sensor.gyroscope.reportingMode", "0");
+ addConfigurationProperty("sensor.gyroscope.maxDelay", "100000");
+ addConfigurationProperty("sensor.gyroscope.minDelay", "5000");
+ addConfigurationProperty("sensor.gyroscope.power", "0.8");
+}
+
+TEST_F(SensorInputMapperTest, GetSources) {
+ SensorInputMapper& mapper = addMapperAndConfigure<SensorInputMapper>();
+
+ ASSERT_EQ(static_cast<uint32_t>(AINPUT_SOURCE_SENSOR), mapper.getSources());
+}
+
+TEST_F(SensorInputMapperTest, ProcessAccelerometerSensor) {
+ setAccelProperties();
+ prepareAccelAxes();
+ SensorInputMapper& mapper = addMapperAndConfigure<SensorInputMapper>();
+
+ ASSERT_TRUE(mapper.enableSensor(InputDeviceSensorType::ACCELEROMETER,
+ std::chrono::microseconds(10000),
+ std::chrono::microseconds(0)));
+ ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(EVENTHUB_ID));
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_X, 20000);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_Y, -20000);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_Z, 40000);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_TIMESTAMP, 1000);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+
+ NotifySensorArgs args;
+ std::vector<float> values = {20000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT,
+ -20000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT,
+ 40000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT};
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifySensorWasCalled(&args));
+ ASSERT_EQ(args.source, AINPUT_SOURCE_SENSOR);
+ ASSERT_EQ(args.deviceId, DEVICE_ID);
+ ASSERT_EQ(args.sensorType, InputDeviceSensorType::ACCELEROMETER);
+ ASSERT_EQ(args.accuracy, InputDeviceSensorAccuracy::ACCURACY_HIGH);
+ ASSERT_EQ(args.hwTimestamp, ARBITRARY_TIME);
+ ASSERT_EQ(args.values, values);
+ mapper.flushSensor(InputDeviceSensorType::ACCELEROMETER);
+}
+
+TEST_F(SensorInputMapperTest, ProcessGyroscopeSensor) {
+ setGyroProperties();
+ prepareGyroAxes();
+ SensorInputMapper& mapper = addMapperAndConfigure<SensorInputMapper>();
+
+ ASSERT_TRUE(mapper.enableSensor(InputDeviceSensorType::GYROSCOPE,
+ std::chrono::microseconds(10000),
+ std::chrono::microseconds(0)));
+ ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(EVENTHUB_ID));
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_RX, 20000);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_RY, -20000);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_RZ, 40000);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_TIMESTAMP, 1000);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+
+ NotifySensorArgs args;
+ std::vector<float> values = {20000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT,
+ -20000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT,
+ 40000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT};
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifySensorWasCalled(&args));
+ ASSERT_EQ(args.source, AINPUT_SOURCE_SENSOR);
+ ASSERT_EQ(args.deviceId, DEVICE_ID);
+ ASSERT_EQ(args.sensorType, InputDeviceSensorType::GYROSCOPE);
+ ASSERT_EQ(args.accuracy, InputDeviceSensorAccuracy::ACCURACY_HIGH);
+ ASSERT_EQ(args.hwTimestamp, ARBITRARY_TIME);
+ ASSERT_EQ(args.values, values);
+ mapper.flushSensor(InputDeviceSensorType::GYROSCOPE);
+}
// --- KeyboardInputMapperTest ---
@@ -2434,8 +3067,8 @@
* orientation.
*/
void KeyboardInputMapperTest::prepareDisplay(int32_t orientation) {
- setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- orientation, UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+ setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, UNIQUE_ID,
+ NO_PORT, ViewportType::INTERNAL);
}
void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper& mapper,
@@ -2443,14 +3076,14 @@
int32_t rotatedKeyCode, int32_t displayId) {
NotifyKeyArgs args;
- process(mapper, ARBITRARY_TIME, EV_KEY, originalScanCode, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, originalScanCode, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
ASSERT_EQ(originalScanCode, args.scanCode);
ASSERT_EQ(rotatedKeyCode, args.keyCode);
ASSERT_EQ(displayId, args.displayId);
- process(mapper, ARBITRARY_TIME, EV_KEY, originalScanCode, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, originalScanCode, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
ASSERT_EQ(originalScanCode, args.scanCode);
@@ -2471,13 +3104,19 @@
const int32_t USAGE_UNKNOWN = 0x07ffff;
mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
mFakeEventHub->addKey(EVENTHUB_ID, 0, USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE);
+ mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_NUMLOCK, AKEYCODE_NUM_LOCK, POLICY_FLAG_WAKE);
+ mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK, POLICY_FLAG_WAKE);
+ mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK, POLICY_FLAG_WAKE);
KeyboardInputMapper& mapper =
addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ // Initial metastate to AMETA_NONE.
+ ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState());
+ mapper.updateMetaState(AKEYCODE_NUM_LOCK);
// Key down by scan code.
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1);
NotifyKeyArgs args;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(DEVICE_ID, args.deviceId);
@@ -2492,7 +3131,7 @@
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
// Key up by scan code.
- process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_HOME, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -2506,8 +3145,8 @@
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
// Key down by usage code.
- process(mapper, ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_A);
- process(mapper, ARBITRARY_TIME, EV_KEY, 0, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_SCAN, USAGE_A);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, 0, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -2521,8 +3160,8 @@
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
// Key up by usage code.
- process(mapper, ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_A);
- process(mapper, ARBITRARY_TIME + 1, EV_KEY, 0, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_SCAN, USAGE_A);
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, 0, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -2536,8 +3175,8 @@
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
// Key down with unknown scan code or usage code.
- process(mapper, ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UNKNOWN, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UNKNOWN, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -2551,8 +3190,8 @@
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
// Key up with unknown scan code or usage code.
- process(mapper, ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
- process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_UNKNOWN, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_UNKNOWN, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -2566,43 +3205,69 @@
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
}
+/**
+ * Ensure that the readTime is set to the time when the EV_KEY is received.
+ */
+TEST_F(KeyboardInputMapperTest, Process_SendsReadTime) {
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
+
+ KeyboardInputMapper& mapper =
+ addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+ AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ NotifyKeyArgs args;
+
+ // Key down
+ process(mapper, ARBITRARY_TIME, 12 /*readTime*/, EV_KEY, KEY_HOME, 1);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(12, args.readTime);
+
+ // Key up
+ process(mapper, ARBITRARY_TIME, 15 /*readTime*/, EV_KEY, KEY_HOME, 1);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(15, args.readTime);
+}
+
TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) {
mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFTSHIFT, 0, AKEYCODE_SHIFT_LEFT, 0);
mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_NUMLOCK, AKEYCODE_NUM_LOCK, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK, 0);
KeyboardInputMapper& mapper =
addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
AINPUT_KEYBOARD_TYPE_ALPHABETIC);
- // Initial metastate.
- ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
+ // Initial metastate to AMETA_NONE.
+ ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState());
+ mapper.updateMetaState(AKEYCODE_NUM_LOCK);
// Metakey down.
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_LEFTSHIFT, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_LEFTSHIFT, 1);
NotifyKeyArgs args;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper.getMetaState());
- ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled());
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertUpdateGlobalMetaStateWasCalled());
// Key down.
- process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_A, 1);
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_A, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper.getMetaState());
// Key up.
- process(mapper, ARBITRARY_TIME + 2, EV_KEY, KEY_A, 0);
+ process(mapper, ARBITRARY_TIME + 2, READ_TIME, EV_KEY, KEY_A, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper.getMetaState());
// Metakey up.
- process(mapper, ARBITRARY_TIME + 3, EV_KEY, KEY_LEFTSHIFT, 0);
+ process(mapper, ARBITRARY_TIME + 3, READ_TIME, EV_KEY, KEY_LEFTSHIFT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AMETA_NONE, args.metaState);
ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
- ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled());
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertUpdateGlobalMetaStateWasCalled());
}
TEST_F(KeyboardInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateDPad) {
@@ -2685,7 +3350,7 @@
NotifyKeyArgs args;
clearViewports();
prepareDisplay(DISPLAY_ORIENTATION_270);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
ASSERT_EQ(KEY_UP, args.scanCode);
@@ -2693,7 +3358,7 @@
clearViewports();
prepareDisplay(DISPLAY_ORIENTATION_180);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
ASSERT_EQ(KEY_UP, args.scanCode);
@@ -2711,16 +3376,16 @@
NotifyKeyArgs args;
// Display id should be ADISPLAY_ID_NONE without any display configuration.
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(ADISPLAY_ID_NONE, args.displayId);
prepareDisplay(DISPLAY_ORIENTATION_0);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(ADISPLAY_ID_NONE, args.displayId);
}
@@ -2740,20 +3405,20 @@
// ^--- already checked by the previous test
setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
- UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+ UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(DISPLAY_ID, args.displayId);
constexpr int32_t newDisplayId = 2;
clearViewports();
setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
- UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+ UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(newDisplayId, args.displayId);
}
@@ -2807,6 +3472,9 @@
KeyboardInputMapper& mapper =
addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ // Initialize metastate to AMETA_NUM_LOCK_ON.
+ ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState());
+ mapper.updateMetaState(AKEYCODE_NUM_LOCK);
// Initialization should have turned all of the lights off.
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
@@ -2814,54 +3482,91 @@
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
// Toggle caps lock on.
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState());
// Toggle num lock on.
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper.getMetaState());
// Toggle caps lock off.
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState());
// Toggle scroll lock on.
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
ASSERT_EQ(AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
// Toggle num lock off.
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
// Toggle scroll lock off.
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
}
+TEST_F(KeyboardInputMapperTest, NoMetaStateWhenMetaKeysNotPresent) {
+ mFakeEventHub->addKey(EVENTHUB_ID, BTN_A, 0, AKEYCODE_BUTTON_A, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, BTN_B, 0, AKEYCODE_BUTTON_B, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, BTN_X, 0, AKEYCODE_BUTTON_X, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, BTN_Y, 0, AKEYCODE_BUTTON_Y, 0);
+
+ KeyboardInputMapper& mapper =
+ addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+ AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC);
+
+ // Initial metastate should be AMETA_NONE as no meta keys added.
+ ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
+ // Meta state should be AMETA_NONE after reset
+ mapper.reset(ARBITRARY_TIME);
+ ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
+ // Meta state should be AMETA_NONE with update, as device doesn't have the keys.
+ mapper.updateMetaState(AKEYCODE_NUM_LOCK);
+ ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
+
+ NotifyKeyArgs args;
+ // Press button "A"
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_A, 1);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(AMETA_NONE, args.metaState);
+ ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
+ ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
+ ASSERT_EQ(AKEYCODE_BUTTON_A, args.keyCode);
+
+ // Button up.
+ process(mapper, ARBITRARY_TIME + 2, READ_TIME, EV_KEY, BTN_A, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(AMETA_NONE, args.metaState);
+ ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
+ ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
+ ASSERT_EQ(AKEYCODE_BUTTON_A, args.keyCode);
+}
+
TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) {
// keyboard 1.
mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
@@ -2871,15 +3576,13 @@
// keyboard 2.
const std::string USB2 = "USB2";
+ const std::string DEVICE_NAME2 = "KEYBOARD2";
constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1;
- InputDeviceIdentifier identifier;
- identifier.name = "KEYBOARD2";
- identifier.location = USB2;
- std::unique_ptr<InputDevice> device2 =
- std::make_unique<InputDevice>(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION,
- identifier);
- mFakeEventHub->addDevice(SECOND_EVENTHUB_ID, DEVICE_NAME, 0 /*classes*/);
+ std::shared_ptr<InputDevice> device2 =
+ newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID,
+ Flags<InputDeviceClass>(0));
+
mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
@@ -2911,9 +3614,9 @@
// Prepare second display.
constexpr int32_t newDisplayId = 2;
setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
- UNIQUE_ID, hdmi1, ViewportType::VIEWPORT_INTERNAL);
+ UNIQUE_ID, hdmi1, ViewportType::INTERNAL);
setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
- SECONDARY_UNIQUE_ID, hdmi2, ViewportType::VIEWPORT_EXTERNAL);
+ SECONDARY_UNIQUE_ID, hdmi2, ViewportType::EXTERNAL);
// Default device will reconfigure above, need additional reconfiguration for another device.
device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
@@ -2942,13 +3645,80 @@
AKEYCODE_DPAD_LEFT, newDisplayId));
}
+TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleAfterReattach) {
+ mFakeEventHub->addLed(EVENTHUB_ID, LED_CAPSL, true /*initially on*/);
+ mFakeEventHub->addLed(EVENTHUB_ID, LED_NUML, false /*initially off*/);
+ mFakeEventHub->addLed(EVENTHUB_ID, LED_SCROLLL, false /*initially off*/);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
+
+ KeyboardInputMapper& mapper =
+ addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+ AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ // Initial metastate to AMETA_NONE.
+ ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState());
+ mapper.updateMetaState(AKEYCODE_NUM_LOCK);
+
+ // Initialization should have turned all of the lights off.
+ ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
+ ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
+ ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
+
+ // Toggle caps lock on.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+ ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
+ ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState());
+
+ // Toggle num lock on.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
+ ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
+ ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper.getMetaState());
+
+ // Toggle scroll lock on.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+ ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
+ ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
+
+ mFakeEventHub->removeDevice(EVENTHUB_ID);
+ mReader->loopOnce();
+
+ // keyboard 2 should default toggle keys.
+ const std::string USB2 = "USB2";
+ const std::string DEVICE_NAME2 = "KEYBOARD2";
+ constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
+ constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1;
+ std::shared_ptr<InputDevice> device2 =
+ newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID,
+ Flags<InputDeviceClass>(0));
+ mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_CAPSL, true /*initially on*/);
+ mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_NUML, false /*initially off*/);
+ mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_SCROLLL, false /*initially off*/);
+ mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
+ mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
+ mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
+
+ KeyboardInputMapper& mapper2 =
+ device2->addMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD,
+ AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/);
+ device2->reset(ARBITRARY_TIME);
+
+ ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_CAPSL));
+ ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_NUML));
+ ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_SCROLLL));
+ ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON,
+ mapper2.getMetaState());
+}
+
// --- KeyboardInputMapperTest_ExternalDevice ---
class KeyboardInputMapperTest_ExternalDevice : public InputMapperTest {
protected:
- virtual void SetUp() override {
- InputMapperTest::SetUp(DEVICE_CLASSES | INPUT_DEVICE_CLASS_EXTERNAL);
- }
+ void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL); }
};
TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior) {
@@ -2964,28 +3734,28 @@
addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
AINPUT_KEYBOARD_TYPE_ALPHABETIC);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1);
NotifyKeyArgs args;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
- process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_HOME, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(uint32_t(0), args.policyFlags);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAY, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(uint32_t(0), args.policyFlags);
- process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0);
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAY, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(uint32_t(0), args.policyFlags);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
- process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0);
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
}
@@ -3002,28 +3772,28 @@
addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
AINPUT_KEYBOARD_TYPE_ALPHABETIC);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1);
NotifyKeyArgs args;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
- process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_HOME, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_DOWN, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_DOWN, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(uint32_t(0), args.policyFlags);
- process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_DOWN, 0);
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_DOWN, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(uint32_t(0), args.policyFlags);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAY, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
- process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0);
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAY, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
}
@@ -3036,7 +3806,7 @@
std::shared_ptr<FakePointerController> mFakePointerController;
- virtual void SetUp() override {
+ void SetUp() override {
InputMapperTest::SetUp();
mFakePointerController = std::make_shared<FakePointerController>();
@@ -3048,7 +3818,7 @@
void prepareDisplay(int32_t orientation) {
const std::string uniqueId = "local:0";
- const ViewportType viewportType = ViewportType::VIEWPORT_INTERNAL;
+ const ViewportType viewportType = ViewportType::INTERNAL;
setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
orientation, uniqueId, NO_PORT, viewportType);
}
@@ -3061,9 +3831,9 @@
int32_t rotatedY) {
NotifyMotionArgs args;
- process(mapper, ARBITRARY_TIME, EV_REL, REL_X, originalX);
- process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, originalY);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, originalX);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, originalY);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -3138,14 +3908,14 @@
addConfigurationProperty("cursor.mode", "navigation");
CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
- mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+ mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
NotifyMotionArgs args;
// Button press.
// Mostly testing non x/y behavior here so we don't need to check again elsewhere.
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
ASSERT_EQ(DEVICE_ID, args.deviceId);
@@ -3185,8 +3955,8 @@
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
// Button release. Should have same down time.
- process(mapper, ARBITRARY_TIME + 1, EV_KEY, BTN_MOUSE, 0);
- process(mapper, ARBITRARY_TIME + 1, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, BTN_MOUSE, 0);
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
ASSERT_EQ(DEVICE_ID, args.deviceId);
@@ -3233,16 +4003,16 @@
NotifyMotionArgs args;
// Motion in X but not Y.
- process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 1);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
1.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
// Motion in Y but not X.
- process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, -2);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, -2);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -3256,8 +4026,8 @@
NotifyMotionArgs args;
// Button press.
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -3269,8 +4039,8 @@
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
// Button release.
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 0);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -3289,10 +4059,10 @@
NotifyMotionArgs args;
// Combined X, Y and Button.
- process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 1);
- process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, -2);
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, -2);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -3306,9 +4076,9 @@
1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
// Move X, Y a bit while pressed.
- process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 2);
- process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 1);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 2);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -3316,8 +4086,8 @@
1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
// Release Button.
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 0);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -3402,8 +4172,8 @@
NotifyKeyArgs keyArgs;
// press BTN_LEFT, release BTN_LEFT
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_LEFT, 1);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_LEFT, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
@@ -3418,8 +4188,8 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_LEFT, 0);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_LEFT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
@@ -3442,9 +4212,9 @@
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
// press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 1);
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 1);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_RIGHT, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
@@ -3471,8 +4241,8 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 0);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_RIGHT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
@@ -3487,16 +4257,16 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 0);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, mFakePointerController->getButtonState());
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 0);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(0, motionArgs.buttonState);
@@ -3513,8 +4283,8 @@
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
// press BTN_BACK, release BTN_BACK
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_BACK, 1);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_BACK, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
@@ -3533,8 +4303,8 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_BACK, 0);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_BACK, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
@@ -3554,8 +4324,8 @@
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
// press BTN_SIDE, release BTN_SIDE
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_SIDE, 1);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_SIDE, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
@@ -3574,8 +4344,8 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_SIDE, 0);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_SIDE, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
@@ -3595,8 +4365,8 @@
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
// press BTN_FORWARD, release BTN_FORWARD
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_FORWARD, 1);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_FORWARD, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
@@ -3615,8 +4385,8 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_FORWARD, 0);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_FORWARD, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
@@ -3636,8 +4406,8 @@
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
// press BTN_EXTRA, release BTN_EXTRA
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_EXTRA, 1);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_EXTRA, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
@@ -3656,8 +4426,8 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_EXTRA, 0);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_EXTRA, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
@@ -3687,9 +4457,9 @@
NotifyMotionArgs args;
- process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 10);
- process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 20);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
@@ -3715,9 +4485,9 @@
NotifyMotionArgs args;
// Move.
- process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 10);
- process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 20);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
@@ -3726,8 +4496,8 @@
ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 100.0f, 200.0f));
// Button press.
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
@@ -3740,8 +4510,8 @@
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
// Button release.
- process(mapper, ARBITRARY_TIME + 2, EV_KEY, BTN_MOUSE, 0);
- process(mapper, ARBITRARY_TIME + 2, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME + 2, READ_TIME, EV_KEY, BTN_MOUSE, 0);
+ process(mapper, ARBITRARY_TIME + 2, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
@@ -3754,9 +4524,9 @@
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
// Another move.
- process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 30);
- process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 40);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 30);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 40);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
@@ -3766,18 +4536,18 @@
// Disable pointer capture and check that the device generation got bumped
// and events are generated the usual way.
- const uint32_t generation = mFakeContext->getGeneration();
+ const uint32_t generation = mReader->getContext()->getGeneration();
mFakePolicy->setPointerCapture(false);
configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
- ASSERT_TRUE(mFakeContext->getGeneration() != generation);
+ ASSERT_TRUE(mReader->getContext()->getGeneration() != generation);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
ASSERT_EQ(DEVICE_ID, resetArgs.deviceId);
- process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 10);
- process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 20);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
@@ -3793,8 +4563,8 @@
constexpr int32_t SECOND_DISPLAY_ID = 1;
const std::string SECOND_DISPLAY_UNIQUE_ID = "local:1";
mFakePolicy->addDisplayViewport(SECOND_DISPLAY_ID, 800, 480, DISPLAY_ORIENTATION_0,
- SECOND_DISPLAY_UNIQUE_ID, NO_PORT,
- ViewportType::VIEWPORT_EXTERNAL);
+ true /*isActive*/, SECOND_DISPLAY_UNIQUE_ID, NO_PORT,
+ ViewportType::EXTERNAL);
mFakePolicy->setDefaultPointerDisplayId(SECOND_DISPLAY_ID);
configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
@@ -3803,9 +4573,9 @@
mFakePointerController->setButtonState(0);
NotifyMotionArgs args;
- process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 10);
- process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 20);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
@@ -3921,8 +4691,8 @@
};
void TouchInputMapperTest::prepareDisplay(int32_t orientation, std::optional<uint8_t> port) {
- setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation,
- UNIQUE_ID, port, ViewportType::VIEWPORT_INTERNAL);
+ setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, UNIQUE_ID,
+ port, ViewportType::INTERNAL);
}
void TouchInputMapperTest::prepareSecondaryDisplay(ViewportType type, std::optional<uint8_t> port) {
@@ -3931,9 +4701,9 @@
}
void TouchInputMapperTest::prepareVirtualDisplay(int32_t orientation) {
- setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH,
- VIRTUAL_DISPLAY_HEIGHT, orientation,
- VIRTUAL_DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_VIRTUAL);
+ setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH, VIRTUAL_DISPLAY_HEIGHT,
+ orientation, VIRTUAL_DISPLAY_UNIQUE_ID, NO_PORT,
+ ViewportType::VIRTUAL);
}
void TouchInputMapperTest::prepareVirtualKeys() {
@@ -4028,46 +4798,46 @@
}
void SingleTouchInputMapperTest::processDown(SingleTouchInputMapper& mapper, int32_t x, int32_t y) {
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 1);
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_X, x);
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_Y, y);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_TOUCH, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_X, x);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_Y, y);
}
void SingleTouchInputMapperTest::processMove(SingleTouchInputMapper& mapper, int32_t x, int32_t y) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_X, x);
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_Y, y);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_X, x);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_Y, y);
}
void SingleTouchInputMapperTest::processUp(SingleTouchInputMapper& mapper) {
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_TOUCH, 0);
}
void SingleTouchInputMapperTest::processPressure(SingleTouchInputMapper& mapper, int32_t pressure) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_PRESSURE, pressure);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_PRESSURE, pressure);
}
void SingleTouchInputMapperTest::processToolMajor(SingleTouchInputMapper& mapper,
int32_t toolMajor) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_TOOL_WIDTH, toolMajor);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_TOOL_WIDTH, toolMajor);
}
void SingleTouchInputMapperTest::processDistance(SingleTouchInputMapper& mapper, int32_t distance) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_DISTANCE, distance);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_DISTANCE, distance);
}
void SingleTouchInputMapperTest::processTilt(SingleTouchInputMapper& mapper, int32_t tiltX,
int32_t tiltY) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_TILT_X, tiltX);
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_TILT_Y, tiltY);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_TILT_X, tiltX);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_TILT_Y, tiltY);
}
void SingleTouchInputMapperTest::processKey(SingleTouchInputMapper& mapper, int32_t code,
int32_t value) {
- process(mapper, ARBITRARY_TIME, EV_KEY, code, value);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, code, value);
}
void SingleTouchInputMapperTest::processSync(SingleTouchInputMapper& mapper) {
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
}
TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndNotACursor_ReturnsPointer) {
@@ -4185,7 +4955,7 @@
prepareVirtualKeys();
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
- mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+ mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
NotifyKeyArgs args;
@@ -4235,7 +5005,7 @@
prepareVirtualKeys();
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
- mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+ mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
NotifyKeyArgs keyArgs;
@@ -4356,7 +5126,7 @@
prepareVirtualKeys();
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
- mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+ mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
NotifyMotionArgs motionArgs;
@@ -4431,7 +5201,7 @@
prepareVirtualKeys();
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
- mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+ mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
NotifyMotionArgs motionArgs;
@@ -4527,7 +5297,7 @@
prepareVirtualKeys();
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
- mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+ mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
NotifyMotionArgs motionArgs;
@@ -5355,64 +6125,64 @@
void MultiTouchInputMapperTest::processPosition(MultiTouchInputMapper& mapper, int32_t x,
int32_t y) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, x);
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, y);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_POSITION_X, x);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_POSITION_Y, y);
}
void MultiTouchInputMapperTest::processTouchMajor(MultiTouchInputMapper& mapper,
int32_t touchMajor) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TOUCH_MAJOR, touchMajor);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_TOUCH_MAJOR, touchMajor);
}
void MultiTouchInputMapperTest::processTouchMinor(MultiTouchInputMapper& mapper,
int32_t touchMinor) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TOUCH_MINOR, touchMinor);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_TOUCH_MINOR, touchMinor);
}
void MultiTouchInputMapperTest::processToolMajor(MultiTouchInputMapper& mapper, int32_t toolMajor) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_WIDTH_MAJOR, toolMajor);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_WIDTH_MAJOR, toolMajor);
}
void MultiTouchInputMapperTest::processToolMinor(MultiTouchInputMapper& mapper, int32_t toolMinor) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_WIDTH_MINOR, toolMinor);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_WIDTH_MINOR, toolMinor);
}
void MultiTouchInputMapperTest::processOrientation(MultiTouchInputMapper& mapper,
int32_t orientation) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_ORIENTATION, orientation);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_ORIENTATION, orientation);
}
void MultiTouchInputMapperTest::processPressure(MultiTouchInputMapper& mapper, int32_t pressure) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_PRESSURE, pressure);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_PRESSURE, pressure);
}
void MultiTouchInputMapperTest::processDistance(MultiTouchInputMapper& mapper, int32_t distance) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_DISTANCE, distance);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_DISTANCE, distance);
}
void MultiTouchInputMapperTest::processId(MultiTouchInputMapper& mapper, int32_t id) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, id);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_TRACKING_ID, id);
}
void MultiTouchInputMapperTest::processSlot(MultiTouchInputMapper& mapper, int32_t slot) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, slot);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_SLOT, slot);
}
void MultiTouchInputMapperTest::processToolType(MultiTouchInputMapper& mapper, int32_t toolType) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, toolType);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_TOOL_TYPE, toolType);
}
void MultiTouchInputMapperTest::processKey(MultiTouchInputMapper& mapper, int32_t code,
int32_t value) {
- process(mapper, ARBITRARY_TIME, EV_KEY, code, value);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, code, value);
}
void MultiTouchInputMapperTest::processMTSync(MultiTouchInputMapper& mapper) {
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_MT_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_MT_REPORT, 0);
}
void MultiTouchInputMapperTest::processSync(MultiTouchInputMapper& mapper) {
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
}
TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithoutTrackingIds) {
@@ -5422,7 +6192,7 @@
prepareVirtualKeys();
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
- mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+ mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
NotifyMotionArgs motionArgs;
@@ -5698,7 +6468,7 @@
prepareVirtualKeys();
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
- mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+ mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
NotifyMotionArgs motionArgs;
@@ -5873,7 +6643,7 @@
prepareVirtualKeys();
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
- mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+ mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
NotifyMotionArgs motionArgs;
@@ -6786,7 +7556,7 @@
const uint8_t hdmi1 = 0;
const uint8_t hdmi2 = 1;
const std::string secondaryUniqueId = "uniqueId2";
- constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL;
+ constexpr ViewportType type = ViewportType::EXTERNAL;
addConfigurationProperty("touch.deviceType", "touchScreen");
prepareAxes(POSITION);
@@ -6827,7 +7597,7 @@
mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
- prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL);
+ prepareSecondaryDisplay(ViewportType::EXTERNAL);
prepareDisplay(DISPLAY_ORIENTATION_0);
prepareAxes(POSITION);
@@ -6845,6 +7615,103 @@
ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId);
}
+/**
+ * Ensure that the readTime is set to the SYN_REPORT value when processing touch events.
+ */
+TEST_F(MultiTouchInputMapperTest, Process_SendsReadTime) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareAxes(POSITION);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ process(mapper, 10, 11 /*readTime*/, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ process(mapper, 15, 16 /*readTime*/, EV_ABS, ABS_MT_POSITION_X, 100);
+ process(mapper, 20, 21 /*readTime*/, EV_ABS, ABS_MT_POSITION_Y, 100);
+ process(mapper, 25, 26 /*readTime*/, EV_SYN, SYN_REPORT, 0);
+
+ NotifyMotionArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(26, args.readTime);
+
+ process(mapper, 30, 31 /*readTime*/, EV_ABS, ABS_MT_POSITION_X, 110);
+ process(mapper, 30, 32 /*readTime*/, EV_ABS, ABS_MT_POSITION_Y, 220);
+ process(mapper, 30, 33 /*readTime*/, EV_SYN, SYN_REPORT, 0);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(33, args.readTime);
+}
+
+/**
+ * When the viewport is not active (isActive=false), the touch mapper should be disabled and the
+ * events should not be delivered to the listener.
+ */
+TEST_F(MultiTouchInputMapperTest, WhenViewportIsNotActive_TouchesAreDropped) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, false /*isActive*/, UNIQUE_ID, NO_PORT,
+ ViewportType::INTERNAL);
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ prepareAxes(POSITION);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ NotifyMotionArgs motionArgs;
+ processPosition(mapper, 100, 100);
+ processSync(mapper);
+
+ mFakeListener->assertNotifyMotionWasNotCalled();
+}
+
+TEST_F(MultiTouchInputMapperTest, Process_DeactivateViewport_AbortTouches) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, true /*isActive*/, UNIQUE_ID, NO_PORT,
+ ViewportType::INTERNAL);
+ std::optional<DisplayViewport> optionalDisplayViewport =
+ mFakePolicy->getDisplayViewportByUniqueId(UNIQUE_ID);
+ ASSERT_TRUE(optionalDisplayViewport.has_value());
+ DisplayViewport displayViewport = *optionalDisplayViewport;
+
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ prepareAxes(POSITION);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ // Finger down
+ int32_t x = 100, y = 100;
+ processPosition(mapper, x, y);
+ processSync(mapper);
+
+ NotifyMotionArgs motionArgs;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+
+ // Deactivate display viewport
+ displayViewport.isActive = false;
+ ASSERT_TRUE(mFakePolicy->updateViewport(displayViewport));
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+
+ // Finger move
+ x += 10, y += 10;
+ processPosition(mapper, x, y);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ EXPECT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action);
+
+ // Reactivate display viewport
+ displayViewport.isActive = true;
+ ASSERT_TRUE(mFakePolicy->updateViewport(displayViewport));
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+
+ // Finger move again
+ x += 10, y += 10;
+ processPosition(mapper, x, y);
+ processSync(mapper);
+
+ // Gesture is aborted, so events after display is activated won't be dispatched until there is
+ // no pointer on the touch device.
+ mFakeListener->assertNotifyMotionWasNotCalled();
+}
+
TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) {
// Setup the first touch screen device.
prepareAxes(POSITION | ID | SLOT);
@@ -6853,15 +7720,13 @@
// Create the second touch screen device, and enable multi fingers.
const std::string USB2 = "USB2";
+ const std::string DEVICE_NAME2 = "TOUCHSCREEN2";
constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1;
- InputDeviceIdentifier identifier;
- identifier.name = "TOUCHSCREEN2";
- identifier.location = USB2;
- std::unique_ptr<InputDevice> device2 =
- std::make_unique<InputDevice>(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION,
- identifier);
- mFakeEventHub->addDevice(SECOND_EVENTHUB_ID, DEVICE_NAME, 0 /*classes*/);
+ std::shared_ptr<InputDevice> device2 =
+ newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID,
+ Flags<InputDeviceClass>(0));
+
mFakeEventHub->addAbsoluteAxis(SECOND_EVENTHUB_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX,
0 /*flat*/, 0 /*fuzz*/);
mFakeEventHub->addAbsoluteAxis(SECOND_EVENTHUB_ID, ABS_MT_POSITION_Y, RAW_Y_MIN, RAW_Y_MAX,
@@ -6894,11 +7759,11 @@
// Create displays.
prepareDisplay(DISPLAY_ORIENTATION_0, hdmi1);
- prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL, hdmi2);
+ prepareSecondaryDisplay(ViewportType::EXTERNAL, hdmi2);
// Default device will reconfigure above, need additional reconfiguration for another device.
device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ InputReaderConfiguration::CHANGE_DISPLAY_INFO);
// Two fingers down at default display.
int32_t x1 = 100, y1 = 125, x2 = 300, y2 = 500;
@@ -7005,7 +7870,7 @@
TEST_F(MultiTouchInputMapperTest, Configure_EnabledForAssociatedDisplay) {
constexpr uint8_t hdmi2 = 1;
const std::string secondaryUniqueId = "uniqueId2";
- constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL;
+ constexpr ViewportType type = ViewportType::EXTERNAL;
mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi2);
@@ -7028,8 +7893,6 @@
ASSERT_EQ(SECONDARY_DISPLAY_ID, args.displayId);
}
-
-
TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleSingleTouch) {
addConfigurationProperty("touch.deviceType", "touchScreen");
prepareDisplay(DISPLAY_ORIENTATION_0);
@@ -7373,9 +8236,7 @@
class MultiTouchInputMapperTest_ExternalDevice : public MultiTouchInputMapperTest {
protected:
- virtual void SetUp() override {
- InputMapperTest::SetUp(DEVICE_CLASSES | INPUT_DEVICE_CLASS_EXTERNAL);
- }
+ void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL); }
};
/**
@@ -7399,7 +8260,7 @@
ASSERT_EQ(ADISPLAY_ID_DEFAULT, motionArgs.displayId);
// Expect the event to be sent to the external viewport if it is present.
- prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL);
+ prepareSecondaryDisplay(ViewportType::EXTERNAL);
processPosition(mapper, 100, 100);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
@@ -7413,7 +8274,7 @@
protected:
void halfDisplayToCenterHorizontal(int32_t orientation) {
std::optional<DisplayViewport> internalViewport =
- mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
// Half display to (width/4, 0, width * 3/4, height) to make display has offset.
internalViewport->orientation = orientation;
@@ -7778,4 +8639,218 @@
ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
}
+// --- PeripheralControllerTest ---
+
+class PeripheralControllerTest : public testing::Test {
+protected:
+ static const char* DEVICE_NAME;
+ static const char* DEVICE_LOCATION;
+ static const int32_t DEVICE_ID;
+ static const int32_t DEVICE_GENERATION;
+ static const int32_t DEVICE_CONTROLLER_NUMBER;
+ static const Flags<InputDeviceClass> DEVICE_CLASSES;
+ static const int32_t EVENTHUB_ID;
+
+ std::shared_ptr<FakeEventHub> mFakeEventHub;
+ sp<FakeInputReaderPolicy> mFakePolicy;
+ sp<TestInputListener> mFakeListener;
+ std::unique_ptr<InstrumentedInputReader> mReader;
+ std::shared_ptr<InputDevice> mDevice;
+
+ virtual void SetUp(Flags<InputDeviceClass> classes) {
+ mFakeEventHub = std::make_unique<FakeEventHub>();
+ mFakePolicy = new FakeInputReaderPolicy();
+ mFakeListener = new TestInputListener();
+ mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
+ mFakeListener);
+ mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes);
+ }
+
+ void SetUp() override { SetUp(DEVICE_CLASSES); }
+
+ void TearDown() override {
+ mFakeListener.clear();
+ mFakePolicy.clear();
+ }
+
+ void configureDevice(uint32_t changes) {
+ if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+ mReader->requestRefreshConfiguration(changes);
+ mReader->loopOnce();
+ }
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
+ }
+
+ std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
+ const std::string& location, int32_t eventHubId,
+ Flags<InputDeviceClass> classes) {
+ InputDeviceIdentifier identifier;
+ identifier.name = name;
+ identifier.location = location;
+ std::shared_ptr<InputDevice> device =
+ std::make_shared<InputDevice>(mReader->getContext(), deviceId, DEVICE_GENERATION,
+ identifier);
+ mReader->pushNextDevice(device);
+ mFakeEventHub->addDevice(eventHubId, name, classes);
+ mReader->loopOnce();
+ return device;
+ }
+
+ template <class T, typename... Args>
+ T& addControllerAndConfigure(Args... args) {
+ T& controller = mDevice->addController<T>(EVENTHUB_ID, args...);
+
+ return controller;
+ }
+};
+
+const char* PeripheralControllerTest::DEVICE_NAME = "device";
+const char* PeripheralControllerTest::DEVICE_LOCATION = "BLUETOOTH";
+const int32_t PeripheralControllerTest::DEVICE_ID = END_RESERVED_ID + 1000;
+const int32_t PeripheralControllerTest::DEVICE_GENERATION = 2;
+const int32_t PeripheralControllerTest::DEVICE_CONTROLLER_NUMBER = 0;
+const Flags<InputDeviceClass> PeripheralControllerTest::DEVICE_CLASSES =
+ Flags<InputDeviceClass>(0); // not needed for current tests
+const int32_t PeripheralControllerTest::EVENTHUB_ID = 1;
+
+// --- BatteryControllerTest ---
+class BatteryControllerTest : public PeripheralControllerTest {
+protected:
+ void SetUp() override {
+ PeripheralControllerTest::SetUp(DEVICE_CLASSES | InputDeviceClass::BATTERY);
+ }
+};
+
+TEST_F(BatteryControllerTest, GetBatteryCapacity) {
+ PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+
+ ASSERT_TRUE(controller.getBatteryCapacity(DEFAULT_BATTERY));
+ ASSERT_EQ(controller.getBatteryCapacity(DEFAULT_BATTERY).value_or(-1), BATTERY_CAPACITY);
+}
+
+TEST_F(BatteryControllerTest, GetBatteryStatus) {
+ PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+
+ ASSERT_TRUE(controller.getBatteryStatus(DEFAULT_BATTERY));
+ ASSERT_EQ(controller.getBatteryStatus(DEFAULT_BATTERY).value_or(-1), BATTERY_STATUS);
+}
+
+// --- LightControllerTest ---
+class LightControllerTest : public PeripheralControllerTest {
+protected:
+ void SetUp() override {
+ PeripheralControllerTest::SetUp(DEVICE_CLASSES | InputDeviceClass::LIGHT);
+ }
+};
+
+TEST_F(LightControllerTest, SingleLight) {
+ RawLightInfo infoSingle = {.id = 1,
+ .name = "Mono",
+ .maxBrightness = 255,
+ .flags = InputLightClass::BRIGHTNESS,
+ .path = ""};
+ mFakeEventHub->addRawLightInfo(infoSingle.id, std::move(infoSingle));
+
+ PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+ InputDeviceInfo info;
+ controller.populateDeviceInfo(&info);
+ const auto& ids = info.getLightIds();
+ ASSERT_EQ(1UL, ids.size());
+ ASSERT_EQ(InputDeviceLightType::SINGLE, info.getLightInfo(ids[0])->type);
+
+ ASSERT_TRUE(controller.setLightColor(ids[0], LIGHT_BRIGHTNESS));
+ ASSERT_EQ(controller.getLightColor(ids[0]).value_or(-1), LIGHT_BRIGHTNESS);
+}
+
+TEST_F(LightControllerTest, RGBLight) {
+ RawLightInfo infoRed = {.id = 1,
+ .name = "red",
+ .maxBrightness = 255,
+ .flags = InputLightClass::BRIGHTNESS | InputLightClass::RED,
+ .path = ""};
+ RawLightInfo infoGreen = {.id = 2,
+ .name = "green",
+ .maxBrightness = 255,
+ .flags = InputLightClass::BRIGHTNESS | InputLightClass::GREEN,
+ .path = ""};
+ RawLightInfo infoBlue = {.id = 3,
+ .name = "blue",
+ .maxBrightness = 255,
+ .flags = InputLightClass::BRIGHTNESS | InputLightClass::BLUE,
+ .path = ""};
+ mFakeEventHub->addRawLightInfo(infoRed.id, std::move(infoRed));
+ mFakeEventHub->addRawLightInfo(infoGreen.id, std::move(infoGreen));
+ mFakeEventHub->addRawLightInfo(infoBlue.id, std::move(infoBlue));
+
+ PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+ InputDeviceInfo info;
+ controller.populateDeviceInfo(&info);
+ const auto& ids = info.getLightIds();
+ ASSERT_EQ(1UL, ids.size());
+ ASSERT_EQ(InputDeviceLightType::RGB, info.getLightInfo(ids[0])->type);
+
+ ASSERT_TRUE(controller.setLightColor(ids[0], LIGHT_COLOR));
+ ASSERT_EQ(controller.getLightColor(ids[0]).value_or(-1), LIGHT_COLOR);
+}
+
+TEST_F(LightControllerTest, MultiColorRGBLight) {
+ RawLightInfo infoColor = {.id = 1,
+ .name = "red",
+ .maxBrightness = 255,
+ .flags = InputLightClass::BRIGHTNESS |
+ InputLightClass::MULTI_INTENSITY |
+ InputLightClass::MULTI_INDEX,
+ .path = ""};
+
+ mFakeEventHub->addRawLightInfo(infoColor.id, std::move(infoColor));
+
+ PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+ InputDeviceInfo info;
+ controller.populateDeviceInfo(&info);
+ const auto& ids = info.getLightIds();
+ ASSERT_EQ(1UL, ids.size());
+ ASSERT_EQ(InputDeviceLightType::MULTI_COLOR, info.getLightInfo(ids[0])->type);
+
+ ASSERT_TRUE(controller.setLightColor(ids[0], LIGHT_COLOR));
+ ASSERT_EQ(controller.getLightColor(ids[0]).value_or(-1), LIGHT_COLOR);
+}
+
+TEST_F(LightControllerTest, PlayerIdLight) {
+ RawLightInfo info1 = {.id = 1,
+ .name = "player1",
+ .maxBrightness = 255,
+ .flags = InputLightClass::BRIGHTNESS,
+ .path = ""};
+ RawLightInfo info2 = {.id = 2,
+ .name = "player2",
+ .maxBrightness = 255,
+ .flags = InputLightClass::BRIGHTNESS,
+ .path = ""};
+ RawLightInfo info3 = {.id = 3,
+ .name = "player3",
+ .maxBrightness = 255,
+ .flags = InputLightClass::BRIGHTNESS,
+ .path = ""};
+ RawLightInfo info4 = {.id = 4,
+ .name = "player4",
+ .maxBrightness = 255,
+ .flags = InputLightClass::BRIGHTNESS,
+ .path = ""};
+ mFakeEventHub->addRawLightInfo(info1.id, std::move(info1));
+ mFakeEventHub->addRawLightInfo(info2.id, std::move(info2));
+ mFakeEventHub->addRawLightInfo(info3.id, std::move(info3));
+ mFakeEventHub->addRawLightInfo(info4.id, std::move(info4));
+
+ PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+ InputDeviceInfo info;
+ controller.populateDeviceInfo(&info);
+ const auto& ids = info.getLightIds();
+ ASSERT_EQ(1UL, ids.size());
+ ASSERT_EQ(InputDeviceLightType::PLAYER_ID, info.getLightInfo(ids[0])->type);
+
+ ASSERT_FALSE(controller.setLightColor(ids[0], LIGHT_COLOR));
+ ASSERT_TRUE(controller.setLightPlayerId(ids[0], LIGHT_PLAYER_ID));
+ ASSERT_EQ(controller.getLightPlayerId(ids[0]).value_or(-1), LIGHT_PLAYER_ID);
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
index 9bff166..fb7de97 100644
--- a/services/inputflinger/tests/TestInputListener.cpp
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -14,11 +14,10 @@
* limitations under the License.
*/
+#include "TestInputListener.h"
#include <gtest/gtest.h>
-#include "TestInputListener.h"
-
namespace android {
// --- TestInputListener ---
@@ -28,7 +27,7 @@
: mEventHappenedTimeout(eventHappenedTimeout),
mEventDidNotHappenTimeout(eventDidNotHappenTimeout) {}
-TestInputListener::~TestInputListener() { }
+TestInputListener::~TestInputListener() {}
void TestInputListener::assertNotifyConfigurationChangedWasCalled(
NotifyConfigurationChangedArgs* outEventArgs) {
@@ -43,8 +42,7 @@
"notifyConfigurationChanged() should not be called."));
}
-void TestInputListener::assertNotifyDeviceResetWasCalled(
- NotifyDeviceResetArgs* outEventArgs) {
+void TestInputListener::assertNotifyDeviceResetWasCalled(NotifyDeviceResetArgs* outEventArgs) {
ASSERT_NO_FATAL_FAILURE(
assertCalled<
NotifyDeviceResetArgs>(outEventArgs,
@@ -73,7 +71,7 @@
void TestInputListener::assertNotifyMotionWasNotCalled() {
ASSERT_NO_FATAL_FAILURE(
- assertNotCalled<NotifySwitchArgs>("notifySwitch() should not be called."));
+ assertNotCalled<NotifyMotionArgs>("notifyMotion() should not be called."));
}
void TestInputListener::assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs) {
@@ -82,6 +80,26 @@
"Expected notifySwitch() to have been called."));
}
+void TestInputListener::assertNotifySensorWasCalled(NotifySensorArgs* outEventArgs) {
+ ASSERT_NO_FATAL_FAILURE(
+ assertCalled<NotifySensorArgs>(outEventArgs,
+ "Expected notifySensor() to have been called."));
+}
+
+void TestInputListener::assertNotifyVibratorStateWasCalled(NotifyVibratorStateArgs* outEventArgs) {
+ ASSERT_NO_FATAL_FAILURE(assertCalled<NotifyVibratorStateArgs>(outEventArgs,
+ "Expected notifyVibratorState() "
+ "to have been called."));
+}
+
+void TestInputListener::assertNotifyCaptureWasCalled(
+ NotifyPointerCaptureChangedArgs* outEventArgs) {
+ ASSERT_NO_FATAL_FAILURE(
+ assertCalled<NotifyPointerCaptureChangedArgs>(outEventArgs,
+ "Expected notifyPointerCaptureChanged() "
+ "to have been called."));
+}
+
template <class NotifyArgsType>
void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string message) {
std::unique_lock<std::mutex> lock(mLock);
@@ -145,4 +163,16 @@
notify<NotifySwitchArgs>(args);
}
+void TestInputListener::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
+ notify<NotifyPointerCaptureChangedArgs>(args);
+}
+
+void TestInputListener::notifySensor(const NotifySensorArgs* args) {
+ notify<NotifySensorArgs>(args);
+}
+
+void TestInputListener::notifyVibratorState(const NotifyVibratorStateArgs* args) {
+ notify<NotifyVibratorStateArgs>(args);
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
index d50c6bc..0ffcaaa 100644
--- a/services/inputflinger/tests/TestInputListener.h
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -54,6 +54,10 @@
void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = nullptr);
+ void assertNotifyCaptureWasCalled(NotifyPointerCaptureChangedArgs* outEventArgs = nullptr);
+ void assertNotifySensorWasCalled(NotifySensorArgs* outEventArgs = nullptr);
+ void assertNotifyVibratorStateWasCalled(NotifyVibratorStateArgs* outEventArgs = nullptr);
+
private:
template <class NotifyArgsType>
void assertCalled(NotifyArgsType* outEventArgs, std::string message);
@@ -74,16 +78,25 @@
virtual void notifySwitch(const NotifySwitchArgs* args) override;
+ virtual void notifySensor(const NotifySensorArgs* args) override;
+
+ virtual void notifyVibratorState(const NotifyVibratorStateArgs* args) override;
+
+ virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
+
std::mutex mLock;
std::condition_variable mCondition;
const std::chrono::milliseconds mEventHappenedTimeout;
const std::chrono::milliseconds mEventDidNotHappenTimeout;
- std::tuple<std::vector<NotifyConfigurationChangedArgs>, //
- std::vector<NotifyDeviceResetArgs>, //
- std::vector<NotifyKeyArgs>, //
- std::vector<NotifyMotionArgs>, //
- std::vector<NotifySwitchArgs>> //
+ std::tuple<std::vector<NotifyConfigurationChangedArgs>, //
+ std::vector<NotifyDeviceResetArgs>, //
+ std::vector<NotifyKeyArgs>, //
+ std::vector<NotifyMotionArgs>, //
+ std::vector<NotifySwitchArgs>, //
+ std::vector<NotifySensorArgs>, //
+ std::vector<NotifyVibratorStateArgs>, //
+ std::vector<NotifyPointerCaptureChangedArgs>> //
mQueues GUARDED_BY(mLock);
};
diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp
deleted file mode 100644
index 2a90a08..0000000
--- a/services/inputflinger/tests/fuzzers/Android.bp
+++ /dev/null
@@ -1,121 +0,0 @@
-//
-// Copyright (C) 2020 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"],
-}
-
-cc_defaults {
- name: "inputflinger_fuzz_defaults",
- defaults: [
- "inputflinger_defaults",
- ],
- include_dirs: [
- "frameworks/native/services/inputflinger",
- ],
- shared_libs: [
- "android.hardware.input.classifier@1.0",
- "libbase",
- "libbinder",
- "libcutils",
- "liblog",
- "libutils",
- "libui",
- "libinput",
- "libinputflinger",
- "libinputreader",
- "libinputflinger_base",
- "libstatslog",
- ],
- header_libs: [
- "libinputreader_headers",
- ],
-}
-
-cc_fuzz {
- name: "inputflinger_cursor_input_fuzzer",
- defaults: [
- "inputflinger_fuzz_defaults",
- ],
- srcs: [
- "CursorInputFuzzer.cpp",
- ],
-}
-
-cc_fuzz {
- name: "inputflinger_keyboard_input_fuzzer",
- defaults: [
- "inputflinger_fuzz_defaults",
- ],
- srcs: [
- "KeyboardInputFuzzer.cpp",
- ],
-}
-
-cc_fuzz {
- name: "inputflinger_multitouch_input_fuzzer",
- defaults: [
- "inputflinger_fuzz_defaults",
- ],
- srcs: [
- "MultiTouchInputFuzzer.cpp",
- ],
-}
-
-cc_fuzz {
- name: "inputflinger_switch_input_fuzzer",
- defaults: [
- "inputflinger_fuzz_defaults",
- ],
- srcs: [
- "SwitchInputFuzzer.cpp",
- ],
-}
-
-cc_fuzz {
- name: "inputflinger_input_reader_device_fuzzer",
- defaults: [
- "inputflinger_fuzz_defaults",
- ],
- srcs: [
- "InputReaderDeviceFuzzer.cpp",
- ],
-}
-
-cc_fuzz {
- name: "inputflinger_blocking_queue_fuzzer",
- defaults: [
- "inputflinger_fuzz_defaults",
- ],
- srcs: [
- "BlockingQueueFuzzer.cpp",
- ],
-}
-
-cc_fuzz {
- name: "inputflinger_input_classifier_fuzzer",
- defaults: [
- "inputflinger_fuzz_defaults",
- ],
- srcs: [
- "InputClassifierFuzzer.cpp",
- ],
-}
diff --git a/services/inputflinger/tests/fuzzers/BlockingQueueFuzzer.cpp b/services/inputflinger/tests/fuzzers/BlockingQueueFuzzer.cpp
deleted file mode 100644
index 6a136cf..0000000
--- a/services/inputflinger/tests/fuzzers/BlockingQueueFuzzer.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright 2020 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 <fuzzer/FuzzedDataProvider.h>
-#include <thread>
-#include "BlockingQueue.h"
-
-// Chosen to be a number large enough for variation in fuzzer runs, but not consume too much memory.
-static constexpr size_t MAX_CAPACITY = 1024;
-
-namespace android {
-
-extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) {
- FuzzedDataProvider fdp(data, size);
- size_t capacity = fdp.ConsumeIntegralInRange<size_t>(1, MAX_CAPACITY);
- size_t filled = 0;
- BlockingQueue<int32_t> queue(capacity);
-
- while (fdp.remaining_bytes() > 0) {
- fdp.PickValueInArray<std::function<void()>>({
- [&]() -> void {
- size_t numPushes = fdp.ConsumeIntegralInRange<size_t>(0, capacity + 1);
- for (size_t i = 0; i < numPushes; i++) {
- queue.push(fdp.ConsumeIntegral<int32_t>());
- }
- filled = std::min(capacity, filled + numPushes);
- },
- [&]() -> void {
- // Pops blocks if it is empty, so only pop up to num elements inserted.
- size_t numPops = fdp.ConsumeIntegralInRange<size_t>(0, filled);
- for (size_t i = 0; i < numPops; i++) {
- queue.pop();
- }
- filled > numPops ? filled -= numPops : filled = 0;
- },
- [&]() -> void {
- queue.clear();
- filled = 0;
- },
- [&]() -> void {
- int32_t eraseElement = fdp.ConsumeIntegral<int32_t>();
- queue.erase([&](int32_t element) {
- if (element == eraseElement) {
- filled--;
- return true;
- }
- return false;
- });
- },
- })();
- }
-
- return 0;
-}
-
-} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
deleted file mode 100644
index 8aa7434..0000000
--- a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright 2020 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 <CursorInputMapper.h>
-#include <FuzzContainer.h>
-#include <fuzzer/FuzzedDataProvider.h>
-
-namespace android {
-
-void addProperty(FuzzContainer& fuzzer, std::shared_ptr<FuzzedDataProvider> fdp) {
- // Pick a random property to set for the mapper to have set.
- fdp->PickValueInArray<std::function<void()>>(
- {[&]() -> void { fuzzer.addProperty(String8("cursor.mode"), String8("pointer")); },
- [&]() -> void { fuzzer.addProperty(String8("cursor.mode"), String8("navigation")); },
- [&]() -> void {
- fuzzer.addProperty(String8("cursor.mode"),
- String8(fdp->ConsumeRandomLengthString(100).data()));
- },
- [&]() -> void {
- fuzzer.addProperty(String8("cursor.orientationAware"),
- String8(fdp->ConsumeRandomLengthString(100).data()));
- }})();
-}
-
-extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
- std::shared_ptr<FuzzedDataProvider> fdp = std::make_shared<FuzzedDataProvider>(data, size);
- FuzzContainer fuzzer = FuzzContainer(fdp);
-
- CursorInputMapper& mapper = fuzzer.getMapper<CursorInputMapper>();
- auto policyConfig = fuzzer.getPolicyConfig();
-
- // Loop through mapper operations until randomness is exhausted.
- while (fdp->remaining_bytes() > 0) {
- fdp->PickValueInArray<std::function<void()>>({
- [&]() -> void { addProperty(fuzzer, fdp); },
- [&]() -> void {
- std::string dump;
- mapper.dump(dump);
- },
- [&]() -> void { mapper.getSources(); },
- [&]() -> void {
- mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig,
- fdp->ConsumeIntegral<int32_t>());
- },
- [&]() -> void {
- // Need to reconfigure with 0 or you risk a NPE.
- mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig, 0);
- InputDeviceInfo info;
- mapper.populateDeviceInfo(&info);
- },
- [&]() -> void {
- // Need to reconfigure with 0 or you risk a NPE.
- mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig, 0);
- RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(),
- fdp->ConsumeIntegral<int32_t>(),
- fdp->ConsumeIntegral<int32_t>(),
- fdp->ConsumeIntegral<int32_t>(),
- fdp->ConsumeIntegral<int32_t>()};
- mapper.process(&rawEvent);
- },
- [&]() -> void { mapper.reset(fdp->ConsumeIntegral<nsecs_t>()); },
- [&]() -> void {
- mapper.getScanCodeState(fdp->ConsumeIntegral<uint32_t>(),
- fdp->ConsumeIntegral<int32_t>());
- },
- [&]() -> void {
- // Need to reconfigure with 0 or you risk a NPE.
- mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig, 0);
- mapper.getAssociatedDisplayId();
- },
- })();
- }
-
- return 0;
-}
-
-} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/FuzzContainer.h b/services/inputflinger/tests/fuzzers/FuzzContainer.h
deleted file mode 100644
index 84f0566..0000000
--- a/services/inputflinger/tests/fuzzers/FuzzContainer.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright 2020 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.
- */
-
-#pragma once
-
-#include <InputDevice.h>
-#include <InputMapper.h>
-#include <InputReader.h>
-#include <MapperHelpers.h>
-#include <fuzzer/FuzzedDataProvider.h>
-
-namespace android {
-
-class FuzzContainer {
- int32_t meventID;
- std::shared_ptr<FuzzEventHub> mFuzzEventHub;
- sp<FuzzInputReaderPolicy> mFuzzPolicy;
- sp<FuzzInputListener> mFuzzListener;
- std::unique_ptr<FuzzInputReaderContext> mFuzzContext;
- std::unique_ptr<InputDevice> mFuzzDevice;
- InputReaderConfiguration mPolicyConfig;
- std::shared_ptr<FuzzedDataProvider> fdp;
-
-public:
- FuzzContainer(std::shared_ptr<FuzzedDataProvider> fdp) : fdp(fdp) {
- // Setup parameters.
- std::string deviceName = fdp->ConsumeRandomLengthString(16);
- std::string deviceLocation = fdp->ConsumeRandomLengthString(12);
- int32_t deviceID = fdp->ConsumeIntegralInRange<int32_t>(0, 5);
- int32_t deviceGeneration = fdp->ConsumeIntegralInRange<int32_t>(0, 5);
- meventID = fdp->ConsumeIntegral<int32_t>();
-
- // Create mocked objects.
- mFuzzEventHub = std::make_shared<FuzzEventHub>(fdp);
- mFuzzPolicy = new FuzzInputReaderPolicy(fdp);
- mFuzzListener = new FuzzInputListener();
- mFuzzContext = std::make_unique<FuzzInputReaderContext>(mFuzzEventHub, mFuzzPolicy,
- mFuzzListener, fdp);
-
- InputDeviceIdentifier identifier;
- identifier.name = deviceName;
- identifier.location = deviceLocation;
- mFuzzDevice = std::make_unique<InputDevice>(mFuzzContext.get(), deviceID, deviceGeneration,
- identifier);
- mFuzzPolicy->getReaderConfiguration(&mPolicyConfig);
- }
-
- ~FuzzContainer() {}
-
- void configureDevice() {
- nsecs_t arbitraryTime = fdp->ConsumeIntegral<nsecs_t>();
- mFuzzDevice->configure(arbitraryTime, &mPolicyConfig, 0);
- mFuzzDevice->reset(arbitraryTime);
- }
-
- void addProperty(const String8& key, const String8& value) {
- mFuzzEventHub->addProperty(key, value);
- configureDevice();
- }
-
- InputReaderConfiguration& getPolicyConfig() { return mPolicyConfig; }
-
- template <class T, typename... Args>
- T& getMapper(Args... args) {
- T& mapper = mFuzzDevice->addMapper<T>(fdp->ConsumeIntegral<int32_t>(), args...);
- configureDevice();
- return mapper;
- }
-};
-
-} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp
deleted file mode 100644
index 7f91000..0000000
--- a/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright 2020 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 <MapperHelpers.h>
-#include <fuzzer/FuzzedDataProvider.h>
-#include "InputClassifier.h"
-#include "InputClassifierConverter.h"
-
-namespace android {
-
-static constexpr int32_t MAX_AXES = 64;
-
-// Used by two fuzz operations and a bit lengthy, so pulled out into a function.
-NotifyMotionArgs generateFuzzedMotionArgs(FuzzedDataProvider &fdp) {
- // Create a basic motion event for testing
- PointerProperties properties;
- properties.id = 0;
- properties.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
- PointerCoords coords;
- coords.clear();
- for (int32_t i = 0; i < fdp.ConsumeIntegralInRange<int32_t>(0, MAX_AXES); i++) {
- coords.setAxisValue(fdp.ConsumeIntegral<int32_t>(), fdp.ConsumeFloatingPoint<float>());
- }
-
- nsecs_t downTime = 2;
- NotifyMotionArgs motionArgs(fdp.ConsumeIntegral<uint32_t>() /*sequenceNum*/,
- downTime /*eventTime*/, fdp.ConsumeIntegral<int32_t>() /*deviceId*/,
- AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT,
- fdp.ConsumeIntegral<uint32_t>() /*policyFlags*/,
- AMOTION_EVENT_ACTION_DOWN,
- fdp.ConsumeIntegral<int32_t>() /*actionButton*/,
- fdp.ConsumeIntegral<int32_t>() /*flags*/, AMETA_NONE,
- fdp.ConsumeIntegral<int32_t>() /*buttonState*/,
- MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
- 1 /*pointerCount*/, &properties, &coords,
- fdp.ConsumeFloatingPoint<float>() /*xPrecision*/,
- fdp.ConsumeFloatingPoint<float>() /*yPrecision*/,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime,
- {} /*videoFrames*/);
- return motionArgs;
-}
-
-extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) {
- FuzzedDataProvider fdp(data, size);
-
- sp<FuzzInputListener> mFuzzListener = new FuzzInputListener();
- sp<InputClassifierInterface> mClassifier = new InputClassifier(mFuzzListener);
-
- while (fdp.remaining_bytes() > 0) {
- fdp.PickValueInArray<std::function<void()>>({
- [&]() -> void {
- // SendToNextStage_NotifyConfigurationChangedArgs
- NotifyConfigurationChangedArgs
- args(fdp.ConsumeIntegral<uint32_t>() /*sequenceNum*/,
- fdp.ConsumeIntegral<nsecs_t>() /*eventTime*/);
- mClassifier->notifyConfigurationChanged(&args);
- },
- [&]() -> void {
- // SendToNextStage_NotifyKeyArgs
- NotifyKeyArgs keyArgs(fdp.ConsumeIntegral<uint32_t>() /*sequenceNum*/,
- fdp.ConsumeIntegral<nsecs_t>() /*eventTime*/,
- fdp.ConsumeIntegral<int32_t>() /*deviceId*/,
- AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_DEFAULT,
- fdp.ConsumeIntegral<uint32_t>() /*policyFlags*/,
- AKEY_EVENT_ACTION_DOWN,
- fdp.ConsumeIntegral<int32_t>() /*flags*/, AKEYCODE_HOME,
- fdp.ConsumeIntegral<int32_t>() /*scanCode*/, AMETA_NONE,
- fdp.ConsumeIntegral<nsecs_t>() /*downTime*/);
-
- mClassifier->notifyKey(&keyArgs);
- },
- [&]() -> void {
- // SendToNextStage_NotifyMotionArgs
- NotifyMotionArgs motionArgs = generateFuzzedMotionArgs(fdp);
- mClassifier->notifyMotion(&motionArgs);
- },
- [&]() -> void {
- // SendToNextStage_NotifySwitchArgs
- NotifySwitchArgs switchArgs(fdp.ConsumeIntegral<uint32_t>() /*sequenceNum*/,
- fdp.ConsumeIntegral<nsecs_t>() /*eventTime*/,
- fdp.ConsumeIntegral<uint32_t>() /*policyFlags*/,
- fdp.ConsumeIntegral<uint32_t>() /*switchValues*/,
- fdp.ConsumeIntegral<uint32_t>() /*switchMask*/);
-
- mClassifier->notifySwitch(&switchArgs);
- },
- [&]() -> void {
- // SendToNextStage_NotifyDeviceResetArgs
- NotifyDeviceResetArgs resetArgs(fdp.ConsumeIntegral<uint32_t>() /*sequenceNum*/,
- fdp.ConsumeIntegral<nsecs_t>() /*eventTime*/,
- fdp.ConsumeIntegral<int32_t>() /*deviceId*/);
-
- mClassifier->notifyDeviceReset(&resetArgs);
- },
- [&]() -> void {
- // InputClassifierConverterTest
- const NotifyMotionArgs motionArgs = generateFuzzedMotionArgs(fdp);
- hardware::input::common::V1_0::MotionEvent motionEvent =
- notifyMotionArgsToHalMotionEvent(motionArgs);
- },
- })();
- }
- return 0;
-}
-
-} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/InputReaderDeviceFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderDeviceFuzzer.cpp
deleted file mode 100644
index 9194891..0000000
--- a/services/inputflinger/tests/fuzzers/InputReaderDeviceFuzzer.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright 2020 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 <InputReader.h>
-#include <MapperHelpers.h>
-#include <fuzzer/FuzzedDataProvider.h>
-#include <chrono>
-#include <thread>
-
-namespace android {
-
-extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
- std::shared_ptr<FuzzedDataProvider> fdp = std::make_shared<FuzzedDataProvider>(data, size);
-
- sp<FuzzInputListener> fuzzListener = new FuzzInputListener();
- sp<FuzzInputReaderPolicy> fuzzPolicy = new FuzzInputReaderPolicy(fdp);
- std::shared_ptr<FuzzEventHub> fuzzEventHub = std::make_shared<FuzzEventHub>(fdp);
- std::unique_ptr<InputReader> reader =
- std::make_unique<InputReader>(fuzzEventHub, fuzzPolicy, fuzzListener);
-
- fuzzEventHub->addEvents(fdp);
- reader->start();
-
- // Loop through mapper operations until randomness is exhausted.
- while (fdp->remaining_bytes() > 0) {
- fdp->PickValueInArray<std::function<void()>>({
- [&]() -> void {
- std::string dump;
- reader->dump(dump);
- },
- [&]() -> void { reader->monitor(); },
- [&]() -> void { fuzzEventHub->addEvents(fdp); },
- [&]() -> void {
- std::vector<InputDeviceInfo> inputDevices;
- reader->getInputDevices(inputDevices);
- },
- [&]() -> void { reader->isInputDeviceEnabled(fdp->ConsumeIntegral<int32_t>()); },
- [&]() -> void {
- reader->getScanCodeState(fdp->ConsumeIntegral<int32_t>(),
- fdp->ConsumeIntegral<uint32_t>(),
- fdp->ConsumeIntegral<int32_t>());
- },
- [&]() -> void {
- reader->getKeyCodeState(fdp->ConsumeIntegral<int32_t>(),
- fdp->ConsumeIntegral<uint32_t>(),
- fdp->ConsumeIntegral<int32_t>());
- },
- [&]() -> void {
- reader->getSwitchState(fdp->ConsumeIntegral<int32_t>(),
- fdp->ConsumeIntegral<uint32_t>(),
- fdp->ConsumeIntegral<int32_t>());
- },
- [&]() -> void { reader->toggleCapsLockState(fdp->ConsumeIntegral<int32_t>()); },
- [&]() -> void {
- size_t count = fdp->ConsumeIntegralInRange<size_t>(1, 1024);
- uint8_t* outFlags = new uint8_t[count];
- reader->hasKeys(fdp->ConsumeIntegral<int32_t>(),
- fdp->ConsumeIntegral<uint32_t>(), count, nullptr, outFlags);
- delete[] outFlags;
- },
- [&]() -> void {
- reader->requestRefreshConfiguration(fdp->ConsumeIntegral<uint32_t>());
- },
- [&]() -> void {
- // 260 is slightly higher than the maximum intended size of 256.
- size_t count = fdp->ConsumeIntegralInRange<size_t>(0, 260);
- nsecs_t pattern[count];
-
- for (size_t i = 0; i < count; i++) pattern[i] = fdp->ConsumeIntegral<nsecs_t>();
-
- reader->vibrate(fdp->ConsumeIntegral<int32_t>(), pattern, count,
- fdp->ConsumeIntegral<ssize_t>(),
- fdp->ConsumeIntegral<int32_t>());
- },
- [&]() -> void {
- reader->cancelVibrate(fdp->ConsumeIntegral<int32_t>(),
- fdp->ConsumeIntegral<int32_t>());
- },
- [&]() -> void {
- reader->canDispatchToDisplay(fdp->ConsumeIntegral<int32_t>(),
- fdp->ConsumeIntegral<int32_t>());
- },
- })();
- }
-
- reader->stop();
- return 0;
-}
-
-} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
deleted file mode 100644
index 854e2fd..0000000
--- a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright 2020 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 <FuzzContainer.h>
-#include <KeyboardInputMapper.h>
-#include <fuzzer/FuzzedDataProvider.h>
-
-namespace android {
-
-void addProperty(FuzzContainer& fuzzer, std::shared_ptr<FuzzedDataProvider> fdp) {
- // Pick a random property to set for the mapper to have set.
- fdp->PickValueInArray<std::function<void()>>(
- {[&]() -> void {
- fuzzer.addProperty(String8("keyboard.orientationAware"), String8("1"));
- },
- [&]() -> void {
- fuzzer.addProperty(String8("keyboard.orientationAware"),
- String8(fdp->ConsumeRandomLengthString(100).data()));
- },
- [&]() -> void {
- fuzzer.addProperty(String8("keyboard.doNotWakeByDefault"),
- String8(fdp->ConsumeRandomLengthString(100).data()));
- },
- [&]() -> void {
- fuzzer.addProperty(String8("keyboard.handlesKeyRepeat"),
- String8(fdp->ConsumeRandomLengthString(100).data()));
- }})();
-}
-
-extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
- std::shared_ptr<FuzzedDataProvider> fdp = std::make_shared<FuzzedDataProvider>(data, size);
- FuzzContainer fuzzer = FuzzContainer(fdp);
-
- KeyboardInputMapper& mapper =
- fuzzer.getMapper<KeyboardInputMapper>(fdp->ConsumeIntegral<uint32_t>(),
- fdp->ConsumeIntegral<int32_t>());
- auto policyConfig = fuzzer.getPolicyConfig();
-
- // Loop through mapper operations until randomness is exhausted.
- while (fdp->remaining_bytes() > 0) {
- fdp->PickValueInArray<std::function<void()>>({
- [&]() -> void { addProperty(fuzzer, fdp); },
- [&]() -> void {
- std::string dump;
- mapper.dump(dump);
- },
- [&]() -> void {
- InputDeviceInfo info;
- mapper.populateDeviceInfo(&info);
- },
- [&]() -> void { mapper.getSources(); },
- [&]() -> void {
- mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig,
- fdp->ConsumeIntegral<uint32_t>());
- },
- [&]() -> void { mapper.reset(fdp->ConsumeIntegral<nsecs_t>()); },
- [&]() -> void {
- RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(),
- fdp->ConsumeIntegral<int32_t>(),
- fdp->ConsumeIntegral<int32_t>(),
- fdp->ConsumeIntegral<int32_t>(),
- fdp->ConsumeIntegral<int32_t>()};
- mapper.process(&rawEvent);
- },
- [&]() -> void {
- mapper.getKeyCodeState(fdp->ConsumeIntegral<uint32_t>(),
- fdp->ConsumeIntegral<int32_t>());
- },
- [&]() -> void {
- mapper.getScanCodeState(fdp->ConsumeIntegral<uint32_t>(),
- fdp->ConsumeIntegral<int32_t>());
- },
- [&]() -> void {
- mapper.markSupportedKeyCodes(fdp->ConsumeIntegral<uint32_t>(),
- fdp->ConsumeIntegral<size_t>(), nullptr, nullptr);
- },
- [&]() -> void { mapper.getMetaState(); },
- [&]() -> void { mapper.updateMetaState(fdp->ConsumeIntegral<int32_t>()); },
- [&]() -> void { mapper.getAssociatedDisplayId(); },
- })();
- }
-
- return 0;
-}
-
-} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
deleted file mode 100644
index edfb68d..0000000
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright 2020 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.
- */
-
-#pragma once
-
-#include <InputDevice.h>
-#include <InputMapper.h>
-#include <InputReader.h>
-#include <fuzzer/FuzzedDataProvider.h>
-
-namespace android {
-
-class FuzzEventHub : public EventHubInterface {
- InputDeviceIdentifier mIdentifier;
- std::vector<TouchVideoFrame> mVideoFrames;
- PropertyMap mFuzzConfig;
- std::mutex mEventLock;
- size_t mCount = 0;
- RawEvent mBuf[256];
- std::shared_ptr<FuzzedDataProvider> fdp;
-
-public:
- FuzzEventHub(std::shared_ptr<FuzzedDataProvider> fdp) : fdp(fdp) {}
- ~FuzzEventHub() {}
- void addProperty(const String8& key, const String8 value) {
- mFuzzConfig.addProperty(key, value);
- }
- void addEvents(std::shared_ptr<FuzzedDataProvider> fdp) {
- std::lock_guard<std::mutex> guard(mEventLock);
- mCount = fdp->ConsumeIntegralInRange<size_t>(0, 256);
-
- for (size_t i = 0; i < mCount; i++)
- mBuf[i] = {fdp->ConsumeIntegral<nsecs_t>(), fdp->ConsumeIntegral<int32_t>(),
- fdp->ConsumeIntegral<int32_t>(), fdp->ConsumeIntegral<int32_t>(),
- fdp->ConsumeIntegral<int32_t>()};
- }
- uint32_t getDeviceClasses(int32_t deviceId) const override {
- return fdp->ConsumeIntegral<uint32_t>();
- }
- InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override {
- return mIdentifier;
- }
- int32_t getDeviceControllerNumber(int32_t deviceId) const override {
- return fdp->ConsumeIntegral<int32_t>();
- }
- void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override {
- *outConfiguration = mFuzzConfig;
- }
- status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
- RawAbsoluteAxisInfo* outAxisInfo) const override {
- return fdp->ConsumeIntegral<status_t>();
- }
- bool hasRelativeAxis(int32_t deviceId, int axis) const override { return fdp->ConsumeBool(); }
- bool hasInputProperty(int32_t deviceId, int property) const override {
- return fdp->ConsumeBool();
- }
- status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
- int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const override {
- return fdp->ConsumeIntegral<status_t>();
- }
- status_t mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxisInfo) const override {
- return fdp->ConsumeIntegral<status_t>();
- }
- void setExcludedDevices(const std::vector<std::string>& devices) override {}
- size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) override {
- std::lock_guard<std::mutex> guard(mEventLock);
- for (size_t i = 0; i < mCount; i++) buffer[i] = mBuf[i];
-
- return mCount;
- }
- std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override { return mVideoFrames; }
- int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override {
- return fdp->ConsumeIntegral<int32_t>();
- }
- int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override {
- return fdp->ConsumeIntegral<int32_t>();
- }
- int32_t getSwitchState(int32_t deviceId, int32_t sw) const override {
- return fdp->ConsumeIntegral<int32_t>();
- }
- status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
- int32_t* outValue) const override {
- return fdp->ConsumeIntegral<status_t>();
- }
- bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
- uint8_t* outFlags) const override {
- return fdp->ConsumeBool();
- }
- bool hasScanCode(int32_t deviceId, int32_t scanCode) const override {
- return fdp->ConsumeBool();
- }
- bool hasLed(int32_t deviceId, int32_t led) const override { return fdp->ConsumeBool(); }
- void setLedState(int32_t deviceId, int32_t led, bool on) override {}
- void getVirtualKeyDefinitions(
- int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const override {}
- sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const override { return nullptr; }
- bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) override {
- return fdp->ConsumeBool();
- }
- void vibrate(int32_t deviceId, nsecs_t duration) override {}
- void cancelVibrate(int32_t deviceId) override {}
- void requestReopenDevices() override {}
- void wake() override {}
- void dump(std::string& dump) override {}
- void monitor() override {}
- bool isDeviceEnabled(int32_t deviceId) override { return fdp->ConsumeBool(); }
- status_t enableDevice(int32_t deviceId) override { return fdp->ConsumeIntegral<status_t>(); }
- status_t disableDevice(int32_t deviceId) override { return fdp->ConsumeIntegral<status_t>(); }
-};
-
-class FuzzPointerController : public PointerControllerInterface {
- std::shared_ptr<FuzzedDataProvider> fdp;
-
-public:
- FuzzPointerController(std::shared_ptr<FuzzedDataProvider> fdp) : fdp(fdp) {}
- ~FuzzPointerController() {}
- bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const override {
- return fdp->ConsumeBool();
- }
- void move(float deltaX, float deltaY) override {}
- void setButtonState(int32_t buttonState) override {}
- int32_t getButtonState() const override { return fdp->ConsumeIntegral<int32_t>(); }
- void setPosition(float x, float y) override {}
- void getPosition(float* outX, float* outY) const override {}
- void fade(Transition transition) override {}
- void unfade(Transition transition) override {}
- void setPresentation(Presentation presentation) override {}
- void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
- BitSet32 spotIdBits, int32_t displayId) override {}
- void clearSpots() override {}
- int32_t getDisplayId() const override { return fdp->ConsumeIntegral<int32_t>(); }
- void setDisplayViewport(const DisplayViewport& displayViewport) override {}
-};
-
-class FuzzInputReaderPolicy : public InputReaderPolicyInterface {
- TouchAffineTransformation mTransform;
- std::shared_ptr<FuzzPointerController> mPointerController;
- std::shared_ptr<FuzzedDataProvider> fdp;
-
-protected:
- ~FuzzInputReaderPolicy() {}
-
-public:
- FuzzInputReaderPolicy(std::shared_ptr<FuzzedDataProvider> fdp) : fdp(fdp) {
- mPointerController = std::make_shared<FuzzPointerController>(fdp);
- }
- void getReaderConfiguration(InputReaderConfiguration* outConfig) override {}
- std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId) override {
- return mPointerController;
- }
- void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override {}
- sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier& identifier) override {
- return nullptr;
- }
- std::string getDeviceAlias(const InputDeviceIdentifier& identifier) {
- return fdp->ConsumeRandomLengthString(32);
- }
- TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor,
- int32_t surfaceRotation) override {
- return mTransform;
- }
- void setTouchAffineTransformation(const TouchAffineTransformation t) { mTransform = t; }
-};
-
-class FuzzInputListener : public virtual InputListenerInterface {
-protected:
- ~FuzzInputListener() {}
-
-public:
- FuzzInputListener() {}
- void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override {}
- void notifyKey(const NotifyKeyArgs* args) override {}
- void notifyMotion(const NotifyMotionArgs* args) override {}
- void notifySwitch(const NotifySwitchArgs* args) override {}
- void notifyDeviceReset(const NotifyDeviceResetArgs* args) override {}
-};
-
-class FuzzInputReaderContext : public InputReaderContext {
- std::shared_ptr<EventHubInterface> mEventHub;
- sp<InputReaderPolicyInterface> mPolicy;
- sp<InputListenerInterface> mListener;
- std::shared_ptr<FuzzedDataProvider> fdp;
-
-public:
- FuzzInputReaderContext(std::shared_ptr<EventHubInterface> eventHub,
- const sp<InputReaderPolicyInterface>& policy,
- const sp<InputListenerInterface>& listener,
- std::shared_ptr<FuzzedDataProvider> fdp)
- : mEventHub(eventHub), mPolicy(policy), mListener(listener), fdp(fdp) {}
- ~FuzzInputReaderContext() {}
- void updateGlobalMetaState() override {}
- int32_t getGlobalMetaState() { return fdp->ConsumeIntegral<int32_t>(); }
- void disableVirtualKeysUntil(nsecs_t time) override {}
- bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode) override {
- return fdp->ConsumeBool();
- }
- void fadePointer() override {}
- std::shared_ptr<PointerControllerInterface> getPointerController(int32_t deviceId) override {
- return mPolicy->obtainPointerController(0);
- }
- void requestTimeoutAtTime(nsecs_t when) override {}
- int32_t bumpGeneration() override { return fdp->ConsumeIntegral<int32_t>(); }
- void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override {}
- void dispatchExternalStylusState(const StylusState& outState) override {}
- InputReaderPolicyInterface* getPolicy() override { return mPolicy.get(); }
- InputListenerInterface* getListener() override { return mListener.get(); }
- EventHubInterface* getEventHub() override { return mEventHub.get(); }
- int32_t getNextId() override { return fdp->ConsumeIntegral<int32_t>(); }
-};
-
-} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
deleted file mode 100644
index 336ecb6..0000000
--- a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright 2020 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 <FuzzContainer.h>
-#include <MultiTouchInputMapper.h>
-#include <fuzzer/FuzzedDataProvider.h>
-
-namespace android {
-
-void addProperty(FuzzContainer& fuzzer, std::shared_ptr<FuzzedDataProvider> fdp) {
- // Pick a random property to set for the mapper to have set.
- fdp->PickValueInArray<std::function<void()>>(
- {[&]() -> void {
- fuzzer.addProperty(String8("touch.deviceType"), String8("touchScreen"));
- },
- [&]() -> void {
- fuzzer.addProperty(String8("touch.deviceType"),
- String8(fdp->ConsumeRandomLengthString(8).data()));
- },
- [&]() -> void {
- fuzzer.addProperty(String8("touch.size.scale"),
- String8(fdp->ConsumeRandomLengthString(8).data()));
- },
- [&]() -> void {
- fuzzer.addProperty(String8("touch.size.bias"),
- String8(fdp->ConsumeRandomLengthString(8).data()));
- },
- [&]() -> void {
- fuzzer.addProperty(String8("touch.size.isSummed"),
- String8(fdp->ConsumeRandomLengthString(8).data()));
- },
- [&]() -> void {
- fuzzer.addProperty(String8("touch.size.calibration"),
- String8(fdp->ConsumeRandomLengthString(8).data()));
- },
- [&]() -> void {
- fuzzer.addProperty(String8("touch.pressure.scale"),
- String8(fdp->ConsumeRandomLengthString(8).data()));
- },
- [&]() -> void {
- fuzzer.addProperty(String8("touch.size.calibration"),
- fdp->ConsumeBool() ? String8("diameter") : String8("area"));
- },
- [&]() -> void {
- fuzzer.addProperty(String8("touch.pressure.calibration"),
- String8(fdp->ConsumeRandomLengthString(8).data()));
- }})();
-}
-
-extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
- std::shared_ptr<FuzzedDataProvider> fdp = std::make_shared<FuzzedDataProvider>(data, size);
- FuzzContainer fuzzer = FuzzContainer(fdp);
-
- MultiTouchInputMapper& mapper = fuzzer.getMapper<MultiTouchInputMapper>();
- auto policyConfig = fuzzer.getPolicyConfig();
-
- // Loop through mapper operations until randomness is exhausted.
- while (fdp->remaining_bytes() > 0) {
- fdp->PickValueInArray<std::function<void()>>({
- [&]() -> void { addProperty(fuzzer, fdp); },
- [&]() -> void {
- std::string dump;
- mapper.dump(dump);
- },
- [&]() -> void {
- InputDeviceInfo info;
- mapper.populateDeviceInfo(&info);
- },
- [&]() -> void { mapper.getSources(); },
- [&]() -> void {
- mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig,
- fdp->ConsumeIntegral<uint32_t>());
- },
- [&]() -> void { mapper.reset(fdp->ConsumeIntegral<nsecs_t>()); },
- [&]() -> void {
- RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(),
- fdp->ConsumeIntegral<int32_t>(),
- fdp->ConsumeIntegral<int32_t>(),
- fdp->ConsumeIntegral<int32_t>(),
- fdp->ConsumeIntegral<int32_t>()};
- mapper.process(&rawEvent);
- },
- [&]() -> void {
- mapper.getKeyCodeState(fdp->ConsumeIntegral<uint32_t>(),
- fdp->ConsumeIntegral<int32_t>());
- },
- [&]() -> void {
- mapper.getScanCodeState(fdp->ConsumeIntegral<uint32_t>(),
- fdp->ConsumeIntegral<int32_t>());
- },
- [&]() -> void {
- mapper.markSupportedKeyCodes(fdp->ConsumeIntegral<uint32_t>(),
- fdp->ConsumeIntegral<size_t>(), nullptr, nullptr);
- },
- [&]() -> void { mapper.cancelTouch(fdp->ConsumeIntegral<nsecs_t>()); },
- [&]() -> void { mapper.timeoutExpired(fdp->ConsumeIntegral<nsecs_t>()); },
- [&]() -> void {
- StylusState state{fdp->ConsumeIntegral<nsecs_t>(),
- fdp->ConsumeFloatingPoint<float>(),
- fdp->ConsumeIntegral<uint32_t>(),
- fdp->ConsumeIntegral<int32_t>()};
- mapper.updateExternalStylusState(state);
- },
- [&]() -> void { mapper.getAssociatedDisplayId(); },
- })();
- }
-
- return 0;
-}
-
-} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
deleted file mode 100644
index ae7429c..0000000
--- a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2020 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 <FuzzContainer.h>
-#include <SwitchInputMapper.h>
-#include <fuzzer/FuzzedDataProvider.h>
-
-namespace android {
-
-extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
- std::shared_ptr<FuzzedDataProvider> fdp = std::make_shared<FuzzedDataProvider>(data, size);
- FuzzContainer fuzzer = FuzzContainer(fdp);
-
- SwitchInputMapper& mapper = fuzzer.getMapper<SwitchInputMapper>();
- auto policyConfig = fuzzer.getPolicyConfig();
-
- // Loop through mapper operations until randomness is exhausted.
- while (fdp->remaining_bytes() > 0) {
- fdp->PickValueInArray<std::function<void()>>({
- [&]() -> void {
- std::string dump;
- mapper.dump(dump);
- },
- [&]() -> void { mapper.getSources(); },
- [&]() -> void {
- RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(),
- fdp->ConsumeIntegral<int32_t>(),
- fdp->ConsumeIntegral<int32_t>(),
- fdp->ConsumeIntegral<int32_t>(),
- fdp->ConsumeIntegral<int32_t>()};
- mapper.process(&rawEvent);
- },
- [&]() -> void {
- mapper.getSwitchState(fdp->ConsumeIntegral<uint32_t>(),
- fdp->ConsumeIntegral<int32_t>());
- },
- })();
- }
-
- return 0;
-}
-
-} // namespace android
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
index 19b6a0f..d828aa9 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -11,9 +11,15 @@
name: "libpowermanager",
srcs: [
- "IPowerManager.cpp",
- "Temperature.cpp",
+ "BatterySaverPolicyConfig.cpp",
"CoolingDevice.cpp",
+ "ParcelDuration.cpp",
+ "PowerHalController.cpp",
+ "PowerHalLoader.cpp",
+ "PowerHalWrapper.cpp",
+ "PowerSaveState.cpp",
+ "Temperature.cpp",
+ "WorkSource.cpp",
":libpowermanager_aidl",
],
@@ -26,9 +32,13 @@
},
shared_libs: [
- "libutils",
"libbinder",
- "liblog"
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ "android.hardware.power@1.0",
+ "android.hardware.power@1.1",
+ "android.hardware.power-V2-cpp",
],
cflags: [
@@ -43,22 +53,3 @@
"include",
],
}
-
-cc_test {
- name: "thermalmanager-test",
- srcs: ["IThermalManagerTest.cpp",
- ],
- cflags: [
- "-Wall",
- "-Werror",
- "-Wextra",
- ],
- shared_libs: [
- "libbase",
- "libhidlbase",
- "liblog",
- "libpowermanager",
- "libbinder",
- "libutils",
- ],
-}
diff --git a/services/powermanager/BatterySaverPolicyConfig.cpp b/services/powermanager/BatterySaverPolicyConfig.cpp
new file mode 100644
index 0000000..49557bc
--- /dev/null
+++ b/services/powermanager/BatterySaverPolicyConfig.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "BatterySaverPolicyConfig"
+
+#include <android/BatterySaverPolicyConfig.h>
+#include <binder/Parcel.h>
+#include <utils/Log.h>
+
+namespace android::os {
+
+status_t BatterySaverPolicyConfig::readDeviceSpecificSettings(const android::Parcel *parcel) {
+ int32_t num = 0;
+ status_t ret = parcel->readInt32(&num);
+ if (ret != OK) {
+ return ret;
+ }
+ for (int i = 0; i < num; i++) {
+ String16 key, val;
+ ret = parcel->readString16(&key) ?:
+ parcel->readString16(&val);
+ if (ret != OK) {
+ return ret;
+ }
+ mDeviceSpecificSettings.emplace_back(key, val);
+ }
+ return ret;
+}
+
+status_t BatterySaverPolicyConfig::readFromParcel(const android::Parcel *parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ return parcel->readFloat(&mAdjustBrightnessFactor)
+ ?: parcel->readBool(&mAdvertiseIsEnabled)
+ ?: parcel->readBool(&mDeferFullBackup)
+ ?: parcel->readBool(&mDeferKeyValueBackup)
+ ?: readDeviceSpecificSettings(parcel)
+ ?: parcel->readBool(&mDisableAnimation)
+ ?: parcel->readBool(&mDisableAod)
+ ?: parcel->readBool(&mDisableLaunchBoost)
+ ?: parcel->readBool(&mDisableOptionalSensors)
+ ?: parcel->readBool(&mDisableVibration)
+ ?: parcel->readBool(&mEnableAdjustBrightness)
+ ?: parcel->readBool(&mEnableDataSaver)
+ ?: parcel->readBool(&mEnableFirewall)
+ ?: parcel->readBool(&mEnableNightMode)
+ ?: parcel->readBool(&mEnableQuickDoze)
+ ?: parcel->readBool(&mForceAllAppsStandby)
+ ?: parcel->readBool(&mForceBackgroundCheck)
+ ?: parcel->readInt32(reinterpret_cast<int32_t *>(&mLocationMode))
+ ?: parcel->readInt32(reinterpret_cast<int32_t *>(&mSoundTriggerMode));
+}
+
+status_t BatterySaverPolicyConfig::writeDeviceSpecificSettings(android::Parcel *parcel) const {
+ status_t ret = parcel->writeInt32(mDeviceSpecificSettings.size());
+ if (ret != OK) {
+ return ret;
+ }
+ for (auto& settings : mDeviceSpecificSettings) {
+ ret = parcel->writeString16(settings.first) ?:
+ parcel->writeString16(settings.second);
+ if (ret != OK) {
+ return ret;
+ }
+ }
+ return ret;
+}
+
+status_t BatterySaverPolicyConfig::writeToParcel(android::Parcel *parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ return parcel->writeFloat(mAdjustBrightnessFactor)
+ ?: parcel->writeBool(mAdvertiseIsEnabled)
+ ?: parcel->writeBool(mDeferFullBackup)
+ ?: parcel->writeBool(mDeferKeyValueBackup)
+ ?: writeDeviceSpecificSettings(parcel)
+ ?: parcel->writeBool(mDisableAnimation)
+ ?: parcel->writeBool(mDisableAod)
+ ?: parcel->writeBool(mDisableLaunchBoost)
+ ?: parcel->writeBool(mDisableOptionalSensors)
+ ?: parcel->writeBool(mDisableVibration)
+ ?: parcel->writeBool(mEnableAdjustBrightness)
+ ?: parcel->writeBool(mEnableDataSaver)
+ ?: parcel->writeBool(mEnableFirewall)
+ ?: parcel->writeBool(mEnableNightMode)
+ ?: parcel->writeBool(mEnableQuickDoze)
+ ?: parcel->writeBool(mForceAllAppsStandby)
+ ?: parcel->writeBool(mForceBackgroundCheck)
+ ?: parcel->writeInt32(static_cast<int32_t>(mLocationMode))
+ ?: parcel->writeInt32(static_cast<int32_t>(mSoundTriggerMode));
+}
+
+} // namespace android::os
diff --git a/services/powermanager/IPowerManager.cpp b/services/powermanager/IPowerManager.cpp
deleted file mode 100644
index ea3a831..0000000
--- a/services/powermanager/IPowerManager.cpp
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-#define LOG_TAG "IPowerManager"
-//#define LOG_NDEBUG 0
-#include <utils/Log.h>
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/Parcel.h>
-
-#include <powermanager/IPowerManager.h>
-
-namespace android {
-
-class BpPowerManager : public BpInterface<IPowerManager>
-{
-public:
- explicit BpPowerManager(const sp<IBinder>& impl)
- : BpInterface<IPowerManager>(impl)
- {
- }
-
- virtual status_t acquireWakeLock(int flags, const sp<IBinder>& lock, const String16& tag,
- const String16& packageName, bool isOneWay)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
-
- data.writeStrongBinder(lock);
- data.writeInt32(flags);
- data.writeString16(tag);
- data.writeString16(packageName);
- data.writeInt32(0); // no WorkSource
- data.writeString16(NULL, 0); // no history tag
- return remote()->transact(ACQUIRE_WAKE_LOCK, data, &reply,
- isOneWay ? IBinder::FLAG_ONEWAY : 0);
- }
-
- virtual status_t acquireWakeLockWithUid(int flags, const sp<IBinder>& lock, const String16& tag,
- const String16& packageName, int uid, bool isOneWay)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
-
- data.writeStrongBinder(lock);
- data.writeInt32(flags);
- data.writeString16(tag);
- data.writeString16(packageName);
- data.writeInt32(uid); // uid to blame for the work
- return remote()->transact(ACQUIRE_WAKE_LOCK_UID, data, &reply,
- isOneWay ? IBinder::FLAG_ONEWAY : 0);
- }
-
- virtual status_t releaseWakeLock(const sp<IBinder>& lock, int flags, bool isOneWay)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
- data.writeStrongBinder(lock);
- data.writeInt32(flags);
- return remote()->transact(RELEASE_WAKE_LOCK, data, &reply,
- isOneWay ? IBinder::FLAG_ONEWAY : 0);
- }
-
- virtual status_t updateWakeLockUids(const sp<IBinder>& lock, int len, const int *uids,
- bool isOneWay) {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
- data.writeStrongBinder(lock);
- data.writeInt32Array(len, uids);
- return remote()->transact(UPDATE_WAKE_LOCK_UIDS, data, &reply,
- isOneWay ? IBinder::FLAG_ONEWAY : 0);
- }
-
- virtual status_t powerHint(int hintId, int param)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
- data.writeInt32(hintId);
- data.writeInt32(param);
- // This FLAG_ONEWAY is in the .aidl, so there is no way to disable it
- return remote()->transact(POWER_HINT, data, &reply, IBinder::FLAG_ONEWAY);
- }
-
- virtual status_t goToSleep(int64_t event_time_ms, int reason, int flags)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
- data.writeInt64(event_time_ms);
- data.writeInt32(reason);
- data.writeInt32(flags);
- return remote()->transact(GO_TO_SLEEP, data, &reply, 0);
- }
-
- virtual status_t reboot(bool confirm, const String16& reason, bool wait)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
- data.writeInt32(confirm);
- data.writeString16(reason);
- data.writeInt32(wait);
- return remote()->transact(REBOOT, data, &reply, 0);
- }
-
- virtual status_t shutdown(bool confirm, const String16& reason, bool wait)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
- data.writeInt32(confirm);
- data.writeString16(reason);
- data.writeInt32(wait);
- return remote()->transact(SHUTDOWN, data, &reply, 0);
- }
-
- virtual status_t crash(const String16& message)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
- data.writeString16(message);
- return remote()->transact(CRASH, data, &reply, 0);
- }
-};
-
-IMPLEMENT_META_INTERFACE(PowerManager, "android.os.IPowerManager");
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
diff --git a/services/powermanager/ParcelDuration.cpp b/services/powermanager/ParcelDuration.cpp
new file mode 100644
index 0000000..c0ab380
--- /dev/null
+++ b/services/powermanager/ParcelDuration.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "ParcelDuration"
+
+#include <android/ParcelDuration.h>
+#include <binder/Parcel.h>
+#include <utils/Log.h>
+
+namespace android::os {
+
+status_t ParcelDuration::readFromParcel(const android::Parcel *parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ return parcel->readInt64(&mSeconds) ?: parcel->readInt32(&mNanos);
+}
+
+status_t ParcelDuration::writeToParcel(android::Parcel *parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ return parcel->writeInt64(mSeconds) ?: parcel->writeInt32(mNanos);
+}
+
+} // namespace android::os
diff --git a/services/powermanager/PowerHalController.cpp b/services/powermanager/PowerHalController.cpp
new file mode 100644
index 0000000..8c225d5
--- /dev/null
+++ b/services/powermanager/PowerHalController.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "PowerHalController"
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/IPowerHintSession.h>
+#include <android/hardware/power/Mode.h>
+#include <powermanager/PowerHalController.h>
+#include <powermanager/PowerHalLoader.h>
+#include <utils/Log.h>
+
+using namespace android::hardware::power;
+
+namespace android {
+
+namespace power {
+
+// -------------------------------------------------------------------------------------------------
+
+std::unique_ptr<HalWrapper> HalConnector::connect() {
+ sp<IPower> halAidl = PowerHalLoader::loadAidl();
+ if (halAidl) {
+ return std::make_unique<AidlHalWrapper>(halAidl);
+ }
+ sp<V1_0::IPower> halHidlV1_0 = PowerHalLoader::loadHidlV1_0();
+ sp<V1_1::IPower> halHidlV1_1 = PowerHalLoader::loadHidlV1_1();
+ if (halHidlV1_1) {
+ return std::make_unique<HidlHalWrapperV1_1>(halHidlV1_0, halHidlV1_1);
+ }
+ if (halHidlV1_0) {
+ return std::make_unique<HidlHalWrapperV1_0>(halHidlV1_0);
+ }
+ return nullptr;
+}
+
+void HalConnector::reset() {
+ PowerHalLoader::unloadAll();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void PowerHalController::init() {
+ initHal();
+}
+
+// Check validity of current handle to the power HAL service, and create a new
+// one if necessary.
+std::shared_ptr<HalWrapper> PowerHalController::initHal() {
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ if (mConnectedHal == nullptr) {
+ mConnectedHal = mHalConnector->connect();
+ if (mConnectedHal == nullptr) {
+ // Unable to connect to Power HAL service. Fallback to default.
+ return mDefaultHal;
+ }
+ }
+ return mConnectedHal;
+}
+
+// Check if a call to Power HAL function failed; if so, log the failure and
+// invalidate the current Power HAL handle.
+template <typename T>
+HalResult<T> PowerHalController::processHalResult(HalResult<T> result, const char* fnName) {
+ if (result.isFailed()) {
+ ALOGE("%s failed: %s", fnName, result.errorMessage());
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ // Drop Power HAL handle. This will force future api calls to reconnect.
+ mConnectedHal = nullptr;
+ mHalConnector->reset();
+ }
+ return result;
+}
+
+HalResult<void> PowerHalController::setBoost(Boost boost, int32_t durationMs) {
+ std::shared_ptr<HalWrapper> handle = initHal();
+ auto result = handle->setBoost(boost, durationMs);
+ return processHalResult(result, "setBoost");
+}
+
+HalResult<void> PowerHalController::setMode(Mode mode, bool enabled) {
+ std::shared_ptr<HalWrapper> handle = initHal();
+ auto result = handle->setMode(mode, enabled);
+ return processHalResult(result, "setMode");
+}
+
+HalResult<sp<IPowerHintSession>> PowerHalController::createHintSession(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) {
+ std::shared_ptr<HalWrapper> handle = initHal();
+ auto result = handle->createHintSession(tgid, uid, threadIds, durationNanos);
+ return processHalResult(result, "createHintSession");
+}
+
+HalResult<int64_t> PowerHalController::getHintSessionPreferredRate() {
+ std::shared_ptr<HalWrapper> handle = initHal();
+ auto result = handle->getHintSessionPreferredRate();
+ return processHalResult(result, "getHintSessionPreferredRate");
+}
+
+} // namespace power
+
+} // namespace android
diff --git a/services/powermanager/PowerHalLoader.cpp b/services/powermanager/PowerHalLoader.cpp
new file mode 100644
index 0000000..1f1b43a
--- /dev/null
+++ b/services/powermanager/PowerHalLoader.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "PowerHalLoader"
+
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/IPower.h>
+#include <binder/IServiceManager.h>
+#include <hardware/power.h>
+#include <hardware_legacy/power.h>
+#include <powermanager/PowerHalLoader.h>
+
+using namespace android::hardware::power;
+
+namespace android {
+
+namespace power {
+
+// -------------------------------------------------------------------------------------------------
+
+template <typename T, typename F>
+sp<T> loadHal(bool& exists, sp<T>& hal, F& loadFn, const char* halName) {
+ if (!exists) {
+ return nullptr;
+ }
+ if (hal) {
+ return hal;
+ }
+ hal = loadFn();
+ if (hal) {
+ ALOGV("Successfully connected to Power HAL %s service.", halName);
+ } else {
+ ALOGV("Power HAL %s service not available.", halName);
+ exists = false;
+ }
+ return hal;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+std::mutex PowerHalLoader::gHalMutex;
+sp<IPower> PowerHalLoader::gHalAidl = nullptr;
+sp<V1_0::IPower> PowerHalLoader::gHalHidlV1_0 = nullptr;
+sp<V1_1::IPower> PowerHalLoader::gHalHidlV1_1 = nullptr;
+
+void PowerHalLoader::unloadAll() {
+ std::lock_guard<std::mutex> lock(gHalMutex);
+ gHalAidl = nullptr;
+ gHalHidlV1_0 = nullptr;
+ gHalHidlV1_1 = nullptr;
+}
+
+sp<IPower> PowerHalLoader::loadAidl() {
+ std::lock_guard<std::mutex> lock(gHalMutex);
+ static bool gHalExists = true;
+ static auto loadFn = []() { return waitForVintfService<IPower>(); };
+ return loadHal<IPower>(gHalExists, gHalAidl, loadFn, "AIDL");
+}
+
+sp<V1_0::IPower> PowerHalLoader::loadHidlV1_0() {
+ std::lock_guard<std::mutex> lock(gHalMutex);
+ return loadHidlV1_0Locked();
+}
+
+sp<V1_1::IPower> PowerHalLoader::loadHidlV1_1() {
+ std::lock_guard<std::mutex> lock(gHalMutex);
+ static bool gHalExists = true;
+ static auto loadFn = []() { return V1_1::IPower::castFrom(loadHidlV1_0Locked()); };
+ return loadHal<V1_1::IPower>(gHalExists, gHalHidlV1_1, loadFn, "HIDL v1.1");
+}
+
+sp<V1_0::IPower> PowerHalLoader::loadHidlV1_0Locked() {
+ static bool gHalExists = true;
+ static auto loadFn = []() { return V1_0::IPower::getService(); };
+ return loadHal<V1_0::IPower>(gHalExists, gHalHidlV1_0, loadFn, "HIDL v1.0");
+}
+
+// -------------------------------------------------------------------------------------------------
+
+} // namespace power
+
+} // namespace android
diff --git a/services/powermanager/PowerHalWrapper.cpp b/services/powermanager/PowerHalWrapper.cpp
new file mode 100644
index 0000000..d74bd23
--- /dev/null
+++ b/services/powermanager/PowerHalWrapper.cpp
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "HalWrapper"
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPowerHintSession.h>
+#include <android/hardware/power/Mode.h>
+#include <powermanager/PowerHalWrapper.h>
+#include <utils/Log.h>
+
+#include <cinttypes>
+
+using namespace android::hardware::power;
+namespace V1_0 = android::hardware::power::V1_0;
+namespace V1_1 = android::hardware::power::V1_1;
+namespace Aidl = android::hardware::power;
+
+namespace android {
+
+namespace power {
+
+// -------------------------------------------------------------------------------------------------
+
+inline HalResult<void> toHalResult(const binder::Status& result) {
+ if (result.isOk()) {
+ return HalResult<void>::ok();
+ }
+ ALOGE("Power HAL request failed: %s", result.toString8().c_str());
+ return HalResult<void>::fromStatus(result);
+}
+
+template <typename T>
+template <typename R>
+HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, T data) {
+ return ret.isOk() ? HalResult<T>::ok(data) : HalResult<T>::failed(ret.description());
+}
+
+template <typename T>
+template <typename R>
+HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, V1_0::Status status, T data) {
+ return ret.isOk() ? HalResult<T>::fromStatus(status, data)
+ : HalResult<T>::failed(ret.description());
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<void> HalResult<void>::fromStatus(status_t status) {
+ if (status == android::OK) {
+ return HalResult<void>::ok();
+ }
+ return HalResult<void>::failed(statusToString(status));
+}
+
+HalResult<void> HalResult<void>::fromStatus(binder::Status status) {
+ if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+ return HalResult<void>::unsupported();
+ }
+ if (status.isOk()) {
+ return HalResult<void>::ok();
+ }
+ return HalResult<void>::failed(std::string(status.toString8().c_str()));
+}
+
+template <typename R>
+HalResult<void> HalResult<void>::fromReturn(hardware::Return<R>& ret) {
+ return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed(ret.description());
+}
+// -------------------------------------------------------------------------------------------------
+
+HalResult<void> EmptyHalWrapper::setBoost(Boost boost, int32_t durationMs) {
+ ALOGV("Skipped setBoost %s with duration %dms because Power HAL not available",
+ toString(boost).c_str(), durationMs);
+ return HalResult<void>::unsupported();
+}
+
+HalResult<void> EmptyHalWrapper::setMode(Mode mode, bool enabled) {
+ ALOGV("Skipped setMode %s to %s because Power HAL not available", toString(mode).c_str(),
+ enabled ? "true" : "false");
+ return HalResult<void>::unsupported();
+}
+
+HalResult<sp<Aidl::IPowerHintSession>> EmptyHalWrapper::createHintSession(
+ int32_t, int32_t, const std::vector<int32_t>& threadIds, int64_t) {
+ ALOGV("Skipped createHintSession(task num=%zu) because Power HAL not available",
+ threadIds.size());
+ return HalResult<sp<Aidl::IPowerHintSession>>::unsupported();
+}
+
+HalResult<int64_t> EmptyHalWrapper::getHintSessionPreferredRate() {
+ ALOGV("Skipped getHintSessionPreferredRate because Power HAL not available");
+ return HalResult<int64_t>::unsupported();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<void> HidlHalWrapperV1_0::setBoost(Boost boost, int32_t durationMs) {
+ if (boost == Boost::INTERACTION) {
+ return sendPowerHint(V1_0::PowerHint::INTERACTION, durationMs);
+ } else {
+ ALOGV("Skipped setBoost %s because Power HAL AIDL not available", toString(boost).c_str());
+ return HalResult<void>::unsupported();
+ }
+}
+
+HalResult<void> HidlHalWrapperV1_0::setMode(Mode mode, bool enabled) {
+ uint32_t data = enabled ? 1 : 0;
+ switch (mode) {
+ case Mode::LAUNCH:
+ return sendPowerHint(V1_0::PowerHint::LAUNCH, data);
+ case Mode::LOW_POWER:
+ return sendPowerHint(V1_0::PowerHint::LOW_POWER, data);
+ case Mode::SUSTAINED_PERFORMANCE:
+ return sendPowerHint(V1_0::PowerHint::SUSTAINED_PERFORMANCE, data);
+ case Mode::VR:
+ return sendPowerHint(V1_0::PowerHint::VR_MODE, data);
+ case Mode::INTERACTIVE:
+ return setInteractive(enabled);
+ case Mode::DOUBLE_TAP_TO_WAKE:
+ return setFeature(V1_0::Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE, enabled);
+ default:
+ ALOGV("Skipped setMode %s because Power HAL AIDL not available",
+ toString(mode).c_str());
+ return HalResult<void>::unsupported();
+ }
+}
+
+HalResult<void> HidlHalWrapperV1_0::sendPowerHint(V1_0::PowerHint hintId, uint32_t data) {
+ auto ret = mHandleV1_0->powerHint(hintId, data);
+ return HalResult<void>::fromReturn(ret);
+}
+
+HalResult<void> HidlHalWrapperV1_0::setInteractive(bool enabled) {
+ auto ret = mHandleV1_0->setInteractive(enabled);
+ return HalResult<void>::fromReturn(ret);
+}
+
+HalResult<void> HidlHalWrapperV1_0::setFeature(V1_0::Feature feature, bool enabled) {
+ auto ret = mHandleV1_0->setFeature(feature, enabled);
+ return HalResult<void>::fromReturn(ret);
+}
+
+HalResult<sp<Aidl::IPowerHintSession>> HidlHalWrapperV1_0::createHintSession(
+ int32_t, int32_t, const std::vector<int32_t>& threadIds, int64_t) {
+ ALOGV("Skipped createHintSession(task num=%zu) because Power HAL not available",
+ threadIds.size());
+ return HalResult<sp<Aidl::IPowerHintSession>>::unsupported();
+}
+
+HalResult<int64_t> HidlHalWrapperV1_0::getHintSessionPreferredRate() {
+ ALOGV("Skipped getHintSessionPreferredRate because Power HAL not available");
+ return HalResult<int64_t>::unsupported();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<void> HidlHalWrapperV1_1::sendPowerHint(V1_0::PowerHint hintId, uint32_t data) {
+ auto ret = mHandleV1_1->powerHintAsync(hintId, data);
+ return HalResult<void>::fromReturn(ret);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<void> AidlHalWrapper::setBoost(Boost boost, int32_t durationMs) {
+ std::unique_lock<std::mutex> lock(mBoostMutex);
+ size_t idx = static_cast<size_t>(boost);
+
+ // Quick return if boost is not supported by HAL
+ if (idx >= mBoostSupportedArray.size() || mBoostSupportedArray[idx] == HalSupport::OFF) {
+ ALOGV("Skipped setBoost %s because Power HAL doesn't support it", toString(boost).c_str());
+ return HalResult<void>::unsupported();
+ }
+
+ if (mBoostSupportedArray[idx] == HalSupport::UNKNOWN) {
+ bool isSupported = false;
+ auto isSupportedRet = mHandle->isBoostSupported(boost, &isSupported);
+ if (!isSupportedRet.isOk()) {
+ ALOGE("Skipped setBoost %s because check support failed with: %s",
+ toString(boost).c_str(), isSupportedRet.toString8().c_str());
+ // return HalResult::FAILED;
+ return HalResult<void>::fromStatus(isSupportedRet);
+ }
+
+ mBoostSupportedArray[idx] = isSupported ? HalSupport::ON : HalSupport::OFF;
+ if (!isSupported) {
+ ALOGV("Skipped setBoost %s because Power HAL doesn't support it",
+ toString(boost).c_str());
+ return HalResult<void>::unsupported();
+ }
+ }
+ lock.unlock();
+
+ return toHalResult(mHandle->setBoost(boost, durationMs));
+}
+
+HalResult<void> AidlHalWrapper::setMode(Mode mode, bool enabled) {
+ std::unique_lock<std::mutex> lock(mModeMutex);
+ size_t idx = static_cast<size_t>(mode);
+
+ // Quick return if mode is not supported by HAL
+ if (idx >= mModeSupportedArray.size() || mModeSupportedArray[idx] == HalSupport::OFF) {
+ ALOGV("Skipped setMode %s because Power HAL doesn't support it", toString(mode).c_str());
+ return HalResult<void>::unsupported();
+ }
+
+ if (mModeSupportedArray[idx] == HalSupport::UNKNOWN) {
+ bool isSupported = false;
+ auto isSupportedRet = mHandle->isModeSupported(mode, &isSupported);
+ if (!isSupportedRet.isOk()) {
+ return HalResult<void>::failed(isSupportedRet.toString8().c_str());
+ }
+
+ mModeSupportedArray[idx] = isSupported ? HalSupport::ON : HalSupport::OFF;
+ if (!isSupported) {
+ ALOGV("Skipped setMode %s because Power HAL doesn't support it",
+ toString(mode).c_str());
+ return HalResult<void>::unsupported();
+ }
+ }
+ lock.unlock();
+
+ return toHalResult(mHandle->setMode(mode, enabled));
+}
+
+HalResult<sp<Aidl::IPowerHintSession>> AidlHalWrapper::createHintSession(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) {
+ sp<IPowerHintSession> appSession;
+ return HalResult<sp<Aidl::IPowerHintSession>>::
+ fromStatus(mHandle->createHintSession(tgid, uid, threadIds, durationNanos, &appSession),
+ appSession);
+}
+
+HalResult<int64_t> AidlHalWrapper::getHintSessionPreferredRate() {
+ int64_t rate = -1;
+ auto result = mHandle->getHintSessionPreferredRate(&rate);
+ return HalResult<int64_t>::fromStatus(result, rate);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+} // namespace power
+
+} // namespace android
diff --git a/services/powermanager/PowerSaveState.cpp b/services/powermanager/PowerSaveState.cpp
new file mode 100644
index 0000000..d705e91
--- /dev/null
+++ b/services/powermanager/PowerSaveState.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "PowerSaveState"
+
+#include <android/PowerSaveState.h>
+#include <binder/Parcel.h>
+#include <utils/Log.h>
+
+namespace android::os {
+
+status_t PowerSaveState::readFromParcel(const android::Parcel *parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ return parcel->readBool(&mBatterySaverEnabled)
+ ?: parcel->readBool(&mGlobalBatterySaverEnabled)
+ ?: parcel->readInt32(reinterpret_cast<int32_t *>(&mLocationMode))
+ ?: parcel->readInt32(reinterpret_cast<int32_t *>(&mSoundTriggerMode))
+ ?: parcel->readFloat(&mBrightnessFactor);
+}
+
+status_t PowerSaveState::writeToParcel(android::Parcel *parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ return parcel->writeBool(mBatterySaverEnabled)
+ ?: parcel->writeBool(mGlobalBatterySaverEnabled)
+ ?: parcel->writeInt32(static_cast<int32_t>(mLocationMode))
+ ?: parcel->writeInt32(static_cast<int32_t>(mSoundTriggerMode))
+ ?: parcel->writeFloat(mBrightnessFactor);
+}
+
+} // namespace android::os
diff --git a/services/powermanager/TEST_MAPPING b/services/powermanager/TEST_MAPPING
new file mode 100644
index 0000000..caaec55
--- /dev/null
+++ b/services/powermanager/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libpowermanager_test"
+ }
+ ]
+}
diff --git a/services/powermanager/WorkSource.cpp b/services/powermanager/WorkSource.cpp
new file mode 100644
index 0000000..1006a06
--- /dev/null
+++ b/services/powermanager/WorkSource.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "WorkSource"
+
+#include <android/WorkSource.h>
+#include <binder/Parcel.h>
+#include <utils/Log.h>
+
+namespace android::os {
+
+status_t WorkSource::readFromParcel(const android::Parcel *parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+ int32_t num;
+ status_t ret = parcel->readInt32(&num)
+ ?: parcel->readInt32Vector(&mUids)
+ ?: parcel->readString16Vector(&mNames);
+
+ return ret;
+}
+
+status_t WorkSource::writeToParcel(android::Parcel *parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ return parcel->writeInt32(mUids.size())
+ ?: parcel->writeInt32Vector(mUids)
+ ?: parcel->writeString16Vector(mNames);
+}
+
+} // namespace android::os
diff --git a/services/powermanager/benchmarks/Android.bp b/services/powermanager/benchmarks/Android.bp
new file mode 100644
index 0000000..3997929
--- /dev/null
+++ b/services/powermanager/benchmarks/Android.bp
@@ -0,0 +1,51 @@
+// Copyright (C) 2020 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"],
+}
+
+cc_benchmark {
+ name: "libpowermanager_benchmarks",
+ srcs: [
+ "main.cpp",
+ "PowerHalAidlBenchmarks.cpp",
+ "PowerHalControllerBenchmarks.cpp",
+ "PowerHalHidlBenchmarks.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libhidlbase",
+ "liblog",
+ "libpowermanager",
+ "libutils",
+ "android.hardware.power@1.0",
+ "android.hardware.power@1.1",
+ "android.hardware.power-V2-cpp",
+ ],
+ static_libs: [
+ "libtestUtil",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+}
diff --git a/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
new file mode 100644
index 0000000..1100cad
--- /dev/null
+++ b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "PowerHalAidlBenchmarks"
+
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/IPowerHintSession.h>
+#include <android/hardware/power/Mode.h>
+#include <android/hardware/power/WorkDuration.h>
+#include <benchmark/benchmark.h>
+#include <binder/IServiceManager.h>
+#include <testUtil.h>
+#include <chrono>
+
+using android::hardware::power::Boost;
+using android::hardware::power::IPower;
+using android::hardware::power::IPowerHintSession;
+using android::hardware::power::Mode;
+using android::hardware::power::WorkDuration;
+using std::chrono::microseconds;
+
+using namespace android;
+using namespace std::chrono_literals;
+
+// Values from Boost.aidl and Mode.aidl.
+static constexpr int64_t FIRST_BOOST = static_cast<int64_t>(Boost::INTERACTION);
+static constexpr int64_t LAST_BOOST = static_cast<int64_t>(Boost::CAMERA_SHOT);
+static constexpr int64_t FIRST_MODE = static_cast<int64_t>(Mode::DOUBLE_TAP_TO_WAKE);
+static constexpr int64_t LAST_MODE = static_cast<int64_t>(Mode::CAMERA_STREAMING_HIGH);
+
+class DurationWrapper : public WorkDuration {
+public:
+ DurationWrapper(int64_t dur, int64_t time) {
+ durationNanos = dur;
+ timeStampNanos = time;
+ }
+};
+
+static const std::vector<WorkDuration> DURATIONS = {
+ DurationWrapper(1L, 1L),
+ DurationWrapper(1000L, 2L),
+ DurationWrapper(1000000L, 3L),
+ DurationWrapper(1000000000L, 4L),
+};
+
+// Delay between oneway method calls to avoid overflowing the binder buffers.
+static constexpr microseconds ONEWAY_API_DELAY = 100us;
+
+template <class R, class... Args0, class... Args1>
+static void runBenchmark(benchmark::State& state, microseconds delay, R (IPower::*fn)(Args0...),
+ Args1&&... args1) {
+ sp<IPower> hal = waitForVintfService<IPower>();
+
+ if (hal == nullptr) {
+ ALOGI("Power HAL not available, skipping test...");
+ return;
+ }
+
+ binder::Status ret = (*hal.*fn)(std::forward<Args1>(args1)...);
+ if (ret.exceptionCode() == binder::Status::Exception::EX_UNSUPPORTED_OPERATION) {
+ ALOGI("Power HAL does not support this operation, skipping test...");
+ return;
+ }
+
+ while (state.KeepRunning()) {
+ ret = (*hal.*fn)(std::forward<Args1>(args1)...);
+ state.PauseTiming();
+ if (!ret.isOk()) state.SkipWithError(ret.toString8().c_str());
+ if (delay > 0us) {
+ testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(delay).count());
+ }
+ state.ResumeTiming();
+ }
+}
+
+template <class R, class... Args0, class... Args1>
+static void runSessionBenchmark(benchmark::State& state, R (IPowerHintSession::*fn)(Args0...),
+ Args1&&... args1) {
+ sp<IPower> pwHal = waitForVintfService<IPower>();
+
+ if (pwHal == nullptr) {
+ ALOGI("Power HAL not available, skipping test...");
+ return;
+ }
+
+ // do not use tid from the benchmark process, use 1 for init
+ std::vector<int32_t> threadIds{1};
+ int64_t durationNanos = 16666666L;
+ sp<IPowerHintSession> hal;
+
+ auto status = pwHal->createHintSession(1, 0, threadIds, durationNanos, &hal);
+
+ if (hal == nullptr) {
+ ALOGI("Power HAL doesn't support session, skipping test...");
+ return;
+ }
+
+ binder::Status ret = (*hal.*fn)(std::forward<Args1>(args1)...);
+ if (ret.exceptionCode() == binder::Status::Exception::EX_UNSUPPORTED_OPERATION) {
+ ALOGI("Power HAL does not support this operation, skipping test...");
+ return;
+ }
+
+ while (state.KeepRunning()) {
+ ret = (*hal.*fn)(std::forward<Args1>(args1)...);
+ state.PauseTiming();
+ if (!ret.isOk()) state.SkipWithError(ret.toString8().c_str());
+ if (ONEWAY_API_DELAY > 0us) {
+ testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(ONEWAY_API_DELAY)
+ .count());
+ }
+ state.ResumeTiming();
+ }
+ hal->close();
+}
+
+static void BM_PowerHalAidlBenchmarks_isBoostSupported(benchmark::State& state) {
+ bool isSupported;
+ Boost boost = static_cast<Boost>(state.range(0));
+ runBenchmark(state, 0us, &IPower::isBoostSupported, boost, &isSupported);
+}
+
+static void BM_PowerHalAidlBenchmarks_isModeSupported(benchmark::State& state) {
+ bool isSupported;
+ Mode mode = static_cast<Mode>(state.range(0));
+ runBenchmark(state, 0us, &IPower::isModeSupported, mode, &isSupported);
+}
+
+static void BM_PowerHalAidlBenchmarks_setBoost(benchmark::State& state) {
+ Boost boost = static_cast<Boost>(state.range(0));
+ runBenchmark(state, ONEWAY_API_DELAY, &IPower::setBoost, boost, 1);
+}
+
+static void BM_PowerHalAidlBenchmarks_setMode(benchmark::State& state) {
+ Mode mode = static_cast<Mode>(state.range(0));
+ runBenchmark(state, ONEWAY_API_DELAY, &IPower::setMode, mode, false);
+}
+
+static void BM_PowerHalAidlBenchmarks_createHintSession(benchmark::State& state) {
+ std::vector<int32_t> threadIds{static_cast<int32_t>(state.range(0))};
+ int64_t durationNanos = 16666666L;
+ int32_t tgid = 999;
+ int32_t uid = 1001;
+ sp<IPowerHintSession> appSession;
+ sp<IPower> hal = waitForVintfService<IPower>();
+
+ if (hal == nullptr) {
+ ALOGI("Power HAL not available, skipping test...");
+ return;
+ }
+
+ binder::Status ret = hal->createHintSession(tgid, uid, threadIds, durationNanos, &appSession);
+ if (ret.exceptionCode() == binder::Status::Exception::EX_UNSUPPORTED_OPERATION) {
+ ALOGI("Power HAL does not support this operation, skipping test...");
+ return;
+ }
+
+ while (state.KeepRunning()) {
+ ret = hal->createHintSession(tgid, uid, threadIds, durationNanos, &appSession);
+ state.PauseTiming();
+ if (!ret.isOk()) state.SkipWithError(ret.toString8().c_str());
+ appSession->close();
+ state.ResumeTiming();
+ }
+}
+
+static void BM_PowerHalAidlBenchmarks_getHintSessionPreferredRate(benchmark::State& state) {
+ int64_t rate;
+ runBenchmark(state, 0us, &IPower::getHintSessionPreferredRate, &rate);
+}
+
+static void BM_PowerHalAidlBenchmarks_updateTargetWorkDuration(benchmark::State& state) {
+ int64_t duration = 1000;
+ runSessionBenchmark(state, &IPowerHintSession::updateTargetWorkDuration, duration);
+}
+
+static void BM_PowerHalAidlBenchmarks_reportActualWorkDuration(benchmark::State& state) {
+ runSessionBenchmark(state, &IPowerHintSession::reportActualWorkDuration, DURATIONS);
+}
+
+BENCHMARK(BM_PowerHalAidlBenchmarks_isBoostSupported)->DenseRange(FIRST_BOOST, LAST_BOOST, 1);
+BENCHMARK(BM_PowerHalAidlBenchmarks_isModeSupported)->DenseRange(FIRST_MODE, LAST_MODE, 1);
+BENCHMARK(BM_PowerHalAidlBenchmarks_setBoost)->DenseRange(FIRST_BOOST, LAST_BOOST, 1);
+BENCHMARK(BM_PowerHalAidlBenchmarks_setMode)->DenseRange(FIRST_MODE, LAST_MODE, 1);
+BENCHMARK(BM_PowerHalAidlBenchmarks_createHintSession)->Arg(1);
+BENCHMARK(BM_PowerHalAidlBenchmarks_getHintSessionPreferredRate);
+BENCHMARK(BM_PowerHalAidlBenchmarks_updateTargetWorkDuration);
+BENCHMARK(BM_PowerHalAidlBenchmarks_reportActualWorkDuration);
diff --git a/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp
new file mode 100644
index 0000000..f8abc7a
--- /dev/null
+++ b/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "PowerHalControllerBenchmarks"
+
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/Mode.h>
+#include <benchmark/benchmark.h>
+#include <powermanager/PowerHalController.h>
+#include <testUtil.h>
+#include <chrono>
+
+using android::hardware::power::Boost;
+using android::hardware::power::Mode;
+using android::power::HalResult;
+using android::power::PowerHalController;
+
+using namespace android;
+using namespace std::chrono_literals;
+
+// Values from Boost.aidl and Mode.aidl.
+static constexpr int64_t FIRST_BOOST = static_cast<int64_t>(Boost::INTERACTION);
+static constexpr int64_t LAST_BOOST = static_cast<int64_t>(Boost::CAMERA_SHOT);
+static constexpr int64_t FIRST_MODE = static_cast<int64_t>(Mode::DOUBLE_TAP_TO_WAKE);
+static constexpr int64_t LAST_MODE = static_cast<int64_t>(Mode::CAMERA_STREAMING_HIGH);
+
+// Delay between oneway method calls to avoid overflowing the binder buffers.
+static constexpr std::chrono::microseconds ONEWAY_API_DELAY = 100us;
+
+template <typename T, class... Args0, class... Args1>
+static void runBenchmark(benchmark::State& state, HalResult<T> (PowerHalController::*fn)(Args0...),
+ Args1&&... args1) {
+ while (state.KeepRunning()) {
+ PowerHalController controller;
+ HalResult<T> ret = (controller.*fn)(std::forward<Args1>(args1)...);
+ state.PauseTiming();
+ if (ret.isFailed()) state.SkipWithError("Power HAL request failed");
+ state.ResumeTiming();
+ }
+}
+
+template <typename T, class... Args0, class... Args1>
+static void runCachedBenchmark(benchmark::State& state,
+ HalResult<T> (PowerHalController::*fn)(Args0...), Args1&&... args1) {
+ PowerHalController controller;
+ // First call out of test, to cache HAL service and isSupported result.
+ (controller.*fn)(std::forward<Args1>(args1)...);
+
+ while (state.KeepRunning()) {
+ HalResult<T> ret = (controller.*fn)(std::forward<Args1>(args1)...);
+ state.PauseTiming();
+ if (ret.isFailed()) {
+ state.SkipWithError("Power HAL request failed");
+ }
+ testDelaySpin(
+ std::chrono::duration_cast<std::chrono::duration<float>>(ONEWAY_API_DELAY).count());
+ state.ResumeTiming();
+ }
+}
+
+static void BM_PowerHalControllerBenchmarks_init(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ PowerHalController controller;
+ controller.init();
+ }
+}
+
+static void BM_PowerHalControllerBenchmarks_initCached(benchmark::State& state) {
+ PowerHalController controller;
+ // First connection out of test.
+ controller.init();
+
+ while (state.KeepRunning()) {
+ controller.init();
+ }
+}
+
+static void BM_PowerHalControllerBenchmarks_setBoost(benchmark::State& state) {
+ Boost boost = static_cast<Boost>(state.range(0));
+ runBenchmark(state, &PowerHalController::setBoost, boost, 0);
+}
+
+static void BM_PowerHalControllerBenchmarks_setBoostCached(benchmark::State& state) {
+ Boost boost = static_cast<Boost>(state.range(0));
+ runCachedBenchmark(state, &PowerHalController::setBoost, boost, 0);
+}
+
+static void BM_PowerHalControllerBenchmarks_setMode(benchmark::State& state) {
+ Mode mode = static_cast<Mode>(state.range(0));
+ runBenchmark(state, &PowerHalController::setMode, mode, false);
+}
+
+static void BM_PowerHalControllerBenchmarks_setModeCached(benchmark::State& state) {
+ Mode mode = static_cast<Mode>(state.range(0));
+ runCachedBenchmark(state, &PowerHalController::setMode, mode, false);
+}
+
+BENCHMARK(BM_PowerHalControllerBenchmarks_init);
+BENCHMARK(BM_PowerHalControllerBenchmarks_initCached);
+BENCHMARK(BM_PowerHalControllerBenchmarks_setBoost)->DenseRange(FIRST_BOOST, LAST_BOOST, 1);
+BENCHMARK(BM_PowerHalControllerBenchmarks_setBoostCached)->DenseRange(FIRST_BOOST, LAST_BOOST, 1);
+BENCHMARK(BM_PowerHalControllerBenchmarks_setMode)->DenseRange(FIRST_MODE, LAST_MODE, 1);
+BENCHMARK(BM_PowerHalControllerBenchmarks_setModeCached)->DenseRange(FIRST_MODE, LAST_MODE, 1);
diff --git a/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp
new file mode 100644
index 0000000..97e026b
--- /dev/null
+++ b/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "PowerHalHidlBenchmarks"
+
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+#include <benchmark/benchmark.h>
+#include <hardware/power.h>
+#include <hardware_legacy/power.h>
+#include <testUtil.h>
+#include <chrono>
+
+using android::hardware::Return;
+using android::hardware::power::Boost;
+using android::hardware::power::Mode;
+using android::hardware::power::V1_0::Feature;
+using android::hardware::power::V1_0::PowerHint;
+using std::chrono::microseconds;
+using IPower1_0 = android::hardware::power::V1_0::IPower;
+using IPower1_1 = android::hardware::power::V1_1::IPower;
+
+using namespace android;
+using namespace std::chrono_literals;
+
+// Values from types.hal from versions 1.0 to 1.3.
+static constexpr int64_t FIRST_POWER_HINT = static_cast<int64_t>(PowerHint::VSYNC);
+static constexpr int64_t LAST_POWER_HINT = static_cast<int64_t>(PowerHint::LAUNCH);
+
+// Delay between oneway method calls to avoid overflowing the binder buffers.
+static constexpr microseconds ONEWAY_API_DELAY = 100us;
+
+template <class R, class I, class... Args0, class... Args1>
+static void runBenchmark(benchmark::State& state, microseconds delay, Return<R> (I::*fn)(Args0...),
+ Args1&&... args1) {
+ sp<I> hal = I::getService();
+
+ if (hal == nullptr) {
+ ALOGI("Power HAL HIDL not available, skipping test...");
+ return;
+ }
+
+ while (state.KeepRunning()) {
+ Return<R> ret = (*hal.*fn)(std::forward<Args1>(args1)...);
+ state.PauseTiming();
+ if (!ret.isOk()) state.SkipWithError(ret.description().c_str());
+ if (delay > 0us) {
+ testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(delay).count());
+ }
+ state.ResumeTiming();
+ }
+}
+
+static void BM_PowerHalHidlBenchmarks_setFeature(benchmark::State& state) {
+ runBenchmark(state, 0us, &IPower1_0::setFeature, Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE,
+ false);
+}
+
+static void BM_PowerHalHidlBenchmarks_setInteractive(benchmark::State& state) {
+ runBenchmark(state, 0us, &IPower1_0::setInteractive, false);
+}
+
+static void BM_PowerHalHidlBenchmarks_powerHint(benchmark::State& state) {
+ PowerHint powerHint = static_cast<PowerHint>(state.range(0));
+ runBenchmark(state, 0us, &IPower1_0::powerHint, powerHint, 0);
+}
+
+static void BM_PowerHalHidlBenchmarks_powerHintAsync(benchmark::State& state) {
+ PowerHint powerHint = static_cast<PowerHint>(state.range(0));
+ runBenchmark(state, ONEWAY_API_DELAY, &IPower1_1::powerHintAsync, powerHint, 0);
+}
+
+BENCHMARK(BM_PowerHalHidlBenchmarks_setFeature);
+BENCHMARK(BM_PowerHalHidlBenchmarks_setInteractive);
+BENCHMARK(BM_PowerHalHidlBenchmarks_powerHint)->DenseRange(FIRST_POWER_HINT, LAST_POWER_HINT, 1);
+BENCHMARK(BM_PowerHalHidlBenchmarks_powerHintAsync)
+ ->DenseRange(FIRST_POWER_HINT, LAST_POWER_HINT, 1);
diff --git a/libs/ui/UiConfig.cpp b/services/powermanager/benchmarks/main.cpp
similarity index 61%
copy from libs/ui/UiConfig.cpp
copy to services/powermanager/benchmarks/main.cpp
index 0ac863d..15c57bf 100644
--- a/libs/ui/UiConfig.cpp
+++ b/services/powermanager/benchmarks/main.cpp
@@ -1,11 +1,11 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2020 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
+ * 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,
@@ -14,15 +14,6 @@
* limitations under the License.
*/
-#include <ui/UiConfig.h>
+#include <benchmark/benchmark.h>
-namespace android {
-
-void appendUiConfigString(std::string& configStr) {
- static const char* config =
- " [libui]";
- configStr.append(config);
-}
-
-
-}; // namespace android
+BENCHMARK_MAIN();
diff --git a/services/powermanager/include/android/BatterySaverPolicyConfig.h b/services/powermanager/include/android/BatterySaverPolicyConfig.h
new file mode 100644
index 0000000..3a0c9d0
--- /dev/null
+++ b/services/powermanager/include/android/BatterySaverPolicyConfig.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef ANDROID_OS_BATTERY_SAVER_POLICY_CONFIG_H
+#define ANDROID_OS_BATTERY_SAVER_POLICY_CONFIG_H
+
+#include <math.h>
+#include <binder/Parcelable.h>
+#include <utils/RefBase.h>
+
+namespace android::os {
+
+enum class LocationMode : int32_t;
+enum class SoundTriggerMode : int32_t;
+/**
+ * BatterySaverPolicyConfig is a structure of configs to set Battery Saver policy flags.
+ * This file needs to be kept in sync with
+ * frameworks/base/core/java/android/os/BatterySaverPolicyConfig.java
+ */
+struct BatterySaverPolicyConfig : public android::Parcelable {
+
+ BatterySaverPolicyConfig(float adjustBrightnessFactor = 1.0f,
+ bool advertiseIsEnabled = false,
+ bool deferFullBackup = false,
+ bool deferKeyValueBackup = false,
+ std::vector<std::pair<String16, String16>> deviceSpecificSettings = {},
+ bool disableAnimation = false,
+ bool disableAod = false,
+ bool disableLaunchBoost = false,
+ bool disableOptionalSensors = false,
+ bool disableVibration = false,
+ bool enableAdjustBrightness = false,
+ bool enableDataSaver = false,
+ bool enableFirewall = false,
+ bool enableNightMode = false,
+ bool enableQuickDoze = false,
+ bool forceAllAppsStandby = false,
+ bool forceBackgroundCheck = false,
+ LocationMode locationMode = static_cast<LocationMode>(0),
+ SoundTriggerMode soundTriggerMode = static_cast<SoundTriggerMode>(0))
+ : mAdjustBrightnessFactor(adjustBrightnessFactor),
+ mAdvertiseIsEnabled(advertiseIsEnabled),
+ mDeferFullBackup(deferFullBackup),
+ mDeferKeyValueBackup(deferKeyValueBackup),
+ mDeviceSpecificSettings(deviceSpecificSettings),
+ mDisableAnimation(disableAnimation),
+ mDisableAod(disableAod),
+ mDisableLaunchBoost(disableLaunchBoost),
+ mDisableOptionalSensors(disableOptionalSensors),
+ mDisableVibration(disableVibration),
+ mEnableAdjustBrightness(enableAdjustBrightness),
+ mEnableDataSaver(enableDataSaver),
+ mEnableFirewall(enableFirewall),
+ mEnableNightMode(enableNightMode),
+ mEnableQuickDoze(enableQuickDoze),
+ mForceAllAppsStandby(forceAllAppsStandby),
+ mForceBackgroundCheck(forceBackgroundCheck),
+ mLocationMode(locationMode),
+ mSoundTriggerMode(soundTriggerMode) {
+ }
+
+ status_t readFromParcel(const android::Parcel* parcel) override;
+ status_t writeToParcel(android::Parcel* parcel) const override;
+ bool operator == (const BatterySaverPolicyConfig &bsp) const {
+ return fabs(mAdjustBrightnessFactor - bsp.mAdjustBrightnessFactor) == 0.0f &&
+ mAdvertiseIsEnabled == bsp.mAdvertiseIsEnabled &&
+ mDeferFullBackup == bsp.mDeferFullBackup &&
+ mDeferKeyValueBackup == bsp.mDeferKeyValueBackup &&
+ mDeviceSpecificSettings == bsp.mDeviceSpecificSettings &&
+ mDisableAnimation == bsp.mDisableAnimation &&
+ mDisableAod == bsp.mDisableAod &&
+ mDisableLaunchBoost == bsp.mDisableLaunchBoost &&
+ mDisableOptionalSensors == bsp.mDisableOptionalSensors &&
+ mDisableVibration == bsp.mDisableVibration &&
+ mEnableAdjustBrightness == bsp.mEnableAdjustBrightness &&
+ mEnableDataSaver == bsp.mEnableDataSaver &&
+ mEnableFirewall == bsp.mEnableFirewall &&
+ mEnableNightMode == bsp.mEnableNightMode &&
+ mEnableQuickDoze == bsp.mEnableQuickDoze &&
+ mForceAllAppsStandby == bsp.mForceAllAppsStandby &&
+ mForceBackgroundCheck == bsp.mForceBackgroundCheck &&
+ mLocationMode == bsp.mLocationMode &&
+ mSoundTriggerMode == bsp.mSoundTriggerMode;
+ }
+
+private:
+ status_t readDeviceSpecificSettings(const android::Parcel *parcel);
+ status_t writeDeviceSpecificSettings(android::Parcel *parcel) const;
+ /** Adjust screen brightness factor */
+ float mAdjustBrightnessFactor;
+ /** Is advertise enabled */
+ bool mAdvertiseIsEnabled;
+ /** Defer full backup */
+ bool mDeferFullBackup;
+ /** Defer key value backup */
+ bool mDeferKeyValueBackup;
+ /** Device specific settings */
+ std::vector<std::pair<String16, String16>> mDeviceSpecificSettings;
+ /** Disable animation */
+ bool mDisableAnimation;
+ /** Disable Aod */
+ bool mDisableAod;
+ /** Disable launch boost */
+ bool mDisableLaunchBoost;
+ /** Disable optional sensors */
+ bool mDisableOptionalSensors;
+ /** Disable vibration */
+ bool mDisableVibration;
+ /** Enable adjust brightness */
+ bool mEnableAdjustBrightness;
+ /** Enable data saver */
+ bool mEnableDataSaver;
+ /** Enable firewall */
+ bool mEnableFirewall;
+ /** Enable night mode */
+ bool mEnableNightMode;
+ /** Enable quick doze */
+ bool mEnableQuickDoze;
+ /** Force all Apps standby */
+ bool mForceAllAppsStandby;
+ /** Force Background check */
+ bool mForceBackgroundCheck;
+ /** Location mode */
+ LocationMode mLocationMode;
+ /** SoundTrigger mode */
+ SoundTriggerMode mSoundTriggerMode;
+};
+
+} // namespace android::os
+
+#endif /* ANDROID_OS_BATTERY_SAVER_POLICY_CONFIG_H */
diff --git a/services/powermanager/include/android/LocationMode.h b/services/powermanager/include/android/LocationMode.h
new file mode 100644
index 0000000..42933d4
--- /dev/null
+++ b/services/powermanager/include/android/LocationMode.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef ANDROID_OS_LOCATION_MODE_H
+#define ANDROID_OS_LOCATION_MODE_H
+
+namespace android::os {
+
+enum class LocationMode : int32_t {
+ NO_CHANGE = IPowerManager::LOCATION_MODE_NO_CHANGE,
+ GPS_DISABLED_WHEN_SCREEN_OFF = IPowerManager::LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF,
+ ALL_DISABLED_WHEN_SCREEN_OFF = IPowerManager::LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF,
+ FOREGROUND_ONLY = IPowerManager::LOCATION_MODE_FOREGROUND_ONLY,
+ THROTTLE_REQUESTS_WHEN_SCREEN_OFF =
+ IPowerManager::LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF,
+ MIN = IPowerManager::LOCATION_MODE_NO_CHANGE,
+ MAX = IPowerManager::LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF,
+};
+
+} // namespace android::os
+
+#endif /* ANDROID_OS_LOCATION_MODE_H */
diff --git a/services/powermanager/include/android/ParcelDuration.h b/services/powermanager/include/android/ParcelDuration.h
new file mode 100644
index 0000000..117d173
--- /dev/null
+++ b/services/powermanager/include/android/ParcelDuration.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef ANDROID_OS_PARCELDURATION_H
+#define ANDROID_OS_PARCELDURATION_H
+
+#include <binder/Parcelable.h>
+#include <math.h>
+#include <utils/RefBase.h>
+
+namespace android::os {
+
+/**
+ * Parcelable version of {@link java.time.Duration} that can be used in binder calls.
+ * This file needs to be kept in sync with
+ * frameworks/base/core/java/android/os/ParcelDuration.java
+ */
+struct ParcelDuration : public android::Parcelable {
+ ParcelDuration(int64_t seconds = 0, int32_t nanos = 0) : mSeconds(seconds), mNanos(nanos) {}
+
+ status_t readFromParcel(const android::Parcel* parcel) override;
+ status_t writeToParcel(android::Parcel* parcel) const override;
+ bool operator==(const ParcelDuration& pd) const {
+ return mSeconds == pd.mSeconds && mNanos == pd.mNanos;
+ }
+
+private:
+ int64_t mSeconds;
+ int32_t mNanos;
+};
+
+} // namespace android::os
+
+#endif /* ANDROID_OS_PARCELDURATION_H */
diff --git a/services/powermanager/include/android/PowerSaveState.h b/services/powermanager/include/android/PowerSaveState.h
new file mode 100644
index 0000000..1818db2
--- /dev/null
+++ b/services/powermanager/include/android/PowerSaveState.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef ANDROID_OS_POWER_SAVE_STATE_H
+#define ANDROID_OS_POWER_SAVE_STATE_H
+
+#include <math.h>
+#include <binder/Parcelable.h>
+#include <utils/RefBase.h>
+
+namespace android::os {
+
+enum class LocationMode : int32_t;
+enum class SoundTriggerMode : int32_t;
+/**
+ * PowerSaveState is a structure to encapsulate PowerSaveState status.
+ * This file needs to be kept in sync with frameworks/base/core/java/android/os/PowerSaveState.java
+ */
+struct PowerSaveState : public android::Parcelable {
+
+ PowerSaveState(bool batterySaverEnabled = false,
+ bool globalBatterySaverEnabled = false,
+ LocationMode locationMode = static_cast<LocationMode>(0),
+ SoundTriggerMode soundTriggerMode = static_cast<SoundTriggerMode>(0),
+ float brightnessFactor = 0.5f)
+ : mBatterySaverEnabled(batterySaverEnabled),
+ mGlobalBatterySaverEnabled(globalBatterySaverEnabled),
+ mLocationMode(locationMode),
+ mSoundTriggerMode(soundTriggerMode),
+ mBrightnessFactor(brightnessFactor) {
+ }
+
+ bool getBatterySaverEnabled() const { return mBatterySaverEnabled; }
+ bool getGlobalBatterySaverEnabled() const { return mGlobalBatterySaverEnabled; }
+ LocationMode getLocationMode() const { return mLocationMode; }
+ SoundTriggerMode getSoundTriggerMode() const { return mSoundTriggerMode; }
+ float getBrightnessFactor() const { return mBrightnessFactor; }
+ bool operator == (const PowerSaveState &ps) const {
+ return mBatterySaverEnabled == ps.mBatterySaverEnabled &&
+ mGlobalBatterySaverEnabled == ps.mGlobalBatterySaverEnabled &&
+ mLocationMode == ps.mLocationMode &&
+ fabs(mBrightnessFactor - ps.mBrightnessFactor) == 0.0f;
+ }
+
+ status_t readFromParcel(const android::Parcel* parcel) override;
+ status_t writeToParcel(android::Parcel* parcel) const override;
+
+private:
+ /** Whether we should enable battery saver for this service. */
+ bool mBatterySaverEnabled;
+ /** Whether battery saver mode is enabled. */
+ bool mGlobalBatterySaverEnabled;
+ /** Location mode */
+ LocationMode mLocationMode;
+ /** SoundTrigger mode */
+ SoundTriggerMode mSoundTriggerMode;
+ /** Screen brightness factor. */
+ float mBrightnessFactor;
+};
+
+} // namespace android::os
+
+#endif /* ANDROID_OS_POWER_SAVE_STATE_H */
diff --git a/services/powermanager/include/android/SoundTriggerMode.h b/services/powermanager/include/android/SoundTriggerMode.h
new file mode 100644
index 0000000..cee43e3
--- /dev/null
+++ b/services/powermanager/include/android/SoundTriggerMode.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef ANDROID_OS_SOUNDTRIGGER_MODE_H
+#define ANDROID_OS_SOUNDTRIGGER_MODE_H
+
+namespace android::os {
+
+enum class SoundTriggerMode : int32_t {
+ ALL_ENABLED = IPowerManager::SOUND_TRIGGER_MODE_ALL_ENABLED,
+ CRITICAL_ONLY = IPowerManager::SOUND_TRIGGER_MODE_CRITICAL_ONLY,
+ ALL_DISABLED = IPowerManager::SOUND_TRIGGER_MODE_ALL_DISABLED,
+ MIN = IPowerManager::SOUND_TRIGGER_MODE_ALL_ENABLED,
+ MAX = IPowerManager::SOUND_TRIGGER_MODE_ALL_DISABLED,
+};
+
+} // namespace android::os
+
+#endif /* ANDROID_OS_SOUNDTRIGGER_MODE_H */
diff --git a/services/powermanager/include/android/WorkSource.h b/services/powermanager/include/android/WorkSource.h
new file mode 100644
index 0000000..f12847d
--- /dev/null
+++ b/services/powermanager/include/android/WorkSource.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef ANDROID_OS_WORKSOURCE_H
+#define ANDROID_OS_WORKSOURCE_H
+
+#include <optional>
+#include <binder/Parcelable.h>
+#include <utils/RefBase.h>
+
+namespace android::os {
+
+/**
+ * WorkSource is a structure to describes the source of some work that may be done by someone else.
+ * This file needs to be kept in sync with frameworks/base/core/java/android/os/WorkSource.java
+ */
+struct WorkSource : public android::Parcelable {
+ WorkSource(
+ std::vector<int32_t> uids = {},
+ std::optional<std::vector<std::optional<String16>>> names = std::nullopt)
+ : mUids(uids),
+ mNames(names) {
+ }
+ std::vector<int32_t> getUids() const { return mUids; }
+ std::optional<std::vector<std::optional<String16>>> getNames() const { return mNames; }
+ bool operator == (const WorkSource &ws) const {
+ return mUids == ws.mUids && mNames == ws.mNames;
+ }
+ status_t readFromParcel(const android::Parcel* parcel) override;
+ status_t writeToParcel(android::Parcel* parcel) const override;
+
+private:
+ /** WorkSource UID array */
+ std::vector<int32_t> mUids = {};
+ /** WorkSource Tag array */
+ std::optional<std::vector<std::optional<String16>>> mNames = {};
+};
+
+} // namespace android::os
+
+#endif /* ANDROID_OS_WORKSOURCE_H */
diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp
new file mode 100644
index 0000000..659b2d2
--- /dev/null
+++ b/services/powermanager/tests/Android.bp
@@ -0,0 +1,54 @@
+// Copyright (C) 2020 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"],
+}
+
+cc_test {
+ name: "libpowermanager_test",
+ test_suites: ["device-tests"],
+ srcs: [
+ "IThermalManagerTest.cpp",
+ "PowerHalControllerTest.cpp",
+ "PowerHalLoaderTest.cpp",
+ "PowerHalWrapperAidlTest.cpp",
+ "PowerHalWrapperHidlV1_0Test.cpp",
+ "PowerHalWrapperHidlV1_1Test.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libhidlbase",
+ "liblog",
+ "libpowermanager",
+ "libutils",
+ "android.hardware.power@1.0",
+ "android.hardware.power@1.1",
+ "android.hardware.power-V2-cpp",
+ ],
+ static_libs: [
+ "libgmock",
+ ],
+}
diff --git a/services/powermanager/IThermalManagerTest.cpp b/services/powermanager/tests/IThermalManagerTest.cpp
similarity index 95%
rename from services/powermanager/IThermalManagerTest.cpp
rename to services/powermanager/tests/IThermalManagerTest.cpp
index 575b9ee..b62be5f 100644
--- a/services/powermanager/IThermalManagerTest.cpp
+++ b/services/powermanager/tests/IThermalManagerTest.cpp
@@ -86,12 +86,14 @@
EXPECT_NE(binder, nullptr);
mThermalSvc = interface_cast<IThermalService>(binder);
EXPECT_NE(mThermalSvc, nullptr);
+ // Lock mutex for operation, so listener will only be processed after wait_for is called
+ std::unique_lock<std::mutex> lock(mMutex);
bool success = false;
binder::Status ret = mThermalSvc->registerThermalStatusListener(this, &success);
+ // Check the result
ASSERT_TRUE(success);
ASSERT_TRUE(ret.isOk());
// Wait for listener called after registration, shouldn't timeout
- std::unique_lock<std::mutex> lock(mMutex);
EXPECT_NE(mCondition.wait_for(lock, 1s), std::cv_status::timeout);
}
@@ -111,6 +113,7 @@
TEST_P(IThermalListenerTest, TestListener) {
int level = GetParam();
+ // Lock mutex for operation, so listener will only be processed after wait_for is called
std::unique_lock<std::mutex> lock(mMutex);
// Set the override thermal status
setThermalOverride(level);
diff --git a/services/powermanager/tests/PowerHalControllerTest.cpp b/services/powermanager/tests/PowerHalControllerTest.cpp
new file mode 100644
index 0000000..6cc7a6f
--- /dev/null
+++ b/services/powermanager/tests/PowerHalControllerTest.cpp
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "PowerHalControllerTest"
+
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <powermanager/PowerHalController.h>
+#include <utils/Log.h>
+
+#include <thread>
+
+using android::hardware::power::Boost;
+using android::hardware::power::Mode;
+using android::hardware::power::V1_0::Feature;
+using android::hardware::power::V1_0::IPower;
+using android::hardware::power::V1_0::PowerHint;
+
+using namespace android;
+using namespace android::power;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIPowerV1_0 : public IPower {
+public:
+ MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override));
+ MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override));
+ MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
+ MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats,
+ (getPlatformLowPowerStats_cb _hidl_cb), (override));
+};
+
+class TestPowerHalConnector : public HalConnector {
+public:
+ TestPowerHalConnector(sp<IPower> powerHal) : mHal(std::move(powerHal)) {}
+ virtual ~TestPowerHalConnector() = default;
+
+ virtual std::unique_ptr<HalWrapper> connect() override {
+ mCountMutex.lock();
+ ++mConnectedCount;
+ mCountMutex.unlock();
+ return std::make_unique<HidlHalWrapperV1_0>(mHal);
+ }
+
+ void reset() override {
+ mCountMutex.lock();
+ ++mResetCount;
+ mCountMutex.unlock();
+ }
+
+ int getConnectCount() { return mConnectedCount; }
+
+ int getResetCount() { return mResetCount; }
+
+private:
+ sp<IPower> mHal = nullptr;
+ std::mutex mCountMutex;
+ int mConnectedCount = 0;
+ int mResetCount = 0;
+};
+
+class AlwaysFailingTestPowerHalConnector : public TestPowerHalConnector {
+public:
+ AlwaysFailingTestPowerHalConnector() : TestPowerHalConnector(nullptr) {}
+
+ std::unique_ptr<HalWrapper> connect() override {
+ // Call parent to update counter, but ignore connected HalWrapper.
+ TestPowerHalConnector::connect();
+ return nullptr;
+ }
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class PowerHalControllerTest : public Test {
+public:
+ void SetUp() override {
+ mMockHal = new StrictMock<MockIPowerV1_0>();
+ std::unique_ptr<TestPowerHalConnector> halConnector =
+ std::make_unique<TestPowerHalConnector>(mMockHal);
+ mHalConnector = halConnector.get();
+ mHalController = std::make_unique<PowerHalController>(std::move(halConnector));
+ }
+
+protected:
+ sp<StrictMock<MockIPowerV1_0>> mMockHal = nullptr;
+ TestPowerHalConnector* mHalConnector = nullptr;
+ std::unique_ptr<PowerHalController> mHalController = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(PowerHalControllerTest, TestInitConnectsToPowerHalOnlyOnce) {
+ int powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 0);
+
+ mHalController->init();
+ mHalController->init();
+
+ // PowerHalConnector was called only once and never reset.
+ powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 1);
+ int powerHalResetCount = mHalConnector->getResetCount();
+ EXPECT_EQ(powerHalResetCount, 0);
+}
+
+TEST_F(PowerHalControllerTest, TestUnableToConnectToPowerHalIgnoresAllApiCalls) {
+ std::unique_ptr<AlwaysFailingTestPowerHalConnector> halConnector =
+ std::make_unique<AlwaysFailingTestPowerHalConnector>();
+ AlwaysFailingTestPowerHalConnector* failingHalConnector = halConnector.get();
+ PowerHalController halController(std::move(halConnector));
+
+ int powerHalConnectCount = failingHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 0);
+
+ // Still works with EmptyPowerHalWrapper as fallback ignoring every api call
+ // and logging.
+ auto result = halController.setBoost(Boost::INTERACTION, 1000);
+ ASSERT_TRUE(result.isUnsupported());
+ result = halController.setMode(Mode::LAUNCH, true);
+ ASSERT_TRUE(result.isUnsupported());
+
+ // PowerHalConnector was called every time to attempt to reconnect with
+ // underlying service.
+ powerHalConnectCount = failingHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 2);
+ // PowerHalConnector was never reset.
+ int powerHalResetCount = mHalConnector->getResetCount();
+ EXPECT_EQ(powerHalResetCount, 0);
+}
+
+TEST_F(PowerHalControllerTest, TestAllApiCallsDelegatedToConnectedPowerHal) {
+ int powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 0);
+
+ {
+ InSequence seg;
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::INTERACTION), Eq(100)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(1))).Times(Exactly(1));
+ }
+
+ auto result = mHalController->setBoost(Boost::INTERACTION, 100);
+ ASSERT_TRUE(result.isOk());
+ result = mHalController->setMode(Mode::LAUNCH, true);
+ ASSERT_TRUE(result.isOk());
+
+ // PowerHalConnector was called only once and never reset.
+ powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 1);
+ int powerHalResetCount = mHalConnector->getResetCount();
+ EXPECT_EQ(powerHalResetCount, 0);
+}
+
+TEST_F(PowerHalControllerTest, TestPowerHalRecoversFromFailureByRecreatingPowerHal) {
+ int powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 0);
+
+ ON_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), _))
+ .WillByDefault([](PowerHint, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(Exactly(4));
+
+ auto result = mHalController->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_TRUE(result.isOk());
+ result = mHalController->setMode(Mode::LAUNCH, true);
+ ASSERT_TRUE(result.isFailed());
+ result = mHalController->setMode(Mode::VR, false);
+ ASSERT_TRUE(result.isOk());
+ result = mHalController->setMode(Mode::LOW_POWER, true);
+ ASSERT_TRUE(result.isOk());
+
+ // PowerHalConnector was called only twice: on first api call and after failed
+ // call.
+ powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 2);
+ // PowerHalConnector was reset once after failed call.
+ int powerHalResetCount = mHalConnector->getResetCount();
+ EXPECT_EQ(powerHalResetCount, 1);
+}
+
+TEST_F(PowerHalControllerTest, TestPowerHalDoesNotTryToRecoverFromFailureOnUnsupportedCalls) {
+ int powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 0);
+
+ auto result = mHalController->setBoost(Boost::CAMERA_LAUNCH, 1000);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mHalController->setMode(Mode::CAMERA_STREAMING_HIGH, true);
+ ASSERT_TRUE(result.isUnsupported());
+
+ // PowerHalConnector was called only once and never reset.
+ powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 1);
+ int powerHalResetCount = mHalConnector->getResetCount();
+ EXPECT_EQ(powerHalResetCount, 0);
+}
+
+TEST_F(PowerHalControllerTest, TestMultiThreadConnectsOnlyOnce) {
+ int powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 0);
+
+ EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(Exactly(10));
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mHalController->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_TRUE(result.isOk());
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ // PowerHalConnector was called only by the first thread to use the api and
+ // never reset.
+ powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 1);
+ int powerHalResetCount = mHalConnector->getResetCount();
+ EXPECT_EQ(powerHalResetCount, 0);
+}
+
+TEST_F(PowerHalControllerTest, TestMultiThreadWithFailureReconnectIsThreadSafe) {
+ int powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 0);
+
+ ON_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), _))
+ .WillByDefault([](PowerHint, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(Exactly(40));
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mHalController->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_TRUE(result.isOk());
+ }));
+ threads.push_back(std::thread([&]() {
+ auto result = mHalController->setMode(Mode::LAUNCH, true);
+ ASSERT_TRUE(result.isFailed());
+ }));
+ threads.push_back(std::thread([&]() {
+ auto result = mHalController->setMode(Mode::LOW_POWER, false);
+ ASSERT_TRUE(result.isOk());
+ }));
+ threads.push_back(std::thread([&]() {
+ auto result = mHalController->setMode(Mode::VR, true);
+ ASSERT_TRUE(result.isOk());
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ // PowerHalConnector was called at least once by the first thread.
+ // Reset and reconnect calls were made at most 10 times, once after each
+ // failure.
+ powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_THAT(powerHalConnectCount, AllOf(Ge(1), Le(11)));
+ int powerHalResetCount = mHalConnector->getResetCount();
+ EXPECT_THAT(powerHalResetCount, Le(10));
+}
diff --git a/services/powermanager/tests/PowerHalLoaderTest.cpp b/services/powermanager/tests/PowerHalLoaderTest.cpp
new file mode 100644
index 0000000..058e1b5
--- /dev/null
+++ b/services/powermanager/tests/PowerHalLoaderTest.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "PowerHalLoaderTest"
+
+#include <android-base/logging.h>
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/IPower.h>
+#include <gtest/gtest.h>
+#include <powermanager/PowerHalLoader.h>
+
+#include <future>
+
+using IPowerV1_0 = android::hardware::power::V1_0::IPower;
+using IPowerV1_1 = android::hardware::power::V1_1::IPower;
+using IPowerAidl = android::hardware::power::IPower;
+
+using namespace android;
+using namespace android::power;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+template <typename T>
+sp<T> loadHal();
+
+template <>
+sp<IPowerAidl> loadHal<IPowerAidl>() {
+ return PowerHalLoader::loadAidl();
+}
+
+template <>
+sp<IPowerV1_0> loadHal<IPowerV1_0>() {
+ return PowerHalLoader::loadHidlV1_0();
+}
+
+template <>
+sp<IPowerV1_1> loadHal<IPowerV1_1>() {
+ return PowerHalLoader::loadHidlV1_1();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+template <typename T>
+class PowerHalLoaderTest : public Test {
+public:
+ sp<T> load() { return ::loadHal<T>(); }
+ void unload() { PowerHalLoader::unloadAll(); }
+};
+
+// -------------------------------------------------------------------------------------------------
+
+typedef ::testing::Types<IPowerAidl, IPowerV1_0, IPowerV1_1> PowerHalTypes;
+TYPED_TEST_SUITE(PowerHalLoaderTest, PowerHalTypes);
+
+TYPED_TEST(PowerHalLoaderTest, TestLoadsOnlyOnce) {
+ sp<TypeParam> firstHal = this->load();
+ if (firstHal == nullptr) {
+ ALOGE("Power HAL not available. Skipping test.");
+ return;
+ }
+ sp<TypeParam> secondHal = this->load();
+ ASSERT_EQ(firstHal, secondHal);
+}
+
+TYPED_TEST(PowerHalLoaderTest, TestUnload) {
+ sp<TypeParam> firstHal = this->load();
+ if (firstHal == nullptr) {
+ ALOGE("Power HAL not available. Skipping test.");
+ return;
+ }
+ this->unload();
+ sp<TypeParam> secondHal = this->load();
+ ASSERT_NE(secondHal, nullptr);
+ ASSERT_NE(firstHal, secondHal);
+}
+
+TYPED_TEST(PowerHalLoaderTest, TestLoadMultiThreadLoadsOnlyOnce) {
+ std::vector<std::future<sp<TypeParam>>> futures;
+ for (int i = 0; i < 10; i++) {
+ futures.push_back(
+ std::async(std::launch::async, &PowerHalLoaderTest<TypeParam>::load, this));
+ }
+
+ futures[0].wait();
+ sp<TypeParam> firstHal = futures[0].get();
+ if (firstHal == nullptr) {
+ ALOGE("Power HAL not available. Skipping test.");
+ return;
+ }
+
+ for (int i = 1; i < 10; i++) {
+ futures[i].wait();
+ sp<TypeParam> currentHal = futures[i].get();
+ ASSERT_EQ(firstHal, currentHal);
+ }
+}
diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
new file mode 100644
index 0000000..d890f5c
--- /dev/null
+++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "PowerHalWrapperAidlTest"
+
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPowerHintSession.h>
+#include <android/hardware/power/Mode.h>
+#include <binder/IServiceManager.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <powermanager/PowerHalWrapper.h>
+#include <utils/Log.h>
+
+#include <unistd.h>
+#include <thread>
+
+using android::binder::Status;
+using android::hardware::power::Boost;
+using android::hardware::power::IPower;
+using android::hardware::power::IPowerHintSession;
+using android::hardware::power::Mode;
+
+using namespace android;
+using namespace android::power;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIPower : public IPower {
+public:
+ MOCK_METHOD(Status, isBoostSupported, (Boost boost, bool* ret), (override));
+ MOCK_METHOD(Status, setBoost, (Boost boost, int32_t durationMs), (override));
+ MOCK_METHOD(Status, isModeSupported, (Mode mode, bool* ret), (override));
+ MOCK_METHOD(Status, setMode, (Mode mode, bool enabled), (override));
+ MOCK_METHOD(Status, createHintSession,
+ (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+ int64_t durationNanos, sp<IPowerHintSession>* session),
+ (override));
+ MOCK_METHOD(Status, getHintSessionPreferredRate, (int64_t * rate), (override));
+ MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
+ MOCK_METHOD(std::string, getInterfaceHash, (), (override));
+ MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class PowerHalWrapperAidlTest : public Test {
+public:
+ void SetUp() override;
+
+protected:
+ std::unique_ptr<HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIPower>> mMockHal = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+void PowerHalWrapperAidlTest::SetUp() {
+ mMockHal = new StrictMock<MockIPower>();
+ mWrapper = std::make_unique<AidlHalWrapper>(mMockHal);
+ ASSERT_NE(nullptr, mWrapper);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(PowerHalWrapperAidlTest, TestSetBoostSuccessful) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::DISPLAY_UPDATE_IMMINENT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::DISPLAY_UPDATE_IMMINENT), Eq(100)))
+ .Times(Exactly(1));
+ }
+
+ auto result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 100);
+ ASSERT_TRUE(result.isOk());
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSetBoostFailed) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::INTERACTION), Eq(100)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+ EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::DISPLAY_UPDATE_IMMINENT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+ }
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 100);
+ ASSERT_TRUE(result.isFailed());
+ result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 1000);
+ ASSERT_TRUE(result.isFailed());
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSetBoostUnsupported) {
+ EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status())));
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setBoost(Boost::CAMERA_SHOT, 10);
+ ASSERT_TRUE(result.isUnsupported());
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSetBoostMultiThreadCheckSupportedOnlyOnce) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::INTERACTION), Eq(100))).Times(Exactly(10));
+ }
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 100);
+ ASSERT_TRUE(result.isOk());
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSetModeSuccessful) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::DISPLAY_INACTIVE), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::DISPLAY_INACTIVE), Eq(false)))
+ .Times(Exactly(1));
+ }
+
+ auto result = mWrapper->setMode(Mode::DISPLAY_INACTIVE, false);
+ ASSERT_TRUE(result.isOk());
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSetModeFailed) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::LAUNCH), Eq(true)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+ EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::DISPLAY_INACTIVE), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+ }
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, true);
+ ASSERT_TRUE(result.isFailed());
+ result = mWrapper->setMode(Mode::DISPLAY_INACTIVE, false);
+ ASSERT_TRUE(result.isFailed());
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSetModeUnsupported) {
+ EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status())));
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, true);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
+ ASSERT_TRUE(result.isUnsupported());
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSetModeMultiThreadCheckSupportedOnlyOnce) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::LAUNCH), Eq(false))).Times(Exactly(10));
+ }
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mWrapper->setMode(Mode::LAUNCH, false);
+ ASSERT_TRUE(result.isOk());
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestCreateHintSessionSuccessful) {
+ std::vector<int> threadIds{gettid()};
+ int32_t tgid = 999;
+ int32_t uid = 1001;
+ int64_t durationNanos = 16666666L;
+ EXPECT_CALL(*mMockHal.get(),
+ createHintSession(Eq(tgid), Eq(uid), Eq(threadIds), Eq(durationNanos), _))
+ .Times(Exactly(1));
+ auto result = mWrapper->createHintSession(tgid, uid, threadIds, durationNanos);
+ ASSERT_TRUE(result.isOk());
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestCreateHintSessionFailed) {
+ int32_t tgid = 999;
+ int32_t uid = 1001;
+ std::vector<int> threadIds{};
+ int64_t durationNanos = 16666666L;
+ EXPECT_CALL(*mMockHal.get(),
+ createHintSession(Eq(tgid), Eq(uid), Eq(threadIds), Eq(durationNanos), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT)));
+ auto result = mWrapper->createHintSession(tgid, uid, threadIds, durationNanos);
+ ASSERT_TRUE(result.isFailed());
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestGetHintSessionPreferredRate) {
+ EXPECT_CALL(*mMockHal.get(), getHintSessionPreferredRate(_)).Times(Exactly(1));
+ auto result = mWrapper->getHintSessionPreferredRate();
+ ASSERT_TRUE(result.isOk());
+ int64_t rate = result.value();
+ ASSERT_GE(0, rate);
+}
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
new file mode 100644
index 0000000..b54762c
--- /dev/null
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "PowerHalWrapperHidlV1_0Test"
+
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+#include <binder/IServiceManager.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <powermanager/PowerHalWrapper.h>
+#include <utils/Log.h>
+
+using android::hardware::power::Boost;
+using android::hardware::power::Mode;
+using android::hardware::power::V1_0::Feature;
+using android::hardware::power::V1_0::IPower;
+using android::hardware::power::V1_0::PowerHint;
+
+using namespace android;
+using namespace android::power;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIPowerV1_0 : public IPower {
+public:
+ MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override));
+ MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override));
+ MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
+ MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats,
+ (getPlatformLowPowerStats_cb _hidl_cb), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class PowerHalWrapperHidlV1_0Test : public Test {
+public:
+ void SetUp() override;
+
+protected:
+ std::unique_ptr<HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIPowerV1_0>> mMockHal = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+void PowerHalWrapperHidlV1_0Test::SetUp() {
+ mMockHal = new StrictMock<MockIPowerV1_0>();
+ mWrapper = std::make_unique<HidlHalWrapperV1_0>(mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(PowerHalWrapperHidlV1_0Test, TestSetBoostSuccessful) {
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::INTERACTION), Eq(1000))).Times(Exactly(1));
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_TRUE(result.isOk());
+}
+
+TEST_F(PowerHalWrapperHidlV1_0Test, TestSetBoostFailed) {
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::INTERACTION), Eq(1000)))
+ .Times(Exactly(1))
+ .WillRepeatedly([](PowerHint, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_TRUE(result.isFailed());
+}
+
+TEST_F(PowerHalWrapperHidlV1_0Test, TestSetBoostUnsupported) {
+ auto result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 10);
+ ASSERT_TRUE(result.isUnsupported());
+}
+
+TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeSuccessful) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(1))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LOW_POWER), Eq(0))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::SUSTAINED_PERFORMANCE), Eq(1)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::VR_MODE), Eq(0))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), setInteractive(Eq(true))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(),
+ setFeature(Eq(Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE), Eq(false)))
+ .Times(Exactly(1));
+ }
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, true);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::LOW_POWER, false);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::SUSTAINED_PERFORMANCE, true);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::VR, false);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::INTERACTIVE, true);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::DOUBLE_TAP_TO_WAKE, false);
+ ASSERT_TRUE(result.isOk());
+}
+
+TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeFailed) {
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(1)))
+ .Times(Exactly(1))
+ .WillRepeatedly([](PowerHint, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, 1);
+ ASSERT_TRUE(result.isFailed());
+}
+
+TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeIgnored) {
+ auto result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
+ ASSERT_TRUE(result.isUnsupported());
+}
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
new file mode 100644
index 0000000..d30e8d2
--- /dev/null
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "PowerHalWrapperHidlV1_1Test"
+
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+#include <binder/IServiceManager.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <powermanager/PowerHalWrapper.h>
+#include <utils/Log.h>
+
+using android::hardware::power::Boost;
+using android::hardware::power::Mode;
+using android::hardware::power::V1_0::Feature;
+using android::hardware::power::V1_0::PowerHint;
+using IPowerV1_1 = android::hardware::power::V1_1::IPower;
+using IPowerV1_0 = android::hardware::power::V1_0::IPower;
+
+using namespace android;
+using namespace android::power;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIPowerV1_0 : public IPowerV1_0 {
+public:
+ MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override));
+ MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override));
+ MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
+ MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats,
+ (getPlatformLowPowerStats_cb _hidl_cb), (override));
+};
+
+class MockIPowerV1_1 : public IPowerV1_1 {
+public:
+ MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override));
+ MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override));
+ MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
+ MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats,
+ (getPlatformLowPowerStats_cb _hidl_cb), (override));
+ MOCK_METHOD(hardware::Return<void>, powerHintAsync, (PowerHint hint, int32_t data), (override));
+ MOCK_METHOD(hardware::Return<void>, getSubsystemLowPowerStats,
+ (getSubsystemLowPowerStats_cb _hidl_cb), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class PowerHalWrapperHidlV1_1Test : public Test {
+public:
+ void SetUp() override;
+
+protected:
+ std::unique_ptr<HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIPowerV1_0>> mMockHalV1_0 = nullptr;
+ sp<StrictMock<MockIPowerV1_1>> mMockHalV1_1 = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+void PowerHalWrapperHidlV1_1Test::SetUp() {
+ mMockHalV1_0 = new StrictMock<MockIPowerV1_0>();
+ mMockHalV1_1 = new StrictMock<MockIPowerV1_1>();
+ mWrapper = std::make_unique<HidlHalWrapperV1_1>(mMockHalV1_0, mMockHalV1_1);
+ ASSERT_NE(mWrapper, nullptr);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostSuccessful) {
+ EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::INTERACTION), Eq(1000)))
+ .Times(Exactly(1));
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_TRUE(result.isOk());
+}
+
+TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostFailed) {
+ EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::INTERACTION), Eq(1000)))
+ .Times(Exactly(1))
+ .WillRepeatedly([](PowerHint, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_TRUE(result.isFailed());
+}
+
+TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostUnsupported) {
+ auto result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 10);
+ ASSERT_TRUE(result.isUnsupported());
+}
+
+TEST_F(PowerHalWrapperHidlV1_1Test, TestSetMode) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::LAUNCH), Eq(1)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::LOW_POWER), Eq(0)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHalV1_1.get(),
+ powerHintAsync(Eq(PowerHint::SUSTAINED_PERFORMANCE), Eq(1)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::VR_MODE), Eq(0)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHalV1_0.get(), setInteractive(Eq(true))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHalV1_0.get(),
+ setFeature(Eq(Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE), Eq(false)))
+ .Times(Exactly(1));
+ }
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, true);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::LOW_POWER, false);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::SUSTAINED_PERFORMANCE, true);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::VR, false);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::INTERACTIVE, true);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::DOUBLE_TAP_TO_WAKE, false);
+ ASSERT_TRUE(result.isOk());
+}
+
+TEST_F(PowerHalWrapperHidlV1_1Test, TestSetModeFailed) {
+ EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::LAUNCH), Eq(1)))
+ .Times(Exactly(1))
+ .WillRepeatedly([](PowerHint, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, 1);
+ ASSERT_TRUE(result.isFailed());
+}
+
+TEST_F(PowerHalWrapperHidlV1_1Test, TestSetModeIgnored) {
+ auto result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
+ ASSERT_TRUE(result.isUnsupported());
+}
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 4d4cac5..d8e8b52 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -145,13 +145,7 @@
convertToSensor(convertToOldSensorInfo(list[i]), &sensor);
if (sensor.type < static_cast<int>(SensorType::DEVICE_PRIVATE_BASE)) {
- if(sensor.resolution == 0) {
- // Don't crash here or the device will go into a crashloop.
- ALOGW("%s must have a non-zero resolution", sensor.name);
- // For simple algos, map their resolution to 1 if it's not specified
- sensor.resolution =
- SensorDeviceUtils::defaultResolutionForType(sensor.type);
- }
+ sensor.resolution = SensorDeviceUtils::resolutionForSensor(sensor);
// Some sensors don't have a default resolution and will be left at 0.
// Don't crash in this case since CTS will verify that devices don't go to
@@ -165,6 +159,9 @@
SensorDeviceUtils::quantizeValue(
&sensor.maxRange, promotedResolution);
}
+ } else {
+ // Don't crash here or the device will go into a crashloop.
+ ALOGW("%s should have a non-zero resolution", sensor.name);
}
}
@@ -917,6 +914,21 @@
return mActivationCount.valueAt(activationIndex).numActiveClients() > 0;
}
+void SensorDevice::onMicSensorAccessChanged(void* ident, int handle, nsecs_t samplingPeriodNs) {
+ Mutex::Autolock _l(mLock);
+ ssize_t activationIndex = mActivationCount.indexOfKey(handle);
+ if (activationIndex < 0) {
+ ALOGW("Handle %d cannot be found in activation record", handle);
+ return;
+ }
+ Info& info(mActivationCount.editValueAt(activationIndex));
+ if (info.hasBatchParamsForIdent(ident)) {
+ ssize_t index = info.batchParams.indexOfKey(ident);
+ BatchParams& params = info.batchParams.editValueAt(index);
+ params.mTSample = samplingPeriodNs;
+ }
+}
+
void SensorDevice::enableAllSensors() {
if (mSensors == nullptr) return;
Mutex::Autolock _l(mLock);
diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h
index 5e7d3da..75da7bb 100644
--- a/services/sensorservice/SensorDevice.h
+++ b/services/sensorservice/SensorDevice.h
@@ -125,6 +125,10 @@
bool isSensorActive(int handle) const;
+ // To update the BatchParams of a SensorEventConnection when the mic toggle changes its state
+ // while the Sensors Off toggle is on.
+ void onMicSensorAccessChanged(void* ident, int handle, nsecs_t samplingPeriodNs);
+
// Dumpable
virtual std::string dump() const override;
virtual void dump(util::ProtoOutputStream* proto) const override;
diff --git a/services/sensorservice/SensorDeviceUtils.cpp b/services/sensorservice/SensorDeviceUtils.cpp
index 52213cf..5aa283e 100644
--- a/services/sensorservice/SensorDeviceUtils.cpp
+++ b/services/sensorservice/SensorDeviceUtils.cpp
@@ -31,7 +31,6 @@
namespace SensorDeviceUtils {
void quantizeSensorEventValues(sensors_event_t *event, float resolution) {
- LOG_FATAL_IF(resolution == 0, "Resolution must be specified for all sensors!");
if (resolution == 0) {
return;
}
@@ -79,8 +78,26 @@
}
}
-float defaultResolutionForType(int type) {
- switch ((SensorTypeV2_1)type) {
+float resolutionForSensor(const sensor_t &sensor) {
+ switch ((SensorTypeV2_1)sensor.type) {
+ case SensorTypeV2_1::ACCELEROMETER:
+ case SensorTypeV2_1::MAGNETIC_FIELD:
+ case SensorTypeV2_1::GYROSCOPE:
+ case SensorTypeV2_1::MAGNETIC_FIELD_UNCALIBRATED:
+ case SensorTypeV2_1::GYROSCOPE_UNCALIBRATED:
+ case SensorTypeV2_1::ACCELEROMETER_UNCALIBRATED: {
+ if (sensor.maxRange == 0) {
+ ALOGE("No max range for sensor type %d, can't determine appropriate resolution",
+ sensor.type);
+ return sensor.resolution;
+ }
+ // Accel, gyro, and mag shouldn't have more than 24 bits of resolution on the most
+ // advanced devices.
+ double lowerBound = 2.0 * sensor.maxRange / std::pow(2, 24);
+
+ // No need to check the upper bound as that's already enforced through CTS.
+ return std::max(sensor.resolution, static_cast<float>(lowerBound));
+ }
case SensorTypeV2_1::SIGNIFICANT_MOTION:
case SensorTypeV2_1::STEP_DETECTOR:
case SensorTypeV2_1::STEP_COUNTER:
@@ -91,12 +108,14 @@
case SensorTypeV2_1::WRIST_TILT_GESTURE:
case SensorTypeV2_1::STATIONARY_DETECT:
case SensorTypeV2_1::MOTION_DETECT:
+ // Ignore input resolution as all of these sensors are required to have a resolution of
+ // 1.
return 1.0f;
default:
- // fall through and return 0 for all other types
+ // fall through and return the current resolution for all other types
break;
}
- return 0.0f;
+ return sensor.resolution;
}
HidlServiceRegistrationWaiter::HidlServiceRegistrationWaiter() {
diff --git a/services/sensorservice/SensorDeviceUtils.h b/services/sensorservice/SensorDeviceUtils.h
index c232f0b..1309971 100644
--- a/services/sensorservice/SensorDeviceUtils.h
+++ b/services/sensorservice/SensorDeviceUtils.h
@@ -19,6 +19,7 @@
#include <android/hidl/manager/1.0/IServiceNotification.h>
#include <hardware/sensors.h>
+#include <utils/Log.h>
#include <cmath>
#include <condition_variable>
@@ -33,6 +34,10 @@
// Quantizes a single value using a sensor's resolution.
inline void quantizeValue(float *value, double resolution) {
+ if (resolution == 0) {
+ return;
+ }
+
// Increase the value of the sensor's nominal resolution to ensure that
// sensor accuracy improvements, like runtime calibration, are not masked
// during requantization.
@@ -43,8 +48,8 @@
// Ensures a sensor event doesn't provide values finer grained than its sensor resolution allows.
void quantizeSensorEventValues(sensors_event_t *event, float resolution);
-// Provides a default resolution for simple sensor types if one wasn't provided by the HAL.
-float defaultResolutionForType(int type);
+// Returns the expected resolution value for the given sensor
+float resolutionForSensor(const sensor_t &sensor);
class HidlServiceRegistrationWaiter : public IServiceNotification {
public:
diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp
index e4c33da..af86d09 100644
--- a/services/sensorservice/SensorDirectConnection.cpp
+++ b/services/sensorservice/SensorDirectConnection.cpp
@@ -32,6 +32,8 @@
: mService(service), mUid(uid), mMem(*mem),
mHalChannelHandle(halChannelHandle),
mOpPackageName(opPackageName), mDestroyed(false) {
+ mIsRateCappedBasedOnPermission = mService->isRateCappedBasedOnPermission(mOpPackageName);
+ mUserId = multiuser_get_user_id(mUid);
ALOGD_IF(DEBUG_CONNECTIONS, "Created SensorDirectConnection");
}
@@ -101,6 +103,14 @@
}
}
+void SensorService::SensorDirectConnection::onMicSensorAccessChanged(bool isMicToggleOn) {
+ if (isMicToggleOn) {
+ capRates();
+ } else {
+ uncapRates();
+ }
+}
+
bool SensorService::SensorDirectConnection::hasSensorAccess() const {
return mService->hasSensorAccess(mUid, mOpPackageName);
}
@@ -134,6 +144,7 @@
if (handle == -1 && rateLevel == SENSOR_DIRECT_RATE_STOP) {
stopAll();
+ mMicRateBackup.clear();
return NO_ERROR;
}
@@ -157,6 +168,14 @@
return INVALID_OPERATION;
}
+ int requestedRateLevel = rateLevel;
+ if (mService->isSensorInCappedSet(s.getType()) && rateLevel != SENSOR_DIRECT_RATE_STOP) {
+ status_t err = mService->adjustRateLevelBasedOnMicAndPermission(&rateLevel, mOpPackageName);
+ if (err != OK) {
+ return err;
+ }
+ }
+
struct sensors_direct_cfg_t config = {
.rate_level = rateLevel
};
@@ -168,18 +187,100 @@
if (rateLevel == SENSOR_DIRECT_RATE_STOP) {
if (ret == NO_ERROR) {
mActivated.erase(handle);
+ mMicRateBackup.erase(handle);
} else if (ret > 0) {
ret = UNKNOWN_ERROR;
}
} else {
if (ret > 0) {
mActivated[handle] = rateLevel;
+ if (mService->isSensorInCappedSet(s.getType())) {
+ // Back up the rates that the app is allowed to have if the mic toggle is off
+ // This is used in the uncapRates() function.
+ if (!mIsRateCappedBasedOnPermission ||
+ requestedRateLevel <= SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL) {
+ mMicRateBackup[handle] = requestedRateLevel;
+ } else {
+ mMicRateBackup[handle] = SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL;
+ }
+ }
}
}
return ret;
}
+void SensorService::SensorDirectConnection::capRates() {
+ Mutex::Autolock _l(mConnectionLock);
+ const struct sensors_direct_cfg_t capConfig = {
+ .rate_level = SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL
+ };
+
+ const struct sensors_direct_cfg_t stopConfig = {
+ .rate_level = SENSOR_DIRECT_RATE_STOP
+ };
+
+ // If our requests are in the backup, then we shouldn't activate sensors from here
+ bool temporarilyStopped = mActivated.empty() && !mActivatedBackup.empty();
+ std::unordered_map<int, int>& existingConnections =
+ (!temporarilyStopped) ? mActivated : mActivatedBackup;
+
+ SensorDevice& dev(SensorDevice::getInstance());
+ for (auto &i : existingConnections) {
+ int handle = i.first;
+ int rateLevel = i.second;
+ sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+ if (si != nullptr) {
+ const Sensor& s = si->getSensor();
+ if (mService->isSensorInCappedSet(s.getType()) &&
+ rateLevel > SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL) {
+ mMicRateBackup[handle] = rateLevel;
+ // Modify the rate kept by the existing map
+ existingConnections[handle] = SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL;
+ // Only reconfigure the channel if it's ongoing
+ if (!temporarilyStopped) {
+ // Stopping before reconfiguring is the well-tested path in CTS
+ dev.configureDirectChannel(handle, getHalChannelHandle(), &stopConfig);
+ dev.configureDirectChannel(handle, getHalChannelHandle(), &capConfig);
+ }
+ }
+ }
+ }
+}
+
+void SensorService::SensorDirectConnection::uncapRates() {
+ Mutex::Autolock _l(mConnectionLock);
+
+ // If our requests are in the backup, then we shouldn't activate sensors from here
+ bool temporarilyStopped = mActivated.empty() && !mActivatedBackup.empty();
+ std::unordered_map<int, int>& existingConnections =
+ (!temporarilyStopped) ? mActivated : mActivatedBackup;
+
+ const struct sensors_direct_cfg_t stopConfig = {
+ .rate_level = SENSOR_DIRECT_RATE_STOP
+ };
+ SensorDevice& dev(SensorDevice::getInstance());
+ for (auto &i : mMicRateBackup) {
+ int handle = i.first;
+ int rateLevel = i.second;
+
+ const struct sensors_direct_cfg_t config = {
+ .rate_level = rateLevel
+ };
+
+ // Modify the rate kept by the existing map
+ existingConnections[handle] = rateLevel;
+
+ // Only reconfigure the channel if it's ongoing
+ if (!temporarilyStopped) {
+ // Stopping before reconfiguring is the well-tested path in CTS
+ dev.configureDirectChannel(handle, getHalChannelHandle(), &stopConfig);
+ dev.configureDirectChannel(handle, getHalChannelHandle(), &config);
+ }
+ }
+ mMicRateBackup.clear();
+}
+
void SensorService::SensorDirectConnection::stopAll(bool backupRecord) {
Mutex::Autolock _l(mConnectionLock);
stopAllLocked(backupRecord);
diff --git a/services/sensorservice/SensorDirectConnection.h b/services/sensorservice/SensorDirectConnection.h
index 4181b65..a3f348b 100644
--- a/services/sensorservice/SensorDirectConnection.h
+++ b/services/sensorservice/SensorDirectConnection.h
@@ -50,6 +50,8 @@
// regained due to changes in the sensor restricted/privacy mode or the
// app changed to idle/active status.
void onSensorAccessChanged(bool hasAccess);
+ void onMicSensorAccessChanged(bool isMicToggleOn);
+ userid_t getUserId() const { return mUserId; }
protected:
virtual ~SensorDirectConnection();
@@ -82,6 +84,11 @@
// If no requests are backed up by stopAll(), this method is no-op.
void recoverAll();
+ // Limits all active sensor direct report requests when the mic toggle is flipped to on.
+ void capRates();
+ // Recover sensor requests previously capped by capRates().
+ void uncapRates();
+
const sp<SensorService> mService;
const uid_t mUid;
const sensors_direct_mem_t mMem;
@@ -91,9 +98,12 @@
mutable Mutex mConnectionLock;
std::unordered_map<int, int> mActivated;
std::unordered_map<int, int> mActivatedBackup;
+ std::unordered_map<int, int> mMicRateBackup;
+ std::atomic_bool mIsRateCappedBasedOnPermission;
mutable Mutex mDestroyLock;
bool mDestroyed;
+ userid_t mUserId;
};
} // namepsace android
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index 3cccaf9..9ce8d9b 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -38,12 +38,14 @@
SensorService::SensorEventConnection::SensorEventConnection(
const sp<SensorService>& service, uid_t uid, String8 packageName, bool isDataInjectionMode,
- const String16& opPackageName)
+ const String16& opPackageName, const String16& attributionTag)
: mService(service), mUid(uid), mWakeLockRefCount(0), mHasLooperCallbacks(false),
mDead(false), mDataInjectionMode(isDataInjectionMode), mEventCache(nullptr),
mCacheSize(0), mMaxCacheSize(0), mTimeOfLastEventDrop(0), mEventsDropped(0),
- mPackageName(packageName), mOpPackageName(opPackageName), mTargetSdk(kTargetSdkUnknown),
- mDestroyed(false) {
+ mPackageName(packageName), mOpPackageName(opPackageName), mAttributionTag(attributionTag),
+ mTargetSdk(kTargetSdkUnknown), mDestroyed(false) {
+ mIsRateCappedBasedOnPermission = mService->isRateCappedBasedOnPermission(mOpPackageName);
+ mUserId = multiuser_get_user_id(mUid);
mChannel = new BitTube(mService->mSocketBufferSize);
#if DEBUG_CONNECTIONS
mEventsReceived = mEventsSentFromCache = mEventsSent = 0;
@@ -161,7 +163,7 @@
Mutex::Autolock _l(mConnectionLock);
sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si == nullptr ||
- !canAccessSensor(si->getSensor(), "Tried adding", mOpPackageName) ||
+ !canAccessSensor(si->getSensor(), "Add to SensorEventConnection: ", mOpPackageName) ||
mSensorInfo.count(handle) > 0) {
return false;
}
@@ -460,8 +462,13 @@
mTargetSdk > 0 && mTargetSdk <= __ANDROID_API_P__) {
success = true;
} else {
+ int32_t sensorHandle = event.sensor;
+ String16 noteMsg("Sensor event (");
+ noteMsg.append(String16(mService->getSensorStringType(sensorHandle)));
+ noteMsg.append(String16(")"));
int32_t appOpMode = mService->sAppOpsManager.noteOp(iter->second, mUid,
- mOpPackageName);
+ mOpPackageName, mAttributionTag,
+ noteMsg);
success = (appOpMode == AppOpsManager::MODE_ALLOWED);
}
}
@@ -680,24 +687,118 @@
status_t err;
if (enabled) {
+ nsecs_t requestedSamplingPeriodNs = samplingPeriodNs;
+ bool isSensorCapped = false;
+ sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+ if (si != nullptr) {
+ const Sensor& s = si->getSensor();
+ if (mService->isSensorInCappedSet(s.getType())) {
+ isSensorCapped = true;
+ }
+ }
+ if (isSensorCapped) {
+ err = mService->adjustSamplingPeriodBasedOnMicAndPermission(&samplingPeriodNs,
+ String16(mOpPackageName));
+ if (err != OK) {
+ return err;
+ }
+ }
err = mService->enable(this, handle, samplingPeriodNs, maxBatchReportLatencyNs,
reservedFlags, mOpPackageName);
+ if (err == OK && isSensorCapped) {
+ if (!mIsRateCappedBasedOnPermission ||
+ requestedSamplingPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) {
+ mMicSamplingPeriodBackup[handle] = requestedSamplingPeriodNs;
+ } else {
+ mMicSamplingPeriodBackup[handle] = SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS;
+ }
+ }
} else {
err = mService->disable(this, handle);
+ mMicSamplingPeriodBackup.erase(handle);
}
return err;
}
-status_t SensorService::SensorEventConnection::setEventRate(
- int handle, nsecs_t samplingPeriodNs)
-{
+status_t SensorService::SensorEventConnection::setEventRate(int handle, nsecs_t samplingPeriodNs) {
if (mDestroyed) {
android_errorWriteLog(0x534e4554, "168211968");
return DEAD_OBJECT;
}
- return mService->setEventRate(this, handle, samplingPeriodNs, mOpPackageName);
+ nsecs_t requestedSamplingPeriodNs = samplingPeriodNs;
+ bool isSensorCapped = false;
+ sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+ if (si != nullptr) {
+ const Sensor& s = si->getSensor();
+ if (mService->isSensorInCappedSet(s.getType())) {
+ isSensorCapped = true;
+ }
+ }
+ if (isSensorCapped) {
+ status_t err = mService->adjustSamplingPeriodBasedOnMicAndPermission(&samplingPeriodNs,
+ String16(mOpPackageName));
+ if (err != OK) {
+ return err;
+ }
+ }
+ status_t ret = mService->setEventRate(this, handle, samplingPeriodNs, mOpPackageName);
+ if (ret == OK && isSensorCapped) {
+ if (!mIsRateCappedBasedOnPermission ||
+ requestedSamplingPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) {
+ mMicSamplingPeriodBackup[handle] = requestedSamplingPeriodNs;
+ } else {
+ mMicSamplingPeriodBackup[handle] = SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS;
+ }
+ }
+ return ret;
+}
+
+void SensorService::SensorEventConnection::onMicSensorAccessChanged(bool isMicToggleOn) {
+ if (isMicToggleOn) {
+ capRates();
+ } else {
+ uncapRates();
+ }
+}
+
+void SensorService::SensorEventConnection::capRates() {
+ Mutex::Autolock _l(mConnectionLock);
+ SensorDevice& dev(SensorDevice::getInstance());
+ for (auto &i : mMicSamplingPeriodBackup) {
+ int handle = i.first;
+ nsecs_t samplingPeriodNs = i.second;
+ if (samplingPeriodNs < SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) {
+ if (hasSensorAccess()) {
+ mService->setEventRate(this, handle, SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS,
+ mOpPackageName);
+ } else {
+ // Update SensorDevice with the capped rate so that when sensor access is restored,
+ // the correct event rate is used.
+ dev.onMicSensorAccessChanged(this, handle,
+ SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS);
+ }
+ }
+ }
+}
+
+void SensorService::SensorEventConnection::uncapRates() {
+ Mutex::Autolock _l(mConnectionLock);
+ SensorDevice& dev(SensorDevice::getInstance());
+ for (auto &i : mMicSamplingPeriodBackup) {
+ int handle = i.first;
+ nsecs_t samplingPeriodNs = i.second;
+ if (samplingPeriodNs < SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) {
+ if (hasSensorAccess()) {
+ mService->setEventRate(this, handle, samplingPeriodNs, mOpPackageName);
+ } else {
+ // Update SensorDevice with the uncapped rate so that when sensor access is
+ // restored, the correct event rate is used.
+ dev.onMicSensorAccessChanged(this, handle, samplingPeriodNs);
+ }
+ }
+ }
}
status_t SensorService::SensorEventConnection::flush() {
diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h
index 9487a39..909053b 100644
--- a/services/sensorservice/SensorEventConnection.h
+++ b/services/sensorservice/SensorEventConnection.h
@@ -50,7 +50,8 @@
public:
SensorEventConnection(const sp<SensorService>& service, uid_t uid, String8 packageName,
- bool isDataInjectionMode, const String16& opPackageName);
+ bool isDataInjectionMode, const String16& opPackageName,
+ const String16& attributionTag);
status_t sendEvents(sensors_event_t const* buffer, size_t count, sensors_event_t* scratch,
wp<const SensorEventConnection> const * mapFlushEventsToConnections = nullptr);
@@ -68,6 +69,9 @@
String8 getPackageName() const;
uid_t getUid() const { return mUid; }
+ // cap/uncap existing connection depending on the state of the mic toggle.
+ void onMicSensorAccessChanged(bool isMicToggleOn);
+ userid_t getUserId() const { return mUserId; }
private:
virtual ~SensorEventConnection();
@@ -137,10 +141,14 @@
// Call noteOp for the sensor if the sensor requires a permission
bool noteOpIfRequired(const sensors_event_t& event);
-
+ // Limits all active connections when the mic toggle is flipped to on.
+ void capRates();
+ // Recover sensor connection previously capped by capRates().
+ void uncapRates();
sp<SensorService> const mService;
sp<BitTube> mChannel;
uid_t mUid;
+ std::atomic_bool mIsRateCappedBasedOnPermission;
mutable Mutex mConnectionLock;
// Number of events from wake up sensors which are still pending and haven't been delivered to
// the corresponding application. It is incremented by one unit for each write to the socket.
@@ -177,6 +185,7 @@
int mEventsDropped;
String8 mPackageName;
const String16 mOpPackageName;
+ const String16 mAttributionTag;
int mTargetSdk;
#if DEBUG_CONNECTIONS
int mEventsReceived, mEventsSent, mEventsSentFromCache;
@@ -189,6 +198,9 @@
// Store a mapping of sensor handles to required AppOp for a sensor. This map only contains a
// valid mapping for sensors that require a permission in order to reduce the lookup time.
std::unordered_map<int32_t, int32_t> mHandleToAppOp;
+ // Mapping of sensor handles to its rate before being capped by the mic toggle.
+ std::unordered_map<int, nsecs_t> mMicSamplingPeriodBackup;
+ userid_t mUserId;
};
} // namepsace android
diff --git a/services/sensorservice/SensorList.cpp b/services/sensorservice/SensorList.cpp
index 0ce32cc..85ce0f0 100644
--- a/services/sensorservice/SensorList.cpp
+++ b/services/sensorservice/SensorList.cpp
@@ -57,6 +57,12 @@
mNonSensor.getName());
}
+String8 SensorList::getStringType(int handle) const {
+ return getOne<String8>(
+ handle, [] (const Entry& e) -> String8 {return e.si->getSensor().getStringType();},
+ mNonSensor.getStringType());
+}
+
sp<SensorInterface> SensorList::getInterface(int handle) const {
return getOne<sp<SensorInterface>>(
handle, [] (const Entry& e) -> sp<SensorInterface> {return e.si;}, nullptr);
diff --git a/services/sensorservice/SensorList.h b/services/sensorservice/SensorList.h
index 8424b22..617ceef 100644
--- a/services/sensorservice/SensorList.h
+++ b/services/sensorservice/SensorList.h
@@ -53,6 +53,8 @@
const Vector<Sensor> getVirtualSensors() const;
String8 getName(int handle) const;
+ String8 getStringType(int handle) const;
+
sp<SensorInterface> getInterface(int handle) const;
bool isNewHandle(int handle) const;
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 3ca34bb..9955cdb 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#include <android-base/strings.h>
#include <android/content/pm/IPackageManagerNative.h>
#include <android/util/ProtoOutputStream.h>
#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
@@ -88,6 +89,8 @@
#define SENSOR_SERVICE_SCHED_FIFO_PRIORITY 10
// Permissions.
+static const String16 sAccessHighSensorSamplingRatePermission(
+ "android.permission.HIGH_SAMPLING_RATE_SENSORS");
static const String16 sDumpPermission("android.permission.DUMP");
static const String16 sLocationHardwarePermission("android.permission.LOCATION_HARDWARE");
static const String16 sManageSensorsPermission("android.permission.MANAGE_SENSORS");
@@ -366,6 +369,9 @@
}
mUidPolicy->unregisterSelf();
mSensorPrivacyPolicy->unregisterSelf();
+ for (auto const& [userId, policy] : mMicSensorPrivacyPolicies) {
+ policy->unregisterSelf();
+ }
}
status_t SensorService::dump(int fd, const Vector<String16>& args) {
@@ -695,12 +701,44 @@
}
}
+void SensorService::capRates(userid_t userId) {
+ ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
+ for (const sp<SensorDirectConnection>& conn : connLock.getDirectConnections()) {
+ if (conn->getUserId() == userId) {
+ conn->onMicSensorAccessChanged(true);
+ }
+ }
+
+ for (const sp<SensorEventConnection>& conn : connLock.getActiveConnections()) {
+ if (conn->getUserId() == userId) {
+ conn->onMicSensorAccessChanged(true);
+ }
+ }
+}
+
+void SensorService::uncapRates(userid_t userId) {
+ ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
+ for (const sp<SensorDirectConnection>& conn : connLock.getDirectConnections()) {
+ if (conn->getUserId() == userId) {
+ conn->onMicSensorAccessChanged(false);
+ }
+ }
+
+ for (const sp<SensorEventConnection>& conn : connLock.getActiveConnections()) {
+ if (conn->getUserId() == userId) {
+ conn->onMicSensorAccessChanged(false);
+ }
+ }
+}
// NOTE: This is a remote API - make sure all args are validated
status_t SensorService::shellCommand(int in, int out, int err, Vector<String16>& args) {
if (!checkCallingPermission(sManageSensorsPermission, nullptr, nullptr)) {
return PERMISSION_DENIED;
}
+ if (args.size() == 0) {
+ return BAD_INDEX;
+ }
if (in == BAD_TYPE || out == BAD_TYPE || err == BAD_TYPE) {
return BAD_VALUE;
}
@@ -1109,6 +1147,10 @@
return mSensors.getName(handle);
}
+String8 SensorService::getSensorStringType(int handle) const {
+ return mSensors.getStringType(handle);
+}
+
bool SensorService::isVirtualSensor(int handle) const {
sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
return sensor != nullptr && sensor->isVirtual();
@@ -1202,14 +1244,20 @@
}
}
-Vector<Sensor> SensorService::getSensorList(const String16& /* opPackageName */) {
+Vector<Sensor> SensorService::getSensorList(const String16& opPackageName) {
char value[PROPERTY_VALUE_MAX];
property_get("debug.sensors", value, "0");
const Vector<Sensor>& initialSensorList = (atoi(value)) ?
mSensors.getUserDebugSensors() : mSensors.getUserSensors();
Vector<Sensor> accessibleSensorList;
+
+ bool isCapped = isRateCappedBasedOnPermission(opPackageName);
for (size_t i = 0; i < initialSensorList.size(); i++) {
Sensor sensor = initialSensorList[i];
+ if (isCapped && isSensorInCappedSet(sensor.getType())) {
+ sensor.capMinDelayMicros(SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS / 1000);
+ sensor.capHighestDirectReportRateLevel(SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL);
+ }
accessibleSensorList.add(sensor);
}
makeUuidsIntoIdsForSensorList(accessibleSensorList);
@@ -1237,7 +1285,7 @@
}
sp<ISensorEventConnection> SensorService::createSensorEventConnection(const String8& packageName,
- int requestedMode, const String16& opPackageName) {
+ int requestedMode, const String16& opPackageName, const String16& attributionTag) {
// Only 2 modes supported for a SensorEventConnection ... NORMAL and DATA_INJECTION.
if (requestedMode != NORMAL && requestedMode != DATA_INJECTION) {
return nullptr;
@@ -1259,7 +1307,7 @@
String16 connOpPackageName =
(opPackageName == String16("")) ? String16(connPackageName) : opPackageName;
sp<SensorEventConnection> result(new SensorEventConnection(this, uid, connPackageName,
- requestedMode == DATA_INJECTION, connOpPackageName));
+ requestedMode == DATA_INJECTION, connOpPackageName, attributionTag));
if (requestedMode == DATA_INJECTION) {
mConnectionHolder.addEventConnectionIfNotPresent(result);
// Add the associated file descriptor to the Looper for polling whenever there is data to
@@ -1804,9 +1852,6 @@
}
const int32_t opCode = sensor.getRequiredAppOp();
- const int32_t appOpMode = sAppOpsManager.checkOp(opCode,
- IPCThreadState::self()->getCallingUid(), opPackageName);
- bool appOpAllowed = appOpMode == AppOpsManager::MODE_ALLOWED;
int targetSdkVersion = getTargetSdkVersion(opPackageName);
bool canAccess = false;
@@ -1819,14 +1864,16 @@
canAccess = true;
} else if (hasPermissionForSensor(sensor)) {
// Ensure that the AppOp is allowed, or that there is no necessary app op for the sensor
- if (opCode < 0 || appOpAllowed) {
+ if (opCode >= 0) {
+ const int32_t appOpMode = sAppOpsManager.checkOp(opCode,
+ IPCThreadState::self()->getCallingUid(), opPackageName);
+ canAccess = (appOpMode == AppOpsManager::MODE_ALLOWED);
+ } else {
canAccess = true;
}
}
- if (canAccess) {
- sAppOpsManager.noteOp(opCode, IPCThreadState::self()->getCallingUid(), opPackageName);
- } else {
+ if (!canAccess) {
ALOGE("%s %s a sensor (%s) without holding %s", String8(opPackageName).string(),
operation, sensor.getName().string(), sensor.getRequiredPermission().string());
}
@@ -2018,13 +2065,78 @@
return mUidPolicy->isUidActive(uid);
}
+bool SensorService::isRateCappedBasedOnPermission(const String16& opPackageName) {
+ int targetSdk = getTargetSdkVersion(opPackageName);
+ bool hasSamplingRatePermission = PermissionCache::checkCallingPermission(
+ sAccessHighSensorSamplingRatePermission);
+ if (targetSdk < __ANDROID_API_S__ ||
+ (targetSdk >= __ANDROID_API_S__ && hasSamplingRatePermission)) {
+ return false;
+ }
+ return true;
+}
+
+bool SensorService::isSensorInCappedSet(int sensorType) {
+ return (sensorType == SENSOR_TYPE_ACCELEROMETER
+ || sensorType == SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED
+ || sensorType == SENSOR_TYPE_GYROSCOPE
+ || sensorType == SENSOR_TYPE_GYROSCOPE_UNCALIBRATED
+ || sensorType == SENSOR_TYPE_MAGNETIC_FIELD
+ || sensorType == SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED);
+}
+
+status_t SensorService::adjustSamplingPeriodBasedOnMicAndPermission(nsecs_t* requestedPeriodNs,
+ const String16& opPackageName) {
+ uid_t uid = IPCThreadState::self()->getCallingUid();
+ bool shouldCapBasedOnPermission = isRateCappedBasedOnPermission(opPackageName);
+ if (*requestedPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) {
+ return OK;
+ }
+ if (shouldCapBasedOnPermission) {
+ *requestedPeriodNs = SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS;
+ if (isPackageDebuggable(opPackageName)) {
+ return PERMISSION_DENIED;
+ }
+ return OK;
+ }
+ if (isMicSensorPrivacyEnabledForUid(uid)) {
+ *requestedPeriodNs = SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS;
+ return OK;
+ }
+ return OK;
+}
+
+status_t SensorService::adjustRateLevelBasedOnMicAndPermission(int* requestedRateLevel,
+ const String16& opPackageName) {
+ uid_t uid = IPCThreadState::self()->getCallingUid();
+ bool shouldCapBasedOnPermission = isRateCappedBasedOnPermission(opPackageName);
+
+ if (*requestedRateLevel <= SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL) {
+ return OK;
+ }
+ if (shouldCapBasedOnPermission) {
+ *requestedRateLevel = SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL;
+ if (isPackageDebuggable(opPackageName)) {
+ return PERMISSION_DENIED;
+ }
+ return OK;
+ }
+ if (isMicSensorPrivacyEnabledForUid(uid)) {
+ *requestedRateLevel = SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL;
+ return OK;
+ }
+ return OK;
+}
+
void SensorService::SensorPrivacyPolicy::registerSelf() {
+ AutoCallerClear acc;
SensorPrivacyManager spm;
mSensorPrivacyEnabled = spm.isSensorPrivacyEnabled();
spm.addSensorPrivacyListener(this);
}
void SensorService::SensorPrivacyPolicy::unregisterSelf() {
+ AutoCallerClear acc;
SensorPrivacyManager spm;
spm.removeSensorPrivacyListener(this);
}
@@ -2036,16 +2148,56 @@
binder::Status SensorService::SensorPrivacyPolicy::onSensorPrivacyChanged(bool enabled) {
mSensorPrivacyEnabled = enabled;
sp<SensorService> service = mService.promote();
+
if (service != nullptr) {
- if (enabled) {
- service->disableAllSensors();
+ if (mIsIndividualMic) {
+ if (enabled) {
+ service->capRates(mUserId);
+ } else {
+ service->uncapRates(mUserId);
+ }
} else {
- service->enableAllSensors();
+ if (enabled) {
+ service->disableAllSensors();
+ } else {
+ service->enableAllSensors();
+ }
}
}
return binder::Status::ok();
}
+status_t SensorService::SensorPrivacyPolicy::registerSelfForIndividual(int userId) {
+ Mutex::Autolock _l(mSensorPrivacyLock);
+ AutoCallerClear acc;
+ SensorPrivacyManager spm;
+ status_t err = spm.addIndividualSensorPrivacyListener(userId,
+ SensorPrivacyManager::INDIVIDUAL_SENSOR_MICROPHONE, this);
+
+ if (err != OK) {
+ ALOGE("Cannot register a mic listener.");
+ return err;
+ }
+ mSensorPrivacyEnabled = spm.isIndividualSensorPrivacyEnabled(userId,
+ SensorPrivacyManager::INDIVIDUAL_SENSOR_MICROPHONE);
+
+ mIsIndividualMic = true;
+ mUserId = userId;
+ return OK;
+}
+
+bool SensorService::isMicSensorPrivacyEnabledForUid(uid_t uid) {
+ userid_t userId = multiuser_get_user_id(uid);
+ if (mMicSensorPrivacyPolicies.find(userId) == mMicSensorPrivacyPolicies.end()) {
+ sp<SensorPrivacyPolicy> userPolicy = new SensorPrivacyPolicy(this);
+ if (userPolicy->registerSelfForIndividual(userId) != OK) {
+ return false;
+ }
+ mMicSensorPrivacyPolicies[userId] = userPolicy;
+ }
+ return mMicSensorPrivacyPolicies[userId]->isSensorPrivacyEnabled();
+}
+
SensorService::ConnectionSafeAutolock::ConnectionSafeAutolock(
SensorService::SensorConnectionHolder& holder, Mutex& mutex)
: mConnectionHolder(holder), mAutolock(mutex) {}
@@ -2103,4 +2255,17 @@
return ConnectionSafeAutolock(*this, mutex);
}
+bool SensorService::isPackageDebuggable(const String16& opPackageName) {
+ bool debugMode = false;
+ sp<IBinder> binder = defaultServiceManager()->getService(String16("package_native"));
+ if (binder != nullptr) {
+ sp<content::pm::IPackageManagerNative> packageManager =
+ interface_cast<content::pm::IPackageManagerNative>(binder);
+ if (packageManager != nullptr) {
+ binder::Status status = packageManager->isPackageDebuggable(
+ opPackageName, &debugMode);
+ }
+ }
+ return debugMode;
+}
} // namespace android
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 052cbfe..a563a60 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -61,6 +61,15 @@
#define SENSOR_REGISTRATIONS_BUF_SIZE 200
+// Apps that targets S+ and do not have HIGH_SAMPLING_RATE_SENSORS permission will be capped
+// at 200 Hz. The cap also applies to all requests when the mic toggle is flipped to on, regardless
+// of their target SDKs and permission.
+// Capped sampling periods for apps that have non-direct sensor connections.
+#define SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS (5 * 1000 * 1000)
+// Capped sampling rate level for apps that have direct sensor connections.
+// The enum SENSOR_DIRECT_RATE_NORMAL corresponds to a rate value of at most 110 Hz.
+#define SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL SENSOR_DIRECT_RATE_NORMAL
+
namespace android {
// ---------------------------------------------------------------------------
class SensorInterface;
@@ -95,6 +104,8 @@
status_t flushSensor(const sp<SensorEventConnection>& connection,
const String16& opPackageName);
+ // Returns true if a sensor should be throttled according to our rate-throttling rules.
+ static bool isSensorInCappedSet(int sensorType);
virtual status_t shellCommand(int in, int out, int err, Vector<String16>& args);
@@ -212,17 +223,39 @@
// connections will be allowed again.
class SensorPrivacyPolicy : public hardware::BnSensorPrivacyListener {
public:
- explicit SensorPrivacyPolicy(wp<SensorService> service) : mService(service) {}
+ explicit SensorPrivacyPolicy(wp<SensorService> service)
+ : mService(service), mIsIndividualMic(false), mUserId(0) {}
void registerSelf();
void unregisterSelf();
+ status_t registerSelfForIndividual(int userId);
+
bool isSensorPrivacyEnabled();
binder::Status onSensorPrivacyChanged(bool enabled);
private:
wp<SensorService> mService;
+ Mutex mSensorPrivacyLock;
std::atomic_bool mSensorPrivacyEnabled;
+ bool mIsIndividualMic;
+ userid_t mUserId;
+ };
+
+ // A class automatically clearing and restoring binder caller identity inside
+ // a code block (scoped variable).
+ // Declare one systematically before calling SensorPrivacyManager methods so that they are
+ // executed with the same level of privilege as the SensorService process.
+ class AutoCallerClear {
+ public:
+ AutoCallerClear() :
+ mToken(IPCThreadState::self()->clearCallingIdentity()) {}
+ ~AutoCallerClear() {
+ IPCThreadState::self()->restoreCallingIdentity(mToken);
+ }
+
+ private:
+ const int64_t mToken;
};
enum Mode {
@@ -286,7 +319,7 @@
virtual Vector<Sensor> getDynamicSensorList(const String16& opPackageName);
virtual sp<ISensorEventConnection> createSensorEventConnection(
const String8& packageName,
- int requestedMode, const String16& opPackageName);
+ int requestedMode, const String16& opPackageName, const String16& attributionTag);
virtual int isDataInjectionEnabled();
virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName,
uint32_t size, int32_t type, int32_t format, const native_handle *resource);
@@ -295,6 +328,7 @@
virtual status_t dump(int fd, const Vector<String16>& args);
status_t dumpProtoLocked(int fd, ConnectionSafeAutolock* connLock) const;
String8 getSensorName(int handle) const;
+ String8 getSensorStringType(int handle) const;
bool isVirtualSensor(int handle) const;
sp<SensorInterface> getSensorInterfaceFromHandle(int handle) const;
bool isWakeUpSensor(int type) const;
@@ -345,6 +379,13 @@
// whitelisted). mLock must be held to invoke this method.
bool isOperationRestrictedLocked(const String16& opPackageName);
+ status_t adjustSamplingPeriodBasedOnMicAndPermission(nsecs_t* requestedPeriodNs,
+ const String16& opPackageName);
+ status_t adjustRateLevelBasedOnMicAndPermission(int* requestedRateLevel,
+ const String16& opPackageName);
+ bool isRateCappedBasedOnPermission(const String16& opPackageName);
+ bool isPackageDebuggable(const String16& opPackageName);
+
// Reset the state of SensorService to NORMAL mode.
status_t resetToNormalMode();
status_t resetToNormalModeLocked();
@@ -384,6 +425,11 @@
void enableAllSensors();
void enableAllSensorsLocked(ConnectionSafeAutolock* connLock);
+ // Caps active direct connections (when the mic toggle is flipped to on)
+ void capRates(userid_t userId);
+ // Removes the capped rate on active direct connections (when the mic toggle is flipped to off)
+ void uncapRates(userid_t userId);
+
static uint8_t sHmacGlobalKey[128];
static bool sHmacGlobalKeyIsValid;
@@ -425,6 +471,11 @@
static std::map<String16, int> sPackageTargetVersion;
static Mutex sPackageTargetVersionLock;
static String16 sSensorInterfaceDescriptorPrefix;
+
+ // Map from user to SensorPrivacyPolicy
+ std::map<userid_t, sp<SensorPrivacyPolicy>> mMicSensorPrivacyPolicies;
+ // Checks if the mic sensor privacy is enabled for the uid
+ bool isMicSensorPrivacyEnabledForUid(uid_t uid);
};
} // namespace android
diff --git a/services/stats/Android.bp b/services/stats/Android.bp
index 58e5993..a472c5f 100644
--- a/services/stats/Android.bp
+++ b/services/stats/Android.bp
@@ -10,11 +10,14 @@
cc_library_shared {
name: "libstatshidl",
srcs: [
+ "StatsAidl.cpp",
"StatsHal.cpp",
],
cflags: ["-Wall", "-Werror"],
shared_libs: [
"android.frameworks.stats@1.0",
+ "android.frameworks.stats-V1-ndk_platform",
+ "libbinder_ndk",
"libhidlbase",
"liblog",
"libstatslog",
@@ -22,7 +25,11 @@
"libutils",
],
export_include_dirs: [
- "include/",
+ "include/",
+ ],
+ export_shared_lib_headers: [
+ "android.frameworks.stats@1.0",
+ "android.frameworks.stats-V1-ndk_platform",
],
local_include_dirs: [
"include/stats",
diff --git a/services/stats/StatsAidl.cpp b/services/stats/StatsAidl.cpp
new file mode 100644
index 0000000..a3b68f1
--- /dev/null
+++ b/services/stats/StatsAidl.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#define DEBUG false // STOPSHIP if true
+#define LOG_TAG "StatsAidl"
+
+#include <log/log.h>
+#include <statslog.h>
+
+#include "StatsAidl.h"
+
+namespace aidl {
+namespace android {
+namespace frameworks {
+namespace stats {
+
+StatsHal::StatsHal() {}
+
+ndk::ScopedAStatus StatsHal::reportVendorAtom(const VendorAtom& vendorAtom) {
+ std::string reverseDomainName = (std::string) vendorAtom.reverseDomainName;
+ if (vendorAtom.atomId < 100000 || vendorAtom.atomId >= 200000) {
+ ALOGE("Atom ID %ld is not a valid vendor atom ID", (long) vendorAtom.atomId);
+ return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ -1, "Not a valid vendor atom ID");
+ }
+ if (reverseDomainName.length() > 50) {
+ ALOGE("Vendor atom reverse domain name %s is too long.", reverseDomainName.c_str());
+ return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ -1, "Vendor atom reverse domain name is too long");
+ }
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, vendorAtom.atomId);
+ AStatsEvent_writeString(event, vendorAtom.reverseDomainName.c_str());
+ for (const auto& atomValue : vendorAtom.values) {
+ switch (atomValue.getTag()) {
+ case VendorAtomValue::intValue:
+ AStatsEvent_writeInt32(event,
+ atomValue.get<VendorAtomValue::intValue>());
+ break;
+ case VendorAtomValue::longValue:
+ AStatsEvent_writeInt64(event,
+ atomValue.get<VendorAtomValue::longValue>());
+ break;
+ case VendorAtomValue::floatValue:
+ AStatsEvent_writeFloat(event,
+ atomValue.get<VendorAtomValue::floatValue>());
+ break;
+ case VendorAtomValue::stringValue:
+ AStatsEvent_writeString(event,
+ atomValue.get<VendorAtomValue::stringValue>().c_str());
+ break;
+ }
+ }
+ AStatsEvent_build(event);
+ const int ret = AStatsEvent_write(event);
+ AStatsEvent_release(event);
+
+ return ret <= 0 ?
+ ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(ret, "report atom failed") :
+ ndk::ScopedAStatus::ok();
+}
+
+} // namespace stats
+} // namespace frameworks
+} // namespace android
+} // namespace aidl
diff --git a/services/stats/android.frameworks.stats@1.0-service.xml b/services/stats/android.frameworks.stats@1.0-service.xml
index bb02f66..5fd361c 100644
--- a/services/stats/android.frameworks.stats@1.0-service.xml
+++ b/services/stats/android.frameworks.stats@1.0-service.xml
@@ -8,4 +8,10 @@
<instance>default</instance>
</interface>
</hal>
+
+ <hal format="aidl">
+ <name>android.frameworks.stats</name>
+ <version>1</version>
+ <fqname>IStats/default</fqname>
+ </hal>
</manifest>
diff --git a/services/stats/include/stats/StatsAidl.h b/services/stats/include/stats/StatsAidl.h
new file mode 100644
index 0000000..219e71e
--- /dev/null
+++ b/services/stats/include/stats/StatsAidl.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 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 <aidl/android/frameworks/stats/BnStats.h>
+
+namespace aidl {
+namespace android {
+namespace frameworks {
+namespace stats {
+
+class StatsHal : public BnStats {
+public:
+ StatsHal();
+
+ /**
+ * Binder call to get vendor atom.
+ */
+ virtual ndk::ScopedAStatus reportVendorAtom(
+ const VendorAtom& in_vendorAtom) override;
+};
+
+} // namespace stats
+} // namespace frameworks
+} // namespace android
+} // namespace aidl
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 6b3bf8d..9885352 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -12,6 +12,7 @@
cflags: [
"-Wall",
"-Werror",
+ "-Wextra",
"-Wformat",
"-Wthread-safety",
"-Wunused",
@@ -22,7 +23,10 @@
cc_defaults {
name: "libsurfaceflinger_defaults",
- defaults: ["surfaceflinger_defaults"],
+ defaults: [
+ "surfaceflinger_defaults",
+ "skia_deps",
+ ],
cflags: [
"-DLOG_TAG=\"SurfaceFlinger\"",
"-DGL_GLEXT_PROTOTYPES",
@@ -44,7 +48,6 @@
"android.hardware.power-V1-cpp",
"libbase",
"libbinder",
- "libbufferhubqueue",
"libcutils",
"libEGL",
"libfmq",
@@ -55,10 +58,8 @@
"liblayers_proto",
"liblog",
"libnativewindow",
- "libpdx_default_transport",
"libprocessgroup",
"libprotobuf-cpp-lite",
- "libstatslog",
"libsync",
"libtimestats",
"libui",
@@ -68,6 +69,7 @@
],
static_libs: [
"libcompositionengine",
+ "libframetimeline",
"libperfetto_client_experimental",
"librenderengine",
"libserviceutils",
@@ -148,65 +150,53 @@
"DisplayHardware/HWComposer.cpp",
"DisplayHardware/PowerAdvisor.cpp",
"DisplayHardware/VirtualDisplaySurface.cpp",
+ "DisplayRenderArea.cpp",
"Effects/Daltonizer.cpp",
"EventLog/EventLog.cpp",
+ "FpsReporter.cpp",
"FrameTracer/FrameTracer.cpp",
"FrameTracker.cpp",
+ "HdrLayerInfoReporter.cpp",
"Layer.cpp",
"LayerProtoHelper.cpp",
"LayerRejecter.cpp",
+ "LayerRenderArea.cpp",
"LayerVector.cpp",
"MonitoredProducer.cpp",
"NativeWindowSurface.cpp",
"RefreshRateOverlay.cpp",
"RegionSamplingThread.cpp",
"RenderArea.cpp",
- "Scheduler/DispSync.cpp",
"Scheduler/DispSyncSource.cpp",
- "Scheduler/EventControlThread.cpp",
"Scheduler/EventThread.cpp",
"Scheduler/OneShotTimer.cpp",
"Scheduler/LayerHistory.cpp",
- "Scheduler/LayerHistoryV2.cpp",
"Scheduler/LayerInfo.cpp",
- "Scheduler/LayerInfoV2.cpp",
"Scheduler/MessageQueue.cpp",
- "Scheduler/PhaseOffsets.cpp",
"Scheduler/RefreshRateConfigs.cpp",
"Scheduler/Scheduler.cpp",
"Scheduler/SchedulerUtils.cpp",
"Scheduler/Timer.cpp",
"Scheduler/VSyncDispatchTimerQueue.cpp",
"Scheduler/VSyncPredictor.cpp",
- "Scheduler/VSyncModulator.cpp",
+ "Scheduler/VsyncModulator.cpp",
"Scheduler/VSyncReactor.cpp",
+ "Scheduler/VsyncConfiguration.cpp",
"StartPropertySetThread.cpp",
"SurfaceFlinger.cpp",
"SurfaceFlingerDefaultFactory.cpp",
"SurfaceInterceptor.cpp",
"SurfaceTracing.cpp",
- "TransactionCompletedThread.cpp",
+ "TransactionCallbackInvoker.cpp",
],
}
-cc_library_shared {
- // Please use libsurfaceflinger_defaults to configure how the sources are
- // built, so the same settings can be used elsewhere.
- name: "libsurfaceflinger",
- defaults: ["libsurfaceflinger_production_defaults"],
- srcs: [
- ":libsurfaceflinger_sources",
-
- // Note: SurfaceFlingerFactory is not in the default sources so that it
- // can be easily replaced.
- "SurfaceFlingerFactory.cpp",
- ],
- logtags: ["EventLog/EventLogTags.logtags"],
-}
-
cc_defaults {
name: "libsurfaceflinger_binary",
- defaults: ["surfaceflinger_defaults"],
+ defaults: [
+ "surfaceflinger_defaults",
+ "libsurfaceflinger_production_defaults",
+ ],
cflags: [
"-DLOG_TAG=\"SurfaceFlinger\"",
],
@@ -231,23 +221,31 @@
"libserviceutils",
"libtrace_proto",
],
- ldflags: ["-Wl,--export-dynamic"],
}
filegroup {
name: "surfaceflinger_binary_sources",
- srcs: ["main_surfaceflinger.cpp"],
+ srcs: [
+ ":libsurfaceflinger_sources",
+ "main_surfaceflinger.cpp",
+ ],
}
cc_binary {
name: "surfaceflinger",
defaults: ["libsurfaceflinger_binary"],
init_rc: ["surfaceflinger.rc"],
- srcs: [":surfaceflinger_binary_sources"],
+ srcs: [
+ ":surfaceflinger_binary_sources",
+ // Note: SurfaceFlingerFactory is not in the filegroup so that it
+ // can be easily replaced.
+ "SurfaceFlingerFactory.cpp",
+ ],
shared_libs: [
- "libsurfaceflinger",
"libSurfaceFlingerProp",
],
+
+ logtags: ["EventLog/EventLogTags.logtags"],
}
subdirs = [
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 082356d..4c73b6e 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -185,10 +185,14 @@
return std::nullopt;
}
}
- bool blackOutLayer = (isProtected() && !targetSettings.supportsProtectedContent) ||
+ const bool blackOutLayer = (isProtected() && !targetSettings.supportsProtectedContent) ||
((isSecure() || isProtected()) && !targetSettings.isSecure);
+ const bool bufferCanBeUsedAsHwTexture =
+ mBufferInfo.mBuffer->getBuffer()->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE;
compositionengine::LayerFE::LayerSettings& layer = *result;
- if (blackOutLayer) {
+ if (blackOutLayer || !bufferCanBeUsedAsHwTexture) {
+ ALOGE_IF(!bufferCanBeUsedAsHwTexture, "%s is blacked out as buffer is not gpu readable",
+ mName.c_str());
prepareClearClientComposition(layer, true /* blackout */);
return layer;
}
@@ -209,7 +213,7 @@
? mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel
: defaultMaxContentLuminance;
layer.frameNumber = mCurrentFrameNumber;
- layer.bufferId = mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getId() : 0;
+ layer.bufferId = mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer()->getId() : 0;
const bool useFiltering =
targetSettings.needsFiltering || mNeedsFiltering || bufferNeedsFiltering();
@@ -310,7 +314,7 @@
: Hwc2::IComposerClient::Composition::DEVICE;
}
- compositionState->buffer = mBufferInfo.mBuffer;
+ compositionState->buffer = mBufferInfo.mBuffer->getBuffer();
compositionState->bufferSlot = (mBufferInfo.mBufferSlot == BufferQueue::INVALID_BUFFER_SLOT)
? 0
: mBufferInfo.mBufferSlot;
@@ -325,6 +329,37 @@
mRefreshPending = false;
return hasReadyFrame();
}
+namespace {
+TimeStats::SetFrameRateVote frameRateToSetFrameRateVotePayload(Layer::FrameRate frameRate) {
+ using FrameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility;
+ using Seamlessness = TimeStats::SetFrameRateVote::Seamlessness;
+ const auto frameRateCompatibility = [frameRate] {
+ switch (frameRate.type) {
+ case Layer::FrameRateCompatibility::Default:
+ return FrameRateCompatibility::Default;
+ case Layer::FrameRateCompatibility::ExactOrMultiple:
+ return FrameRateCompatibility::ExactOrMultiple;
+ default:
+ return FrameRateCompatibility::Undefined;
+ }
+ }();
+
+ const auto seamlessness = [frameRate] {
+ switch (frameRate.seamlessness) {
+ case scheduler::Seamlessness::OnlySeamless:
+ return Seamlessness::ShouldBeSeamless;
+ case scheduler::Seamlessness::SeamedAndSeamless:
+ return Seamlessness::NotRequired;
+ default:
+ return Seamlessness::Undefined;
+ }
+ }();
+
+ return TimeStats::SetFrameRateVote{.frameRate = frameRate.rate.getValue(),
+ .frameRateCompatibility = frameRateCompatibility,
+ .seamlessness = seamlessness};
+}
+} // namespace
bool BufferLayer::onPostComposition(const DisplayDevice* display,
const std::shared_ptr<FenceTime>& glDoneFence,
@@ -355,6 +390,13 @@
mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber,
clientCompositionTimestamp,
FrameTracer::FrameEvent::FALLBACK_COMPOSITION);
+ // Update the SurfaceFrames in the drawing state
+ if (mDrawingState.bufferSurfaceFrameTX) {
+ mDrawingState.bufferSurfaceFrameTX->setGpuComposition();
+ }
+ for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) {
+ surfaceFrame->setGpuComposition();
+ }
}
std::shared_ptr<FenceTime> frameReadyFence = mBufferInfo.mFenceTime;
@@ -366,19 +408,27 @@
mFrameTracker.setFrameReadyTime(desiredPresentTime);
}
+ const Fps refreshRate = mFlinger->mRefreshRateConfigs->getCurrentRefreshRate().getFps();
+ const std::optional<Fps> renderRate = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
if (presentFence->isValid()) {
- mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence);
+ mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence,
+ refreshRate, renderRate,
+ frameRateToSetFrameRateVotePayload(
+ mDrawingState.frameRate));
mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber,
presentFence, FrameTracer::FrameEvent::PRESENT_FENCE);
mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
} else if (!display) {
// Do nothing.
- } else if (const auto displayId = display->getId();
+ } else if (const auto displayId = PhysicalDisplayId::tryCast(display->getId());
displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
// The HWC doesn't support present fences, so use the refresh
// timestamp instead.
- const nsecs_t actualPresentTime = mFlinger->getHwComposer().getRefreshTimestamp(*displayId);
- mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime);
+ const nsecs_t actualPresentTime = display->getRefreshTimestamp();
+ mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime,
+ refreshRate, renderRate,
+ frameRateToSetFrameRateVotePayload(
+ mDrawingState.frameRate));
mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber,
actualPresentTime,
FrameTracer::FrameEvent::PRESENT_FENCE);
@@ -392,10 +442,34 @@
void BufferLayer::gatherBufferInfo() {
mBufferInfo.mPixelFormat =
- !mBufferInfo.mBuffer ? PIXEL_FORMAT_NONE : mBufferInfo.mBuffer->format;
+ !mBufferInfo.mBuffer ? PIXEL_FORMAT_NONE : mBufferInfo.mBuffer->getBuffer()->format;
mBufferInfo.mFrameLatencyNeeded = true;
}
+bool BufferLayer::shouldPresentNow(nsecs_t expectedPresentTime) const {
+ // If this is not a valid vsync for the layer's uid, return and try again later
+ const bool isVsyncValidForUid =
+ mFlinger->mScheduler->isVsyncValid(expectedPresentTime, mOwnerUid);
+ if (!isVsyncValidForUid) {
+ ATRACE_NAME("!isVsyncValidForUid");
+ return false;
+ }
+
+ // AutoRefresh layers and sideband streams should always be presented
+ if (getSidebandStreamChanged() || getAutoRefresh()) {
+ return true;
+ }
+
+ // If this layer doesn't have a frame is shouldn't be presented
+ if (!hasFrameUpdate()) {
+ return false;
+ }
+
+ // Defer to the derived class to decide whether the next buffer is due for
+ // presentation.
+ return isBufferDue(expectedPresentTime);
+}
+
bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
nsecs_t expectedPresentTime) {
ATRACE_CALL();
@@ -433,11 +507,6 @@
BufferInfo oldBufferInfo = mBufferInfo;
- if (!allTransactionsSignaled(expectedPresentTime)) {
- mFlinger->setTransactionFlags(eTraversalNeeded);
- return false;
- }
-
status_t err = updateTexImage(recomputeVisibleRegions, latchTime, expectedPresentTime);
if (err != NO_ERROR) {
return false;
@@ -470,10 +539,10 @@
}
if (oldBufferInfo.mBuffer != nullptr) {
- uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
- uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
- if (bufWidth != uint32_t(oldBufferInfo.mBuffer->width) ||
- bufHeight != uint32_t(oldBufferInfo.mBuffer->height)) {
+ uint32_t bufWidth = mBufferInfo.mBuffer->getBuffer()->getWidth();
+ uint32_t bufHeight = mBufferInfo.mBuffer->getBuffer()->getHeight();
+ if (bufWidth != uint32_t(oldBufferInfo.mBuffer->getBuffer()->width) ||
+ bufHeight != uint32_t(oldBufferInfo.mBuffer->getBuffer()->height)) {
recomputeVisibleRegions = true;
}
}
@@ -482,109 +551,20 @@
recomputeVisibleRegions = true;
}
- // Remove any sync points corresponding to the buffer which was just
- // latched
- {
- Mutex::Autolock lock(mLocalSyncPointMutex);
- auto point = mLocalSyncPoints.begin();
- while (point != mLocalSyncPoints.end()) {
- if (!(*point)->frameIsAvailable() || !(*point)->transactionIsApplied()) {
- // This sync point must have been added since we started
- // latching. Don't drop it yet.
- ++point;
- continue;
- }
-
- if ((*point)->getFrameNumber() <= mCurrentFrameNumber) {
- std::stringstream ss;
- ss << "Dropping sync point " << (*point)->getFrameNumber();
- ATRACE_NAME(ss.str().c_str());
- point = mLocalSyncPoints.erase(point);
- } else {
- ++point;
- }
- }
- }
-
return true;
}
-// transaction
-void BufferLayer::notifyAvailableFrames(nsecs_t expectedPresentTime) {
- const auto headFrameNumber = getHeadFrameNumber(expectedPresentTime);
- const bool headFenceSignaled = fenceHasSignaled();
- const bool presentTimeIsCurrent = framePresentTimeIsCurrent(expectedPresentTime);
- Mutex::Autolock lock(mLocalSyncPointMutex);
- for (auto& point : mLocalSyncPoints) {
- if (headFrameNumber >= point->getFrameNumber() && headFenceSignaled &&
- presentTimeIsCurrent) {
- point->setFrameAvailable();
- sp<Layer> requestedSyncLayer = point->getRequestedSyncLayer();
- if (requestedSyncLayer) {
- // Need to update the transaction flag to ensure the layer's pending transaction
- // gets applied.
- requestedSyncLayer->setTransactionFlags(eTransactionNeeded);
- }
- }
- }
-}
-
bool BufferLayer::hasReadyFrame() const {
return hasFrameUpdate() || getSidebandStreamChanged() || getAutoRefresh();
}
uint32_t BufferLayer::getEffectiveScalingMode() const {
- if (mOverrideScalingMode >= 0) {
- return mOverrideScalingMode;
- }
-
return mBufferInfo.mScaleMode;
}
bool BufferLayer::isProtected() const {
- const sp<GraphicBuffer>& buffer(mBufferInfo.mBuffer);
- return (buffer != 0) && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
-}
-
-bool BufferLayer::latchUnsignaledBuffers() {
- static bool propertyLoaded = false;
- static bool latch = false;
- static std::mutex mutex;
- std::lock_guard<std::mutex> lock(mutex);
- if (!propertyLoaded) {
- char value[PROPERTY_VALUE_MAX] = {};
- property_get("debug.sf.latch_unsignaled", value, "0");
- latch = atoi(value);
- propertyLoaded = true;
- }
- return latch;
-}
-
-// h/w composer set-up
-bool BufferLayer::allTransactionsSignaled(nsecs_t expectedPresentTime) {
- const auto headFrameNumber = getHeadFrameNumber(expectedPresentTime);
- bool matchingFramesFound = false;
- bool allTransactionsApplied = true;
- Mutex::Autolock lock(mLocalSyncPointMutex);
-
- for (auto& point : mLocalSyncPoints) {
- if (point->getFrameNumber() > headFrameNumber) {
- break;
- }
- matchingFramesFound = true;
-
- if (!point->frameIsAvailable()) {
- // We haven't notified the remote layer that the frame for
- // this point is available yet. Notify it now, and then
- // abort this attempt to latch.
- point->setFrameAvailable();
- allTransactionsApplied = false;
- break;
- }
-
- allTransactionsApplied = allTransactionsApplied && point->transactionIsApplied();
- }
- return !matchingFramesFound || allTransactionsApplied;
+ return (mBufferInfo.mBuffer != nullptr) &&
+ (mBufferInfo.mBuffer->getBuffer()->getUsage() & GRALLOC_USAGE_PROTECTED);
}
// As documented in libhardware header, formats in the range
@@ -671,8 +651,8 @@
return Rect::INVALID_RECT;
}
- uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
- uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
+ uint32_t bufWidth = mBufferInfo.mBuffer->getBuffer()->getWidth();
+ uint32_t bufHeight = mBufferInfo.mBuffer->getBuffer()->getHeight();
// Undo any transformations on the buffer and return the result.
if (mBufferInfo.mTransform & ui::Transform::ROT_90) {
@@ -703,8 +683,8 @@
return parentBounds;
}
- uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
- uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
+ uint32_t bufWidth = mBufferInfo.mBuffer->getBuffer()->getWidth();
+ uint32_t bufHeight = mBufferInfo.mBuffer->getBuffer()->getHeight();
// Undo any transformations on the buffer and return the result.
if (mBufferInfo.mTransform & ui::Transform::ROT_90) {
@@ -746,7 +726,7 @@
return mBufferInfo.mCrop;
} else if (mBufferInfo.mBuffer != nullptr) {
// otherwise we use the whole buffer
- return mBufferInfo.mBuffer->getBounds();
+ return mBufferInfo.mBuffer->getBuffer()->getBounds();
} else {
// if we don't have a buffer yet, we use an empty/invalid crop
return Rect();
@@ -791,12 +771,14 @@
}
sp<GraphicBuffer> BufferLayer::getBuffer() const {
- return mBufferInfo.mBuffer;
+ return mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer() : nullptr;
}
void BufferLayer::getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]) {
- GLConsumer::computeTransformMatrix(outMatrix, mBufferInfo.mBuffer, mBufferInfo.mCrop,
- mBufferInfo.mTransform, filteringEnabled);
+ GLConsumer::computeTransformMatrix(outMatrix,
+ mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer()
+ : nullptr,
+ mBufferInfo.mCrop, mBufferInfo.mTransform, filteringEnabled);
}
void BufferLayer::setInitialValuesForClone(const sp<Layer>& clonedFrom) {
@@ -848,33 +830,7 @@
}
bool BufferLayer::bufferNeedsFiltering() const {
- // Layers that don't resize along with their buffer, don't need filtering.
- if (!isFixedSize()) {
- return false;
- }
-
- if (!mBufferInfo.mBuffer) {
- return false;
- }
-
- uint32_t bufferWidth = mBufferInfo.mBuffer->width;
- uint32_t bufferHeight = mBufferInfo.mBuffer->height;
-
- // Undo any transformations on the buffer and return the result.
- const State& s(getDrawingState());
- if (s.transform & ui::Transform::ROT_90) {
- std::swap(bufferWidth, bufferHeight);
- }
-
- if (s.transformToDisplayInverse) {
- uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
- if (invTransform & ui::Transform::ROT_90) {
- std::swap(bufferWidth, bufferHeight);
- }
- }
-
- const Rect layerSize{getBounds()};
- return layerSize.width() != bufferWidth || layerSize.height() != bufferHeight;
+ return isFixedSize();
}
} // namespace android
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index c7e8ad7..cd3d80e 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -37,6 +37,7 @@
#include "BufferLayerConsumer.h"
#include "Client.h"
#include "DisplayHardware/HWComposer.h"
+#include "FrameTimeline.h"
#include "FrameTracker.h"
#include "Layer.h"
#include "LayerVector.h"
@@ -50,10 +51,7 @@
explicit BufferLayer(const LayerCreationArgs& args);
virtual ~BufferLayer() override;
- // -----------------------------------------------------------------------
- // Overriden from Layer
- // -----------------------------------------------------------------------
-public:
+ // Implements Layer.
sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const override;
compositionengine::LayerFECompositionState* editCompositionState() override;
@@ -92,12 +90,9 @@
bool isBufferLatched() const override { return mRefreshPending; }
- void notifyAvailableFrames(nsecs_t expectedPresentTime) override;
-
bool hasReadyFrame() const override;
- // Returns the current scaling mode, unless mOverrideScalingMode
- // is set, in which case, it returns mOverrideScalingMode
+ // Returns the current scaling mode
uint32_t getEffectiveScalingMode() const override;
// Calls latchBuffer if the buffer has a frame queued and then releases the buffer.
@@ -120,42 +115,7 @@
// Returns true if the transformed buffer size does not match the layer size and we need
// to apply filtering.
- bool bufferNeedsFiltering() const;
-
- // -----------------------------------------------------------------------
- // Functions that must be implemented by derived classes
- // -----------------------------------------------------------------------
-private:
- virtual bool fenceHasSignaled() const = 0;
- virtual bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const = 0;
-
- PixelFormat getPixelFormat() const;
-
- // Computes the transform matrix using the setFilteringEnabled to determine whether the
- // transform matrix should be computed for use with bilinear filtering.
- void getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]);
-
- virtual uint64_t getFrameNumber(nsecs_t expectedPresentTime) const = 0;
-
- virtual bool getAutoRefresh() const = 0;
- virtual bool getSidebandStreamChanged() const = 0;
-
- // Latch sideband stream and returns true if the dirty region should be updated.
- virtual bool latchSidebandStream(bool& recomputeVisibleRegions) = 0;
-
- virtual bool hasFrameUpdate() const = 0;
-
- virtual status_t bindTextureImage() = 0;
- virtual status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
- nsecs_t expectedPresentTime) = 0;
-
- virtual status_t updateActiveBuffer() = 0;
- virtual status_t updateFrameNumber(nsecs_t latchTime) = 0;
-
- // We generate InputWindowHandles for all non-cursor buffered layers regardless of whether they
- // have an InputChannel. This is to enable the InputDispatcher to do PID based occlusion
- // detection.
- bool needsInputInfo() const override { return !mPotentialCursor; }
+ virtual bool bufferNeedsFiltering() const;
protected:
struct BufferInfo {
@@ -172,7 +132,7 @@
PixelFormat mPixelFormat{PIXEL_FORMAT_NONE};
bool mTransformToDisplayInverse{false};
- sp<GraphicBuffer> mBuffer;
+ std::shared_ptr<renderengine::ExternalTexture> mBuffer;
int mBufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
bool mFrameLatencyNeeded{false};
@@ -191,14 +151,6 @@
bool onPreComposition(nsecs_t) override;
void preparePerFrameCompositionState() override;
- // Loads the corresponding system property once per process
- static bool latchUnsignaledBuffers();
-
- // Check all of the local sync points to ensure that all transactions
- // which need to have been applied prior to the frame which is about to
- // be latched have signaled
- bool allTransactionsSignaled(nsecs_t expectedPresentTime);
-
static bool getOpacityForFormat(uint32_t format);
// from graphics API
@@ -211,15 +163,50 @@
void updateCloneBufferInfo() override;
uint64_t mPreviousFrameNumber = 0;
- virtual uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const;
+ uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const override;
void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override;
// Transform hint provided to the producer. This must be accessed holding
- /// the mStateLock.
+ // the mStateLock.
ui::Transform::RotationFlags mTransformHint = ui::Transform::ROT_0;
+ bool getAutoRefresh() const { return mAutoRefresh; }
+ bool getSidebandStreamChanged() const { return mSidebandStreamChanged; }
+
+ // Returns true if the next buffer should be presented at the expected present time
+ bool shouldPresentNow(nsecs_t expectedPresentTime) const;
+
+ // Returns true if the next buffer should be presented at the expected present time,
+ // overridden by BufferStateLayer and BufferQueueLayer for implementation
+ // specific logic
+ virtual bool isBufferDue(nsecs_t /*expectedPresentTime*/) const = 0;
+
+ std::atomic<bool> mAutoRefresh{false};
+ std::atomic<bool> mSidebandStreamChanged{false};
+
private:
+ virtual bool fenceHasSignaled() const = 0;
+ virtual bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const = 0;
+ virtual uint64_t getFrameNumber(nsecs_t expectedPresentTime) const = 0;
+
+
+ // Latch sideband stream and returns true if the dirty region should be updated.
+ virtual bool latchSidebandStream(bool& recomputeVisibleRegions) = 0;
+
+ virtual bool hasFrameUpdate() const = 0;
+
+ virtual status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
+ nsecs_t expectedPresentTime) = 0;
+
+ virtual status_t updateActiveBuffer() = 0;
+ virtual status_t updateFrameNumber(nsecs_t latchTime) = 0;
+
+ // We generate InputWindowHandles for all non-cursor buffered layers regardless of whether they
+ // have an InputChannel. This is to enable the InputDispatcher to do PID based occlusion
+ // detection.
+ bool needsInputInfo() const override { return !mPotentialCursor; }
+
// Returns true if this layer requires filtering
bool needsFiltering(const DisplayDevice*) const override;
bool needsFilteringForScreenshots(const DisplayDevice*,
@@ -229,6 +216,12 @@
// and its parent layer is not bounded
Rect getBufferSize(const State& s) const override;
+ PixelFormat getPixelFormat() const;
+
+ // Computes the transform matrix using the setFilteringEnabled to determine whether the
+ // transform matrix should be computed for use with bilinear filtering.
+ void getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]);
+
std::unique_ptr<compositionengine::LayerFECompositionState> mCompositionState;
FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 8722952..96b2247 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -25,7 +25,7 @@
#include "BufferLayerConsumer.h"
#include "Layer.h"
-#include "Scheduler/DispSync.h"
+#include "Scheduler/VsyncController.h"
#include <inttypes.h>
@@ -40,7 +40,6 @@
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
#include <private/gui/ComposerService.h>
-#include <renderengine/Image.h>
#include <renderengine/RenderEngine.h>
#include <utils/Log.h>
#include <utils/String8.h>
@@ -153,25 +152,9 @@
if (err != NO_ERROR) {
return err;
}
-
- if (!mRE.useNativeFenceSync()) {
- // Bind the new buffer to the GL texture.
- //
- // Older devices require the "implicit" synchronization provided
- // by glEGLImageTargetTexture2DOES, which this method calls. Newer
- // devices will either call this in Layer::onDraw, or (if it's not
- // a GL-composited layer) not at all.
- err = bindTextureImageLocked();
- }
-
return err;
}
-status_t BufferLayerConsumer::bindTextureImage() {
- Mutex::Autolock lock(mMutex);
- return bindTextureImageLocked();
-}
-
void BufferLayerConsumer::setReleaseFence(const sp<Fence>& fence) {
if (!fence->isValid()) {
return;
@@ -183,7 +166,7 @@
}
auto buffer = mPendingRelease.isPending ? mPendingRelease.graphicBuffer
- : mCurrentTextureBuffer->graphicBuffer();
+ : mCurrentTextureBuffer->getBuffer();
auto err = addReleaseFence(slot, buffer, fence);
if (err != OK) {
BLC_LOGE("setReleaseFence: failed to add the fence: %s (%d)", strerror(-err), err);
@@ -222,9 +205,11 @@
// before, so we need to clean up old references.
if (item->mGraphicBuffer != nullptr) {
std::lock_guard<std::mutex> lock(mImagesMutex);
- if (mImages[item->mSlot] == nullptr || mImages[item->mSlot]->graphicBuffer() == nullptr ||
- mImages[item->mSlot]->graphicBuffer()->getId() != item->mGraphicBuffer->getId()) {
- mImages[item->mSlot] = std::make_shared<Image>(item->mGraphicBuffer, mRE);
+ if (mImages[item->mSlot] == nullptr || mImages[item->mSlot]->getBuffer() == nullptr ||
+ mImages[item->mSlot]->getBuffer()->getId() != item->mGraphicBuffer->getId()) {
+ mImages[item->mSlot] = std::make_shared<
+ renderengine::ExternalTexture>(item->mGraphicBuffer, mRE,
+ renderengine::ExternalTexture::Usage::READABLE);
}
}
@@ -238,8 +223,8 @@
int slot = item.mSlot;
BLC_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture,
- (mCurrentTextureBuffer != nullptr && mCurrentTextureBuffer->graphicBuffer() != nullptr)
- ? mCurrentTextureBuffer->graphicBuffer()->handle
+ (mCurrentTextureBuffer != nullptr && mCurrentTextureBuffer->getBuffer() != nullptr)
+ ? mCurrentTextureBuffer->getBuffer()->handle
: 0,
slot, mSlots[slot].mGraphicBuffer->handle);
@@ -247,7 +232,7 @@
// releaseBufferLocked() if we're in shared buffer mode and both buffers are
// the same.
- std::shared_ptr<Image> nextTextureBuffer;
+ std::shared_ptr<renderengine::ExternalTexture> nextTextureBuffer;
{
std::lock_guard<std::mutex> lock(mImagesMutex);
nextTextureBuffer = mImages[slot];
@@ -257,7 +242,7 @@
if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
if (pendingRelease == nullptr) {
status_t status =
- releaseBufferLocked(mCurrentTexture, mCurrentTextureBuffer->graphicBuffer());
+ releaseBufferLocked(mCurrentTexture, mCurrentTextureBuffer->getBuffer());
if (status < NO_ERROR) {
BLC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status),
status);
@@ -266,7 +251,7 @@
}
} else {
pendingRelease->currentTexture = mCurrentTexture;
- pendingRelease->graphicBuffer = mCurrentTextureBuffer->graphicBuffer();
+ pendingRelease->graphicBuffer = mCurrentTextureBuffer->getBuffer();
pendingRelease->isPending = true;
}
}
@@ -292,17 +277,6 @@
return err;
}
-status_t BufferLayerConsumer::bindTextureImageLocked() {
- ATRACE_CALL();
-
- if (mCurrentTextureBuffer != nullptr && mCurrentTextureBuffer->graphicBuffer() != nullptr) {
- return mRE.bindExternalTextureBuffer(mTexName, mCurrentTextureBuffer->graphicBuffer(),
- mCurrentFence);
- }
-
- return NO_INIT;
-}
-
void BufferLayerConsumer::getTransformMatrix(float mtx[16]) {
Mutex::Autolock lock(mMutex);
memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
@@ -328,14 +302,14 @@
void BufferLayerConsumer::computeCurrentTransformMatrixLocked() {
BLC_LOGV("computeCurrentTransformMatrixLocked");
- if (mCurrentTextureBuffer == nullptr || mCurrentTextureBuffer->graphicBuffer() == nullptr) {
+ if (mCurrentTextureBuffer == nullptr || mCurrentTextureBuffer->getBuffer() == nullptr) {
BLC_LOGD("computeCurrentTransformMatrixLocked: "
"mCurrentTextureBuffer is nullptr");
}
GLConsumer::computeTransformMatrix(mCurrentTransformMatrix,
mCurrentTextureBuffer == nullptr
? nullptr
- : mCurrentTextureBuffer->graphicBuffer(),
+ : mCurrentTextureBuffer->getBuffer(),
getCurrentCropLocked(), mCurrentTransform,
mFilteringEnabled);
}
@@ -387,7 +361,8 @@
return mCurrentApi;
}
-sp<GraphicBuffer> BufferLayerConsumer::getCurrentBuffer(int* outSlot, sp<Fence>* outFence) const {
+std::shared_ptr<renderengine::ExternalTexture> BufferLayerConsumer::getCurrentBuffer(
+ int* outSlot, sp<Fence>* outFence) const {
Mutex::Autolock lock(mMutex);
if (outSlot != nullptr) {
@@ -398,7 +373,7 @@
*outFence = mCurrentFence;
}
- return mCurrentTextureBuffer == nullptr ? nullptr : mCurrentTextureBuffer->graphicBuffer();
+ return mCurrentTextureBuffer == nullptr ? nullptr : mCurrentTextureBuffer;
}
Rect BufferLayerConsumer::getCurrentCrop() const {
@@ -483,10 +458,12 @@
void BufferLayerConsumer::onBufferAvailable(const BufferItem& item) {
if (item.mGraphicBuffer != nullptr && item.mSlot != BufferQueue::INVALID_BUFFER_SLOT) {
std::lock_guard<std::mutex> lock(mImagesMutex);
- const std::shared_ptr<Image>& oldImage = mImages[item.mSlot];
- if (oldImage == nullptr || oldImage->graphicBuffer() == nullptr ||
- oldImage->graphicBuffer()->getId() != item.mGraphicBuffer->getId()) {
- mImages[item.mSlot] = std::make_shared<Image>(item.mGraphicBuffer, mRE);
+ const std::shared_ptr<renderengine::ExternalTexture>& oldImage = mImages[item.mSlot];
+ if (oldImage == nullptr || oldImage->getBuffer() == nullptr ||
+ oldImage->getBuffer()->getId() != item.mGraphicBuffer->getId()) {
+ mImages[item.mSlot] = std::make_shared<
+ renderengine::ExternalTexture>(item.mGraphicBuffer, mRE,
+ renderengine::ExternalTexture::Usage::READABLE);
}
}
}
@@ -526,19 +503,6 @@
ConsumerBase::dumpLocked(result, prefix);
}
-
-BufferLayerConsumer::Image::Image(const sp<GraphicBuffer>& graphicBuffer,
- renderengine::RenderEngine& engine)
- : mGraphicBuffer(graphicBuffer), mRE(engine) {
- mRE.cacheExternalTextureBuffer(mGraphicBuffer);
-}
-
-BufferLayerConsumer::Image::~Image() {
- if (mGraphicBuffer != nullptr) {
- ALOGV("Destroying buffer: %" PRId64, mGraphicBuffer->getId());
- mRE.unbindExternalTextureBuffer(mGraphicBuffer->getId());
- }
-}
}; // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
index 5e3044f..9ed80b4 100644
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -21,12 +21,11 @@
#include <gui/BufferQueueDefs.h>
#include <gui/ConsumerBase.h>
#include <gui/HdrMetadata.h>
-
+#include <renderengine/ExternalTexture.h>
#include <ui/FenceTime.h>
#include <ui/GraphicBuffer.h>
#include <ui/GraphicTypes.h>
#include <ui/Region.h>
-
#include <utils/String8.h>
#include <utils/Vector.h>
#include <utils/threads.h>
@@ -34,13 +33,11 @@
namespace android {
// ----------------------------------------------------------------------------
-class DispSync;
class Layer;
class String8;
namespace renderengine {
class RenderEngine;
-class Image;
} // namespace renderengine
/*
@@ -95,9 +92,6 @@
status_t updateTexImage(BufferRejecter* rejecter, nsecs_t expectedPresentTime,
bool* autoRefresh, bool* queuedBuffer, uint64_t maxFrameNumber);
- // See BufferLayerConsumer::bindTextureImageLocked().
- status_t bindTextureImage();
-
// setReleaseFence stores a fence that will signal when the current buffer
// is no longer being read. This fence will be returned to the producer
// when the current buffer is released by updateTexImage(). Multiple
@@ -157,7 +151,8 @@
// When outSlot is not nullptr, the current buffer slot index is also
// returned. Simiarly, when outFence is not nullptr, the current output
// fence is returned.
- sp<GraphicBuffer> getCurrentBuffer(int* outSlot = nullptr, sp<Fence>* outFence = nullptr) const;
+ std::shared_ptr<renderengine::ExternalTexture> getCurrentBuffer(
+ int* outSlot = nullptr, sp<Fence>* outFence = nullptr) const;
// getCurrentCrop returns the cropping rectangle of the current buffer.
Rect getCurrentCrop() const;
@@ -215,10 +210,6 @@
PendingRelease* pendingRelease = nullptr)
EXCLUDES(mImagesMutex);
- // Binds mTexName and the current buffer to TEXTURE_EXTERNAL target.
- // If the bind succeeds, this calls doFenceWait.
- status_t bindTextureImageLocked();
-
private:
// Utility class for managing GraphicBuffer references into renderengine
class Image {
@@ -266,7 +257,7 @@
// mCurrentTextureBuffer is the buffer containing the current texture. It's
// possible that this buffer is not associated with any buffer slot, so we
// must track it separately in order to support the getCurrentBuffer method.
- std::shared_ptr<Image> mCurrentTextureBuffer;
+ std::shared_ptr<renderengine::ExternalTexture> mCurrentTextureBuffer;
// mCurrentCrop is the crop rectangle that applies to the current texture.
// It gets set each time updateTexImage is called.
@@ -345,7 +336,8 @@
int mCurrentTexture;
// Shadow buffer cache for cleaning up renderengine references.
- std::shared_ptr<Image> mImages[BufferQueueDefs::NUM_BUFFER_SLOTS] GUARDED_BY(mImagesMutex);
+ std::shared_ptr<renderengine::ExternalTexture>
+ mImages[BufferQueueDefs::NUM_BUFFER_SLOTS] GUARDED_BY(mImagesMutex);
// Separate mutex guarding the shadow buffer cache.
// mImagesMutex can be manipulated with binder threads (e.g. onBuffersAllocated)
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 6e4235e..e7f373f 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -17,6 +17,7 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
#undef LOG_TAG
#define LOG_TAG "BufferQueueLayer"
@@ -35,6 +36,7 @@
#include "TimeStats/TimeStats.h"
namespace android {
+using PresentState = frametimeline::SurfaceFrame::PresentState;
BufferQueueLayer::BufferQueueLayer(const LayerCreationArgs& args) : BufferLayer(args) {}
@@ -98,18 +100,10 @@
return mQueuedFrames;
}
-bool BufferQueueLayer::shouldPresentNow(nsecs_t expectedPresentTime) const {
- if (getSidebandStreamChanged() || getAutoRefresh()) {
- return true;
- }
-
- if (!hasFrameUpdate()) {
- return false;
- }
-
+bool BufferQueueLayer::isBufferDue(nsecs_t expectedPresentTime) const {
Mutex::Autolock lock(mQueueItemLock);
- const int64_t addedTime = mQueueItems[0].mTimestamp;
+ const int64_t addedTime = mQueueItems[0].item.mTimestamp;
// Ignore timestamps more than a second in the future
const bool isPlausible = addedTime < (expectedPresentTime + s2ns(1));
@@ -131,16 +125,12 @@
// -----------------------------------------------------------------------
bool BufferQueueLayer::fenceHasSignaled() const {
- if (latchUnsignaledBuffers()) {
- return true;
- }
-
if (!hasFrameUpdate()) {
return true;
}
Mutex::Autolock lock(mQueueItemLock);
- if (mQueueItems[0].mIsDroppable) {
+ if (mQueueItems[0].item.mIsDroppable) {
// Even though this buffer's fence may not have signaled yet, it could
// be replaced by another buffer before it has a chance to, which means
// that it's possible to get into a situation where a buffer is never
@@ -148,7 +138,7 @@
return true;
}
const bool fenceSignaled =
- mQueueItems[0].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+ mQueueItems[0].item.mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
if (!fenceSignaled) {
mFlinger->mTimeStats->incrementLatchSkipped(getSequence(),
TimeStats::LatchSkipReason::LateAcquire);
@@ -163,12 +153,12 @@
}
Mutex::Autolock lock(mQueueItemLock);
- return mQueueItems[0].mTimestamp <= expectedPresentTime;
+ return mQueueItems[0].item.mTimestamp <= expectedPresentTime;
}
uint64_t BufferQueueLayer::getFrameNumber(nsecs_t expectedPresentTime) const {
Mutex::Autolock lock(mQueueItemLock);
- uint64_t frameNumber = mQueueItems[0].mFrameNumber;
+ uint64_t frameNumber = mQueueItems[0].item.mFrameNumber;
// The head of the queue will be dropped if there are signaled and timely frames behind it
if (isRemovedFromCurrentState()) {
@@ -177,39 +167,35 @@
for (int i = 1; i < mQueueItems.size(); i++) {
const bool fenceSignaled =
- mQueueItems[i].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+ mQueueItems[i].item.mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
if (!fenceSignaled) {
break;
}
// We don't drop frames without explicit timestamps
- if (mQueueItems[i].mIsAutoTimestamp) {
+ if (mQueueItems[i].item.mIsAutoTimestamp) {
break;
}
- const nsecs_t desiredPresent = mQueueItems[i].mTimestamp;
+ const nsecs_t desiredPresent = mQueueItems[i].item.mTimestamp;
if (desiredPresent < expectedPresentTime - BufferQueueConsumer::MAX_REASONABLE_NSEC ||
desiredPresent > expectedPresentTime) {
break;
}
- frameNumber = mQueueItems[i].mFrameNumber;
+ frameNumber = mQueueItems[i].item.mFrameNumber;
}
return frameNumber;
}
-bool BufferQueueLayer::getAutoRefresh() const {
- return mAutoRefresh;
-}
-
-bool BufferQueueLayer::getSidebandStreamChanged() const {
- return mSidebandStreamChanged;
-}
-
bool BufferQueueLayer::latchSidebandStream(bool& recomputeVisibleRegions) {
+ // We need to update the sideband stream if the layer has both a buffer and a sideband stream.
+ const bool updateSidebandStream = hasFrameUpdate() && mSidebandStream.get();
+
bool sidebandStreamChanged = true;
- if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, false)) {
+ if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, false) ||
+ updateSidebandStream) {
// mSidebandStreamChanged was changed to false
mSidebandStream = mConsumer->getSidebandStream();
auto* layerCompositionState = editCompositionState();
@@ -229,10 +215,6 @@
return mQueuedFrames > 0;
}
-status_t BufferQueueLayer::bindTextureImage() {
- return mConsumer->bindTextureImage();
-}
-
status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
nsecs_t expectedPresentTime) {
// This boolean is used to make sure that SurfaceFlinger's shadow copy
@@ -242,7 +224,7 @@
bool queuedBuffer = false;
const int32_t layerId = getSequence();
LayerRejecter r(mDrawingState, getCurrentState(), recomputeVisibleRegions,
- getProducerStickyTransform() != 0, mName, mOverrideScalingMode,
+ getProducerStickyTransform() != 0, mName,
getTransformToDisplayInverse());
if (isRemovedFromCurrentState()) {
@@ -258,18 +240,20 @@
Mutex::Autolock lock(mQueueItemLock);
for (int i = 0; i < mQueueItems.size(); i++) {
bool fenceSignaled =
- mQueueItems[i].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+ mQueueItems[i].item.mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
if (!fenceSignaled) {
break;
}
- lastSignaledFrameNumber = mQueueItems[i].mFrameNumber;
+ lastSignaledFrameNumber = mQueueItems[i].item.mFrameNumber;
}
}
const uint64_t maxFrameNumberToAcquire =
std::min(mLastFrameNumberReceived.load(), lastSignaledFrameNumber);
- status_t updateResult = mConsumer->updateTexImage(&r, expectedPresentTime, &mAutoRefresh,
+ bool autoRefresh;
+ status_t updateResult = mConsumer->updateTexImage(&r, expectedPresentTime, &autoRefresh,
&queuedBuffer, maxFrameNumberToAcquire);
+ mAutoRefresh = autoRefresh;
if (updateResult == BufferQueue::PRESENT_LATER) {
// Producer doesn't want buffer to be displayed yet. Signal a
// layer update so we check again at the next opportunity.
@@ -280,9 +264,12 @@
// and return early
if (queuedBuffer) {
Mutex::Autolock lock(mQueueItemLock);
- mConsumer->mergeSurfaceDamage(mQueueItems[0].mSurfaceDamage);
- mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].mFrameNumber);
- mQueueItems.removeAt(0);
+ mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage);
+ mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber);
+ if (mQueueItems[0].surfaceFrame) {
+ addSurfaceFrameDroppedForBuffer(mQueueItems[0].surfaceFrame);
+ }
+ mQueueItems.erase(mQueueItems.begin());
mQueuedFrames--;
}
return BAD_VALUE;
@@ -293,6 +280,11 @@
// early.
if (queuedBuffer) {
Mutex::Autolock lock(mQueueItemLock);
+ for (auto& [item, surfaceFrame] : mQueueItems) {
+ if (surfaceFrame) {
+ addSurfaceFrameDroppedForBuffer(surfaceFrame);
+ }
+ }
mQueueItems.clear();
mQueuedFrames = 0;
mFlinger->mTimeStats->onDestroy(layerId);
@@ -316,19 +308,27 @@
// Remove any stale buffers that have been dropped during
// updateTexImage
- while (mQueueItems[0].mFrameNumber != currentFrameNumber) {
- mConsumer->mergeSurfaceDamage(mQueueItems[0].mSurfaceDamage);
- mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].mFrameNumber);
- mQueueItems.removeAt(0);
+ while (mQueueItems[0].item.mFrameNumber != currentFrameNumber) {
+ mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage);
+ mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber);
+ if (mQueueItems[0].surfaceFrame) {
+ addSurfaceFrameDroppedForBuffer(mQueueItems[0].surfaceFrame);
+ }
+ mQueueItems.erase(mQueueItems.begin());
mQueuedFrames--;
}
- uint64_t bufferID = mQueueItems[0].mGraphicBuffer->getId();
+ uint64_t bufferID = mQueueItems[0].item.mGraphicBuffer->getId();
mFlinger->mTimeStats->setLatchTime(layerId, currentFrameNumber, latchTime);
mFlinger->mFrameTracer->traceTimestamp(layerId, bufferID, currentFrameNumber, latchTime,
FrameTracer::FrameEvent::LATCH);
- mQueueItems.removeAt(0);
+ if (mQueueItems[0].surfaceFrame) {
+ addSurfaceFramePresentedForBuffer(mQueueItems[0].surfaceFrame,
+ mQueueItems[0].item.mFenceTime->getSignalTime(),
+ latchTime);
+ }
+ mQueueItems.erase(mQueueItems.begin());
}
// Decrement the queued-frames count. Signal another event if we
@@ -364,6 +364,10 @@
return NO_ERROR;
}
+void BufferQueueLayer::setFrameTimelineInfoForBuffer(const FrameTimelineInfo& frameTimelineInfo) {
+ mFrameTimelineInfo = frameTimelineInfo;
+}
+
// -----------------------------------------------------------------------
// Interface implementation for BufferLayerConsumer::ContentsChangedListener
// -----------------------------------------------------------------------
@@ -420,7 +424,9 @@
}
}
- mQueueItems.push_back(item);
+ auto surfaceFrame = createSurfaceFrameForBuffer(mFrameTimelineInfo, systemTime(), mName);
+
+ mQueueItems.push_back({item, surfaceFrame});
mQueuedFrames++;
// Wake up any pending callbacks
@@ -453,7 +459,10 @@
ALOGE("Can't replace a frame on an empty queue");
return;
}
- mQueueItems.editItemAt(mQueueItems.size() - 1) = item;
+
+ auto surfaceFrame = createSurfaceFrameForBuffer(mFrameTimelineInfo, systemTime(), mName);
+ mQueueItems[mQueueItems.size() - 1].item = item;
+ mQueueItems[mQueueItems.size() - 1].surfaceFrame = std::move(surfaceFrame);
// Wake up any pending callbacks
mLastFrameNumberReceived = item.mFrameNumber;
@@ -497,10 +506,7 @@
mConsumer->setContentsChangedListener(mContentsChangedListener);
mConsumer->setName(String8(mName.data(), mName.size()));
- // BufferQueueCore::mMaxDequeuedBufferCount is default to 1
- if (!mFlinger->isLayerTripleBufferingDisabled()) {
- mProducer->setMaxDequeuedBufferCount(2);
- }
+ mProducer->setMaxDequeuedBufferCount(2);
}
status_t BufferQueueLayer::setDefaultBufferProperties(uint32_t w, uint32_t h, PixelFormat format) {
@@ -617,4 +623,4 @@
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 5ebc22d..b3b7948 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -22,6 +22,10 @@
namespace android {
+namespace frametimeline {
+class SurfaceFrame;
+}
+
/*
* A new BufferQueue and a new BufferLayerConsumer are created when the
* BufferLayer is first referenced.
@@ -35,10 +39,7 @@
explicit BufferQueueLayer(const LayerCreationArgs&);
~BufferQueueLayer() override;
- // -----------------------------------------------------------------------
- // Interface implementation for Layer
- // -----------------------------------------------------------------------
-public:
+ // Implements Layer.
const char* getType() const override { return "BufferQueueLayer"; }
void onLayerDisplayed(const sp<Fence>& releaseFence) override;
@@ -52,43 +53,15 @@
int32_t getQueuedFrameCount() const override;
- bool shouldPresentNow(nsecs_t expectedPresentTime) const override;
+ // Returns true if the next buffer should be presented at the expected present time
+ bool isBufferDue(nsecs_t expectedPresentTime) const override;
- // -----------------------------------------------------------------------
-
- // -----------------------------------------------------------------------
- // Interface implementation for BufferLayer
- // -----------------------------------------------------------------------
-public:
+ // Implements BufferLayer.
bool fenceHasSignaled() const override;
bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const override;
-private:
- uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override;
-
- bool getAutoRefresh() const override;
- bool getSidebandStreamChanged() const override;
-
- bool latchSidebandStream(bool& recomputeVisibleRegions) override;
- void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override;
-
- bool hasFrameUpdate() const override;
-
- status_t bindTextureImage() override;
- status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
- nsecs_t expectedPresentTime) override;
-
- status_t updateActiveBuffer() override;
- status_t updateFrameNumber(nsecs_t latchTime) override;
-
- sp<Layer> createClone() override;
-
- void onFrameAvailable(const BufferItem& item);
- void onFrameReplaced(const BufferItem& item);
- void onSidebandStreamChanged();
- void onFrameDequeued(const uint64_t bufferId);
- void onFrameDetached(const uint64_t bufferId);
- void onFrameCancelled(const uint64_t bufferId);
+ status_t setDefaultBufferProperties(uint32_t w, uint32_t h, PixelFormat format);
+ sp<IGraphicBufferProducer> getProducer() const;
protected:
void gatherBufferInfo() override;
@@ -114,19 +87,36 @@
BufferQueueLayer* mBufferQueueLayer = nullptr;
Mutex mMutex;
};
- // -----------------------------------------------------------------------
-
-public:
- status_t setDefaultBufferProperties(uint32_t w, uint32_t h, PixelFormat format);
-
- sp<IGraphicBufferProducer> getProducer() const;
private:
- // Temporary - Used only for LEGACY camera mode.
- uint32_t getProducerStickyTransform() const;
+ uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override;
+
+ bool latchSidebandStream(bool& recomputeVisibleRegions) override;
+ void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override;
+
+ bool hasFrameUpdate() const override;
+
+ status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
+ nsecs_t expectedPresentTime) override;
+
+ status_t updateActiveBuffer() override;
+ status_t updateFrameNumber(nsecs_t latchTime) override;
+ void setFrameTimelineInfoForBuffer(const FrameTimelineInfo& frameTimelineInfo) override;
+
+ sp<Layer> createClone() override;
void onFirstRef() override;
+ void onFrameAvailable(const BufferItem& item);
+ void onFrameReplaced(const BufferItem& item);
+ void onSidebandStreamChanged();
+ void onFrameDequeued(const uint64_t bufferId);
+ void onFrameDetached(const uint64_t bufferId);
+ void onFrameCancelled(const uint64_t bufferId);
+
+ // Temporary - Used only for LEGACY camera mode.
+ uint32_t getProducerStickyTransform() const;
+
sp<BufferLayerConsumer> mConsumer;
sp<IGraphicBufferProducer> mProducer;
@@ -138,16 +128,25 @@
// Local copy of the queued contents of the incoming BufferQueue
mutable Mutex mQueueItemLock;
Condition mQueueItemCondition;
- Vector<BufferItem> mQueueItems;
- std::atomic<uint64_t> mLastFrameNumberReceived{0};
- bool mAutoRefresh{false};
+ struct BufferData {
+ BufferData(BufferItem item, std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame)
+ : item(item), surfaceFrame(surfaceFrame) {}
+ BufferItem item;
+ std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame;
+ };
+ std::vector<BufferData> mQueueItems;
+ std::atomic<uint64_t> mLastFrameNumberReceived{0};
// thread-safe
std::atomic<int32_t> mQueuedFrames{0};
- std::atomic<bool> mSidebandStreamChanged{false};
sp<ContentsChangedListener> mContentsChangedListener;
+
+ // The last vsync info received on this layer. This will be used when we get
+ // a buffer to correlate the buffer with the vsync id. Can only be accessed
+ // with the SF state lock held.
+ FrameTimelineInfo mFrameTimelineInfo;
};
} // namespace android
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 41dd7bf..7a5b20d 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -17,6 +17,7 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
//#define LOG_NDEBUG 0
#undef LOG_TAG
@@ -27,16 +28,29 @@
#include <limits>
+#include <FrameTimeline/FrameTimeline.h>
#include <compositionengine/LayerFECompositionState.h>
#include <gui/BufferQueue.h>
#include <private/gui/SyncFeatures.h>
#include <renderengine/Image.h>
#include "EffectLayer.h"
+#include "FrameTracer/FrameTracer.h"
#include "TimeStats/TimeStats.h"
namespace android {
+using PresentState = frametimeline::SurfaceFrame::PresentState;
+namespace {
+void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
+ const sp<GraphicBuffer>& buffer, const sp<Fence>& releaseFence) {
+ if (!listener) {
+ return;
+ }
+ listener->onReleaseBuffer(buffer->getId(), releaseFence ? releaseFence : Fence::NO_FENCE);
+}
+} // namespace
+
// clang-format off
const std::array<float, 16> BufferStateLayer::IDENTITY_MATRIX{
1, 0, 0, 0,
@@ -48,7 +62,6 @@
BufferStateLayer::BufferStateLayer(const LayerCreationArgs& args)
: BufferLayer(args), mHwcSlotGenerator(new HwcSlotGenerator()) {
- mOverrideScalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
mCurrentState.dataspace = ui::Dataspace::V0_SRGB;
}
@@ -58,18 +71,76 @@
// original layer and the clone should be removed at the same time so there shouldn't be any
// issue with the clone layer trying to use the texture.
if (mBufferInfo.mBuffer != nullptr && !isClone()) {
- // Ensure that mBuffer is uncached from RenderEngine here, as
- // RenderEngine may have been using the buffer as an external texture
- // after the client uncached the buffer.
- auto& engine(mFlinger->getRenderEngine());
- engine.unbindExternalTextureBuffer(mBufferInfo.mBuffer->getId());
+ callReleaseBufferCallback(mDrawingState.releaseBufferListener,
+ mBufferInfo.mBuffer->getBuffer(), mBufferInfo.mFence);
}
}
+status_t BufferStateLayer::addReleaseFence(const sp<CallbackHandle>& ch,
+ const sp<Fence>& fence) {
+ if (ch == nullptr) {
+ return OK;
+ }
+ ch->previousBufferId = mPreviousBufferId;
+ if (!ch->previousReleaseFence.get()) {
+ ch->previousReleaseFence = fence;
+ return OK;
+ }
+
+ // Below logic is lifted from ConsumerBase.cpp:
+ // Check status of fences first because merging is expensive.
+ // Merging an invalid fence with any other fence results in an
+ // invalid fence.
+ auto currentStatus = ch->previousReleaseFence->getStatus();
+ if (currentStatus == Fence::Status::Invalid) {
+ ALOGE("Existing fence has invalid state, layer: %s", mName.c_str());
+ return BAD_VALUE;
+ }
+
+ auto incomingStatus = fence->getStatus();
+ if (incomingStatus == Fence::Status::Invalid) {
+ ALOGE("New fence has invalid state, layer: %s", mName.c_str());
+ ch->previousReleaseFence = fence;
+ return BAD_VALUE;
+ }
+
+ // If both fences are signaled or both are unsignaled, we need to merge
+ // them to get an accurate timestamp.
+ if (currentStatus == incomingStatus) {
+ char fenceName[32] = {};
+ snprintf(fenceName, 32, "%.28s", mName.c_str());
+ sp<Fence> mergedFence = Fence::merge(
+ fenceName, ch->previousReleaseFence, fence);
+ if (!mergedFence.get()) {
+ ALOGE("failed to merge release fences, layer: %s", mName.c_str());
+ // synchronization is broken, the best we can do is hope fences
+ // signal in order so the new fence will act like a union
+ ch->previousReleaseFence = fence;
+ return BAD_VALUE;
+ }
+ ch->previousReleaseFence = mergedFence;
+ } else if (incomingStatus == Fence::Status::Unsignaled) {
+ // If one fence has signaled and the other hasn't, the unsignaled
+ // fence will approximately correspond with the correct timestamp.
+ // There's a small race if both fences signal at about the same time
+ // and their statuses are retrieved with unfortunate timing. However,
+ // by this point, they will have both signaled and only the timestamp
+ // will be slightly off; any dependencies after this point will
+ // already have been met.
+ ch->previousReleaseFence = fence;
+ }
+ // else if (currentStatus == Fence::Status::Unsignaled) is a no-op.
+
+ return OK;
+}
+
// -----------------------------------------------------------------------
// Interface implementation for Layer
// -----------------------------------------------------------------------
void BufferStateLayer::onLayerDisplayed(const sp<Fence>& releaseFence) {
+ if (!releaseFence->isValid()) {
+ return;
+ }
// The previous release fence notifies the client that SurfaceFlinger is done with the previous
// buffer that was presented on this layer. The first transaction that came in this frame that
// replaced the previous buffer on this layer needs this release fence, because the fence will
@@ -86,12 +157,17 @@
// buffer. It replaces the buffer in the second transaction. The buffer in the second
// transaction will now no longer be presented so it is released immediately and the third
// transaction doesn't need a previous release fence.
+ sp<CallbackHandle> ch;
for (auto& handle : mDrawingState.callbackHandles) {
if (handle->releasePreviousBuffer) {
- handle->previousReleaseFence = releaseFence;
+ ch = handle;
break;
}
}
+ auto status = addReleaseFence(ch, releaseFence);
+ if (status != OK) {
+ ALOGE("Failed to add release fence for layer %s", getName().c_str());
+ }
mPreviousReleaseFence = releaseFence;
@@ -101,14 +177,53 @@
}
}
+void BufferStateLayer::onSurfaceFrameCreated(
+ const std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame) {
+ while (mPendingJankClassifications.size() >= kPendingClassificationMaxSurfaceFrames) {
+ // Too many SurfaceFrames pending classification. The front of the deque is probably not
+ // tracked by FrameTimeline and will never be presented. This will only result in a memory
+ // leak.
+ ALOGW("Removing the front of pending jank deque from layer - %s to prevent memory leak",
+ mName.c_str());
+ std::string miniDump = mPendingJankClassifications.front()->miniDump();
+ ALOGD("Head SurfaceFrame mini dump\n%s", miniDump.c_str());
+ mPendingJankClassifications.pop_front();
+ }
+ mPendingJankClassifications.emplace_back(surfaceFrame);
+}
+
void BufferStateLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
for (const auto& handle : mDrawingState.callbackHandles) {
handle->transformHint = mTransformHint;
handle->dequeueReadyTime = dequeueReadyTime;
}
- mFlinger->getTransactionCompletedThread().finalizePendingCallbackHandles(
- mDrawingState.callbackHandles);
+ // If there are multiple transactions in this frame, set the previous id on the earliest
+ // transacton. We don't need to pass in the released buffer id to multiple transactions.
+ // The buffer id does not have to correspond to any particular transaction as long as the
+ // listening end point is the same but the client expects the first transaction callback that
+ // replaces the presented buffer to contain the release fence. This follows the same logic.
+ // see BufferStateLayer::onLayerDisplayed.
+ for (auto& handle : mDrawingState.callbackHandles) {
+ if (handle->releasePreviousBuffer) {
+ handle->previousBufferId = mPreviousBufferId;
+ break;
+ }
+ }
+
+ std::vector<JankData> jankData;
+ jankData.reserve(mPendingJankClassifications.size());
+ while (!mPendingJankClassifications.empty()
+ && mPendingJankClassifications.front()->getJankType()) {
+ std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame =
+ mPendingJankClassifications.front();
+ mPendingJankClassifications.pop_front();
+ jankData.emplace_back(
+ JankData(surfaceFrame->getToken(), surfaceFrame->getJankType().value()));
+ }
+
+ mFlinger->getTransactionCallbackInvoker().finalizePendingCallbackHandles(
+ mDrawingState.callbackHandles, jankData);
mDrawingState.callbackHandles = {};
@@ -131,49 +246,20 @@
}
}
-bool BufferStateLayer::shouldPresentNow(nsecs_t /*expectedPresentTime*/) const {
- if (getSidebandStreamChanged() || getAutoRefresh()) {
- return true;
- }
-
- return hasFrameUpdate();
-}
-
bool BufferStateLayer::willPresentCurrentTransaction() const {
// Returns true if the most recent Transaction applied to CurrentState will be presented.
return (getSidebandStreamChanged() || getAutoRefresh() ||
(mCurrentState.modified &&
- (mCurrentState.buffer != nullptr || mCurrentState.bgColorLayer != nullptr))) &&
- !mLayerDetached;
+ (mCurrentState.buffer != nullptr || mCurrentState.bgColorLayer != nullptr)));
}
-/* TODO: vhau uncomment once deferred transaction migration complete in
- * WindowManager
-void BufferStateLayer::pushPendingState() {
- if (!mCurrentState.modified) {
- return;
- }
- mPendingStates.push_back(mCurrentState);
- ATRACE_INT(mTransactionName.c_str(), mPendingStates.size());
-}
-*/
-
-bool BufferStateLayer::applyPendingStates(Layer::State* stateToCommit) {
- mCurrentStateModified = mCurrentState.modified;
- bool stateUpdateAvailable = Layer::applyPendingStates(stateToCommit);
- mCurrentStateModified = stateUpdateAvailable && mCurrentStateModified;
- mCurrentState.modified = false;
- return stateUpdateAvailable;
-}
-
-// Crop that applies to the window
-Rect BufferStateLayer::getCrop(const Layer::State& /*s*/) const {
- return Rect::INVALID_RECT;
+Rect BufferStateLayer::getCrop(const Layer::State& s) const {
+ return s.crop;
}
bool BufferStateLayer::setTransform(uint32_t transform) {
- if (mCurrentState.transform == transform) return false;
- mCurrentState.transform = transform;
+ if (mCurrentState.bufferTransform == transform) return false;
+ mCurrentState.bufferTransform = transform;
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
@@ -189,57 +275,53 @@
}
bool BufferStateLayer::setCrop(const Rect& crop) {
- Rect c = crop;
- if (c.left < 0) {
- c.left = 0;
- }
- if (c.top < 0) {
- c.top = 0;
- }
- // If the width and/or height are < 0, make it [0, 0, -1, -1] so the equality comparision below
- // treats all invalid rectangles the same.
- if (!c.isValid()) {
- c.makeInvalid();
- }
+ if (mCurrentState.crop == crop) return false;
+ mCurrentState.sequence++;
+ mCurrentState.crop = crop;
- if (mCurrentState.crop == c) return false;
- mCurrentState.crop = c;
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
}
-bool BufferStateLayer::setFrame(const Rect& frame) {
- int x = frame.left;
- int y = frame.top;
- int w = frame.getWidth();
- int h = frame.getHeight();
-
- if (x < 0) {
- x = 0;
- w = frame.right;
- }
-
- if (y < 0) {
- y = 0;
- h = frame.bottom;
- }
-
- if (mCurrentState.active.transform.tx() == x && mCurrentState.active.transform.ty() == y &&
- mCurrentState.active.w == w && mCurrentState.active.h == h) {
+bool BufferStateLayer::setMatrix(const layer_state_t::matrix22_t& matrix,
+ bool allowNonRectPreservingTransforms) {
+ if (mCurrentState.transform.dsdx() == matrix.dsdx &&
+ mCurrentState.transform.dtdy() == matrix.dtdy &&
+ mCurrentState.transform.dtdx() == matrix.dtdx &&
+ mCurrentState.transform.dsdy() == matrix.dsdy) {
return false;
}
- if (!frame.isValid()) {
- x = y = w = h = 0;
+ ui::Transform t;
+ t.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
+
+ if (!allowNonRectPreservingTransforms && !t.preserveRects()) {
+ ALOGW("Attempt to set rotation matrix without permission ACCESS_SURFACE_FLINGER nor "
+ "ROTATE_SURFACE_FLINGER ignored");
+ return false;
}
- mCurrentState.active.transform.set(x, y);
- mCurrentState.active.w = w;
- mCurrentState.active.h = h;
+
+ mCurrentState.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
mCurrentState.sequence++;
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
+
+ return true;
+}
+
+bool BufferStateLayer::setPosition(float x, float y) {
+ if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y) {
+ return false;
+ }
+
+ mCurrentState.transform.set(x, y);
+
+ mCurrentState.sequence++;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+
return true;
}
@@ -256,15 +338,34 @@
return true;
}
-bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence,
- nsecs_t postTime, nsecs_t desiredPresentTime,
- const client_cache_t& clientCacheId) {
+bool BufferStateLayer::setBuffer(const std::shared_ptr<renderengine::ExternalTexture>& buffer,
+ const sp<Fence>& acquireFence, nsecs_t postTime,
+ nsecs_t desiredPresentTime, bool isAutoTimestamp,
+ const client_cache_t& clientCacheId, uint64_t frameNumber,
+ std::optional<nsecs_t> dequeueTime, const FrameTimelineInfo& info,
+ const sp<ITransactionCompletedListener>& releaseBufferListener) {
+ ATRACE_CALL();
+
if (mCurrentState.buffer) {
mReleasePreviousBuffer = true;
+ if (!mDrawingState.buffer ||
+ mCurrentState.buffer->getBuffer() != mDrawingState.buffer->getBuffer()) {
+ // If mCurrentState has a buffer, and we are about to update again
+ // before swapping to drawing state, then the first buffer will be
+ // dropped and we should decrement the pending buffer count and
+ // call any release buffer callbacks if set.
+ callReleaseBufferCallback(mCurrentState.releaseBufferListener,
+ mCurrentState.buffer->getBuffer(),
+ mCurrentState.acquireFence);
+ decrementPendingBufferCount();
+ if (mCurrentState.bufferSurfaceFrameTX != nullptr) {
+ addSurfaceFrameDroppedForBuffer(mCurrentState.bufferSurfaceFrameTX);
+ mCurrentState.bufferSurfaceFrameTX.reset();
+ }
+ }
}
-
- mCurrentState.frameNumber++;
-
+ mCurrentState.frameNumber = frameNumber;
+ mCurrentState.releaseBufferListener = releaseBufferListener;
mCurrentState.buffer = buffer;
mCurrentState.clientCacheId = clientCacheId;
mCurrentState.modified = true;
@@ -272,22 +373,48 @@
const int32_t layerId = getSequence();
mFlinger->mTimeStats->setPostTime(layerId, mCurrentState.frameNumber, getName().c_str(),
- postTime);
- desiredPresentTime = desiredPresentTime <= 0 ? 0 : desiredPresentTime;
+ mOwnerUid, postTime);
mCurrentState.desiredPresentTime = desiredPresentTime;
+ mCurrentState.isAutoTimestamp = isAutoTimestamp;
- mFlinger->mScheduler->recordLayerHistory(this, desiredPresentTime,
+ const nsecs_t presentTime = [&] {
+ if (!isAutoTimestamp) return desiredPresentTime;
+
+ const auto prediction =
+ mFlinger->mFrameTimeline->getTokenManager()->getPredictionsForToken(info.vsyncId);
+ if (prediction.has_value()) return prediction->presentTime;
+
+ return static_cast<nsecs_t>(0);
+ }();
+ mFlinger->mScheduler->recordLayerHistory(this, presentTime,
LayerHistory::LayerUpdateType::Buffer);
- addFrameEvent(acquireFence, postTime, desiredPresentTime);
+ addFrameEvent(acquireFence, postTime, isAutoTimestamp ? 0 : desiredPresentTime);
+
+ setFrameTimelineVsyncForBufferTransaction(info, postTime);
+
+ if (buffer && dequeueTime && *dequeueTime != 0) {
+ const uint64_t bufferId = buffer->getBuffer()->getId();
+ mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
+ mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, *dequeueTime,
+ FrameTracer::FrameEvent::DEQUEUE);
+ mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, postTime,
+ FrameTracer::FrameEvent::QUEUE);
+ }
+
+ mCurrentState.width = mCurrentState.buffer->getBuffer()->getWidth();
+ mCurrentState.height = mCurrentState.buffer->getBuffer()->getHeight();
+
return true;
}
bool BufferStateLayer::setAcquireFence(const sp<Fence>& fence) {
- // The acquire fences of BufferStateLayers have already signaled before they are set
- mCallbackHandleAcquireTime = fence->getSignalTime();
-
mCurrentState.acquireFence = fence;
+ mCurrentState.acquireFenceTime = std::make_unique<FenceTime>(fence);
+
+ // The acquire fences of BufferStateLayers have already signaled before they are set
+ mCallbackHandleAcquireTime = mCurrentState.acquireFenceTime->getSignalTime();
+
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
@@ -355,17 +482,18 @@
if (willPresent) {
// If this transaction set an acquire fence on this layer, set its acquire time
handle->acquireTime = mCallbackHandleAcquireTime;
+ handle->frameNumber = mCurrentState.frameNumber;
// Notify the transaction completed thread that there is a pending latched callback
// handle
- mFlinger->getTransactionCompletedThread().registerPendingCallbackHandle(handle);
+ mFlinger->getTransactionCallbackInvoker().registerPendingCallbackHandle(handle);
// Store so latched time and release fence can be set
mCurrentState.callbackHandles.push_back(handle);
} else { // If this layer will NOT need to be relatched and presented this frame
// Notify the transaction completed thread this handle is done
- mFlinger->getTransactionCompletedThread().registerUnpresentedCallbackHandle(handle);
+ mFlinger->getTransactionCallbackInvoker().registerUnpresentedCallbackHandle(handle);
}
}
@@ -375,11 +503,6 @@
return willPresent;
}
-void BufferStateLayer::forceSendCallbacks() {
- mFlinger->getTransactionCompletedThread().finalizePendingCallbackHandles(
- mCurrentState.callbackHandles);
-}
-
bool BufferStateLayer::setTransparentRegionHint(const Region& transparent) {
mCurrentState.transparentRegionHint = transparent;
mCurrentState.modified = true;
@@ -393,6 +516,10 @@
return Rect(getActiveWidth(s), getActiveHeight(s));
}
+ if (mBufferInfo.mBuffer == nullptr) {
+ return Rect::INVALID_RECT;
+ }
+
// if the display frame is not defined, use the parent bounds as the buffer size.
const auto& p = mDrawingParent.promote();
if (p != nullptr) {
@@ -422,10 +549,6 @@
// Interface implementation for BufferLayer
// -----------------------------------------------------------------------
bool BufferStateLayer::fenceHasSignaled() const {
- if (latchUnsignaledBuffers()) {
- return true;
- }
-
const bool fenceSignaled =
getDrawingState().acquireFence->getStatus() == Fence::Status::Signaled;
if (!fenceSignaled) {
@@ -441,7 +564,7 @@
return true;
}
- return mCurrentState.desiredPresentTime <= expectedPresentTime;
+ return mCurrentState.isAutoTimestamp || mCurrentState.desiredPresentTime <= expectedPresentTime;
}
bool BufferStateLayer::onPreComposition(nsecs_t refreshStartTime) {
@@ -481,13 +604,10 @@
return mCurrentState.frameNumber;
}
-bool BufferStateLayer::getAutoRefresh() const {
- // TODO(marissaw): support shared buffer mode
- return false;
-}
-
-bool BufferStateLayer::getSidebandStreamChanged() const {
- return mSidebandStreamChanged.load();
+void BufferStateLayer::setAutoRefresh(bool autoRefresh) {
+ if (!mAutoRefresh.exchange(autoRefresh)) {
+ mFlinger->signalLayerUpdate();
+ }
}
bool BufferStateLayer::latchSidebandStream(bool& recomputeVisibleRegions) {
@@ -512,13 +632,6 @@
return mCurrentStateModified && (c.buffer != nullptr || c.bgColorLayer != nullptr);
}
-status_t BufferStateLayer::bindTextureImage() {
- const State& s(getDrawingState());
- auto& engine(mFlinger->getRenderEngine());
-
- return engine.bindExternalTextureBuffer(mTextureName, s.buffer, s.acquireFence);
-}
-
status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime,
nsecs_t /*expectedPresentTime*/) {
const State& s(getDrawingState());
@@ -532,54 +645,39 @@
return NO_ERROR;
}
- const int32_t layerId = getSequence();
-
- // Reject if the layer is invalid
- uint32_t bufferWidth = s.buffer->width;
- uint32_t bufferHeight = s.buffer->height;
-
- if (s.transform & ui::Transform::ROT_90) {
- std::swap(bufferWidth, bufferHeight);
- }
-
- if (s.transformToDisplayInverse) {
- uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
- if (invTransform & ui::Transform::ROT_90) {
- std::swap(bufferWidth, bufferHeight);
- }
- }
-
- if (getEffectiveScalingMode() == NATIVE_WINDOW_SCALING_MODE_FREEZE &&
- (s.active.w != bufferWidth || s.active.h != bufferHeight)) {
- ALOGE("[%s] rejecting buffer: "
- "bufferWidth=%d, bufferHeight=%d, front.active.{w=%d, h=%d}",
- getDebugName(), bufferWidth, bufferHeight, s.active.w, s.active.h);
- mFlinger->mTimeStats->removeTimeRecord(layerId, mDrawingState.frameNumber);
- return BAD_VALUE;
- }
-
for (auto& handle : mDrawingState.callbackHandles) {
- handle->latchTime = latchTime;
- handle->frameNumber = mDrawingState.frameNumber;
- }
-
- if (!SyncFeatures::getInstance().useNativeFenceSync()) {
- // Bind the new buffer to the GL texture.
- //
- // Older devices require the "implicit" synchronization provided
- // by glEGLImageTargetTexture2DOES, which this method calls. Newer
- // devices will either call this in Layer::onDraw, or (if it's not
- // a GL-composited layer) not at all.
- status_t err = bindTextureImage();
- if (err != NO_ERROR) {
- mFlinger->mTimeStats->onDestroy(layerId);
- return BAD_VALUE;
+ if (handle->frameNumber == mDrawingState.frameNumber) {
+ handle->latchTime = latchTime;
}
}
- mFlinger->mTimeStats->setAcquireFence(layerId, mDrawingState.frameNumber,
- std::make_shared<FenceTime>(mDrawingState.acquireFence));
- mFlinger->mTimeStats->setLatchTime(layerId, mDrawingState.frameNumber, latchTime);
+ const int32_t layerId = getSequence();
+ const uint64_t bufferId = mDrawingState.buffer->getBuffer()->getId();
+ const uint64_t frameNumber = mDrawingState.frameNumber;
+ const auto acquireFence = std::make_shared<FenceTime>(mDrawingState.acquireFence);
+ mFlinger->mTimeStats->setAcquireFence(layerId, frameNumber, acquireFence);
+ mFlinger->mTimeStats->setLatchTime(layerId, frameNumber, latchTime);
+
+ mFlinger->mFrameTracer->traceFence(layerId, bufferId, frameNumber, acquireFence,
+ FrameTracer::FrameEvent::ACQUIRE_FENCE);
+ mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, latchTime,
+ FrameTracer::FrameEvent::LATCH);
+
+ auto& bufferSurfaceFrame = mDrawingState.bufferSurfaceFrameTX;
+ if (bufferSurfaceFrame != nullptr &&
+ bufferSurfaceFrame->getPresentState() != PresentState::Presented) {
+ // Update only if the bufferSurfaceFrame wasn't already presented. A Presented
+ // bufferSurfaceFrame could be seen here if a pending state was applied successfully and we
+ // are processing the next state.
+ addSurfaceFramePresentedForBuffer(bufferSurfaceFrame,
+ mDrawingState.acquireFenceTime->getSignalTime(),
+ latchTime);
+ }
+
+ std::deque<sp<CallbackHandle>> remainingHandles;
+ mFlinger->getTransactionCallbackInvoker()
+ .finalizeOnCommitCallbackHandles(mDrawingState.callbackHandles, remainingHandles);
+ mDrawingState.callbackHandles = remainingHandles;
mCurrentStateModified = false;
@@ -593,6 +691,10 @@
return BAD_VALUE;
}
+ if (!mBufferInfo.mBuffer || s.buffer->getBuffer() != mBufferInfo.mBuffer->getBuffer()) {
+ decrementPendingBufferCount();
+ }
+
mPreviousBufferId = getCurrentBufferId();
mBufferInfo.mBuffer = s.buffer;
mBufferInfo.mFence = s.acquireFence;
@@ -691,9 +793,9 @@
mBufferInfo.mDesiredPresentTime = s.desiredPresentTime;
mBufferInfo.mFenceTime = std::make_shared<FenceTime>(s.acquireFence);
mBufferInfo.mFence = s.acquireFence;
- mBufferInfo.mTransform = s.transform;
+ mBufferInfo.mTransform = s.bufferTransform;
mBufferInfo.mDataspace = translateDataspace(s.dataspace);
- mBufferInfo.mCrop = computeCrop(s);
+ mBufferInfo.mCrop = computeBufferCrop(s);
mBufferInfo.mScaleMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
mBufferInfo.mSurfaceDamage = s.surfaceDamageRegion;
mBufferInfo.mHdrMetadata = s.hdrMetadata;
@@ -702,27 +804,15 @@
mBufferInfo.mBufferSlot = mHwcSlotGenerator->getHwcCacheSlot(s.clientCacheId);
}
-Rect BufferStateLayer::computeCrop(const State& s) {
- if (s.crop.isEmpty() && s.buffer) {
- return s.buffer->getBounds();
- } else if (s.buffer) {
- Rect crop = s.crop;
- crop.left = std::max(crop.left, 0);
- crop.top = std::max(crop.top, 0);
- uint32_t bufferWidth = s.buffer->getWidth();
- uint32_t bufferHeight = s.buffer->getHeight();
- if (bufferHeight <= std::numeric_limits<int32_t>::max() &&
- bufferWidth <= std::numeric_limits<int32_t>::max()) {
- crop.right = std::min(crop.right, static_cast<int32_t>(bufferWidth));
- crop.bottom = std::min(crop.bottom, static_cast<int32_t>(bufferHeight));
- }
- if (!crop.isValid()) {
- // Crop rect is out of bounds, return whole buffer
- return s.buffer->getBounds();
- }
- return crop;
+uint32_t BufferStateLayer::getEffectiveScalingMode() const {
+ return NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
+}
+
+Rect BufferStateLayer::computeBufferCrop(const State& s) {
+ if (s.buffer) {
+ return s.buffer->getBuffer()->getBounds();
}
- return s.crop;
+ return Rect::INVALID_RECT;
}
sp<Layer> BufferStateLayer::createClone() {
@@ -734,34 +824,85 @@
return layer;
}
-Layer::RoundedCornerState BufferStateLayer::getRoundedCornerState() const {
- const auto& p = mDrawingParent.promote();
- if (p != nullptr) {
- RoundedCornerState parentState = p->getRoundedCornerState();
- if (parentState.radius > 0) {
- ui::Transform t = getActiveTransform(getDrawingState());
- t = t.inverse();
- parentState.cropRect = t.transform(parentState.cropRect);
- // The rounded corners shader only accepts 1 corner radius for performance reasons,
- // but a transform matrix can define horizontal and vertical scales.
- // Let's take the average between both of them and pass into the shader, practically we
- // never do this type of transformation on windows anyway.
- parentState.radius *= (t[0][0] + t[1][1]) / 2.0f;
- return parentState;
+bool BufferStateLayer::bufferNeedsFiltering() const {
+ const State& s(getDrawingState());
+ if (!s.buffer) {
+ return false;
+ }
+
+ uint32_t bufferWidth = s.buffer->getBuffer()->width;
+ uint32_t bufferHeight = s.buffer->getBuffer()->height;
+
+ // Undo any transformations on the buffer and return the result.
+ if (s.bufferTransform & ui::Transform::ROT_90) {
+ std::swap(bufferWidth, bufferHeight);
+ }
+
+ if (s.transformToDisplayInverse) {
+ uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
+ if (invTransform & ui::Transform::ROT_90) {
+ std::swap(bufferWidth, bufferHeight);
}
}
- const float radius = getDrawingState().cornerRadius;
- const State& s(getDrawingState());
- if (radius <= 0 || (getActiveWidth(s) == UINT32_MAX && getActiveHeight(s) == UINT32_MAX))
- return RoundedCornerState();
- return RoundedCornerState(FloatRect(static_cast<float>(s.active.transform.tx()),
- static_cast<float>(s.active.transform.ty()),
- static_cast<float>(s.active.transform.tx() + s.active.w),
- static_cast<float>(s.active.transform.ty() + s.active.h)),
- radius);
+
+ const Rect layerSize{getBounds()};
+ return layerSize.width() != bufferWidth || layerSize.height() != bufferHeight;
+}
+
+void BufferStateLayer::decrementPendingBufferCount() {
+ int32_t pendingBuffers = --mPendingBufferTransactions;
+ tracePendingBufferCount(pendingBuffers);
+}
+
+void BufferStateLayer::tracePendingBufferCount(int32_t pendingBuffers) {
+ ATRACE_INT(mBlastTransactionName.c_str(), pendingBuffers);
+}
+
+void BufferStateLayer::bufferMayChange(const sp<GraphicBuffer>& newBuffer) {
+ if (mDrawingState.buffer != nullptr &&
+ (!mBufferInfo.mBuffer ||
+ mDrawingState.buffer->getBuffer() != mBufferInfo.mBuffer->getBuffer()) &&
+ newBuffer != mDrawingState.buffer->getBuffer()) {
+ // If we are about to update mDrawingState.buffer but it has not yet latched
+ // then we will drop a buffer and should decrement the pending buffer count and
+ // call any release buffer callbacks if set.
+ callReleaseBufferCallback(mDrawingState.releaseBufferListener,
+ mDrawingState.buffer->getBuffer(), mDrawingState.acquireFence);
+ decrementPendingBufferCount();
+ }
+}
+
+/*
+ * We don't want to send the layer's transform to input, but rather the
+ * parent's transform. This is because BufferStateLayer's transform is
+ * information about how the buffer is placed on screen. The parent's
+ * transform makes more sense to send since it's information about how the
+ * layer is placed on screen. This transform is used by input to determine
+ * how to go from screen space back to window space.
+ */
+ui::Transform BufferStateLayer::getInputTransform() const {
+ sp<Layer> parent = mDrawingParent.promote();
+ if (parent == nullptr) {
+ return ui::Transform();
+ }
+
+ return parent->getTransform();
+}
+
+/**
+ * Similar to getInputTransform, we need to update the bounds to include the transform.
+ * This is because bounds for BSL doesn't include buffer transform, where the input assumes
+ * that's already included.
+ */
+Rect BufferStateLayer::getInputBounds() const {
+ Rect bufferBounds = getCroppedBufferSize(getDrawingState());
+ if (mDrawingState.transform.getType() == ui::Transform::IDENTITY || !bufferBounds.isValid()) {
+ return bufferBounds;
+ }
+ return mDrawingState.transform.transform(bufferBounds);
}
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 00fa7f7..af4fcae 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -36,9 +36,7 @@
~BufferStateLayer() override;
- // -----------------------------------------------------------------------
- // Interface implementation for Layer
- // -----------------------------------------------------------------------
+ // Implements Layer.
const char* getType() const override { return "BufferStateLayer"; }
void onLayerDisplayed(const sp<Fence>& releaseFence) override;
@@ -47,21 +45,15 @@
void finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& glDoneFence,
const CompositorTiming& compositorTiming) override;
- bool shouldPresentNow(nsecs_t expectedPresentTime) const override;
+ bool isBufferDue(nsecs_t /*expectedPresentTime*/) const override { return true; }
uint32_t doTransactionResize(uint32_t flags, Layer::State* /*stateToCommit*/) override {
return flags;
}
- /*TODO:vhau return to using BufferStateLayer override once WM
- * has removed deferred transactions!
- void pushPendingState() override;*/
- bool applyPendingStates(Layer::State* stateToCommit) override;
- uint32_t getActiveWidth(const Layer::State& s) const override { return s.active.w; }
- uint32_t getActiveHeight(const Layer::State& s) const override { return s.active.h; }
- ui::Transform getActiveTransform(const Layer::State& s) const override {
- return s.active.transform;
- }
+ uint32_t getActiveWidth(const Layer::State& s) const override { return s.width; }
+ uint32_t getActiveHeight(const Layer::State& s) const override { return s.height; }
+ ui::Transform getActiveTransform(const Layer::State& s) const override { return s.transform; }
Region getActiveTransparentRegion(const Layer::State& s) const override {
return s.transparentRegionHint;
}
@@ -70,9 +62,11 @@
bool setTransform(uint32_t transform) override;
bool setTransformToDisplayInverse(bool transformToDisplayInverse) override;
bool setCrop(const Rect& crop) override;
- bool setFrame(const Rect& frame) override;
- bool setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence, nsecs_t postTime,
- nsecs_t desiredPresentTime, const client_cache_t& clientCacheId) override;
+ bool setBuffer(const std::shared_ptr<renderengine::ExternalTexture>& buffer,
+ const sp<Fence>& acquireFence, nsecs_t postTime, nsecs_t desiredPresentTime,
+ bool isAutoTimestamp, const client_cache_t& clientCacheId, uint64_t frameNumber,
+ std::optional<nsecs_t> dequeueTime, const FrameTimelineInfo& info,
+ const sp<ITransactionCompletedListener>& transactionListener) override;
bool setAcquireFence(const sp<Fence>& fence) override;
bool setDataspace(ui::Dataspace dataspace) override;
bool setHdrMetadata(const HdrMetadata& hdrMetadata) override;
@@ -80,28 +74,19 @@
bool setApi(int32_t api) override;
bool setSidebandStream(const sp<NativeHandle>& sidebandStream) override;
bool setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& handles) override;
- void forceSendCallbacks() override;
bool addFrameEvent(const sp<Fence>& acquireFence, nsecs_t postedTime,
nsecs_t requestedPresentTime) override;
+ bool setPosition(float /*x*/, float /*y*/) override;
+ bool setMatrix(const layer_state_t::matrix22_t& /*matrix*/,
+ bool /*allowNonRectPreservingTransforms*/);
// Override to ignore legacy layer state properties that are not used by BufferStateLayer
bool setSize(uint32_t /*w*/, uint32_t /*h*/) override { return false; }
- bool setPosition(float /*x*/, float /*y*/) override { return false; }
bool setTransparentRegionHint(const Region& transparent) override;
- bool setMatrix(const layer_state_t::matrix22_t& /*matrix*/,
- bool /*allowNonRectPreservingTransforms*/) override {
- return false;
- }
- bool setCrop_legacy(const Rect& /*crop*/) override { return false; }
- bool setOverrideScalingMode(int32_t /*overrideScalingMode*/) override { return false; }
- void deferTransactionUntil_legacy(const sp<IBinder>& /*barrierHandle*/,
- uint64_t /*frameNumber*/) override {}
- void deferTransactionUntil_legacy(const sp<Layer>& /*barrierLayer*/,
- uint64_t /*frameNumber*/) override {}
Rect getBufferSize(const State& s) const override;
FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
- Layer::RoundedCornerState getRoundedCornerState() const override;
+ void setAutoRefresh(bool autoRefresh) override;
// -----------------------------------------------------------------------
@@ -111,25 +96,41 @@
bool fenceHasSignaled() const override;
bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const override;
bool onPreComposition(nsecs_t refreshStartTime) override;
+ uint32_t getEffectiveScalingMode() const override;
+
+ // See mPendingBufferTransactions
+ void decrementPendingBufferCount();
+ void bufferMayChange(const sp<GraphicBuffer>& newBuffer) override;
+ std::atomic<int32_t>* getPendingBufferCounter() override { return &mPendingBufferTransactions; }
+ std::string getPendingBufferCounterName() override { return mBlastTransactionName; }
+
+ bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const override { return true; }
protected:
void gatherBufferInfo() override;
uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const;
+ void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame);
+ ui::Transform getInputTransform() const override;
+ Rect getInputBounds() const override;
private:
+ friend class SlotGenerationTest;
+ friend class TransactionFrameTracerTest;
+ friend class TransactionSurfaceFrameTest;
+
+ inline void tracePendingBufferCount(int32_t pendingBuffers);
+
bool updateFrameEventHistory(const sp<Fence>& acquireFence, nsecs_t postedTime,
nsecs_t requestedPresentTime);
- uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override;
+ status_t addReleaseFence(const sp<CallbackHandle>& ch, const sp<Fence>& releaseFence);
- bool getAutoRefresh() const override;
- bool getSidebandStreamChanged() const override;
+ uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override;
bool latchSidebandStream(bool& recomputeVisibleRegions) override;
bool hasFrameUpdate() const override;
- status_t bindTextureImage() override;
status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
nsecs_t expectedPresentTime) override;
@@ -139,18 +140,16 @@
sp<Layer> createClone() override;
// Crop that applies to the buffer
- Rect computeCrop(const State& s);
+ Rect computeBufferCrop(const State& s);
-private:
- friend class SlotGenerationTest;
bool willPresentCurrentTransaction() const;
+ bool bufferNeedsFiltering() const override;
+
static const std::array<float, 16> IDENTITY_MATRIX;
std::unique_ptr<renderengine::Image> mTextureImage;
- std::atomic<bool> mSidebandStreamChanged{false};
-
mutable uint64_t mFrameNumber{0};
uint64_t mFrameCounter{0};
@@ -158,10 +157,28 @@
uint64_t mPreviousBufferId = 0;
uint64_t mPreviousReleasedFrameNumber = 0;
- mutable bool mCurrentStateModified = false;
bool mReleasePreviousBuffer = false;
+
+ // Stores the last set acquire fence signal time used to populate the callback handle's acquire
+ // time.
nsecs_t mCallbackHandleAcquireTime = -1;
+ std::deque<std::shared_ptr<android::frametimeline::SurfaceFrame>> mPendingJankClassifications;
+ // An upper bound on the number of SurfaceFrames in the pending classifications deque.
+ static constexpr int kPendingClassificationMaxSurfaceFrames = 25;
+
+ const std::string mBlastTransactionName{"BufferTX - " + mName};
+ // This integer is incremented everytime a buffer arrives at the server for this layer,
+ // and decremented when a buffer is dropped or latched. When changed the integer is exported
+ // to systrace with ATRACE_INT and mBlastTransactionName. This way when debugging perf it is
+ // possible to see when a buffer arrived at the server, and in which frame it latched.
+ //
+ // You can understand the trace this way:
+ // - If the integer increases, a buffer arrived at the server.
+ // - If the integer decreases in latchBuffer, that buffer was latched
+ // - If the integer decreases in setBuffer or doTransaction, a buffer was dropped
+ std::atomic<int32_t> mPendingBufferTransactions{0};
+
// TODO(marissaw): support sticky transform for LEGACY camera mode
class HwcSlotGenerator : public ClientCache::ErasedRecipient {
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index 78bbcba..aac6c91 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -79,17 +79,18 @@
status_t Client::createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
uint32_t flags, const sp<IBinder>& parentHandle,
LayerMetadata metadata, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp, uint32_t* outTransformHint) {
+ sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
+ uint32_t* outTransformHint) {
// We rely on createLayer to check permissions.
return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
- parentHandle, nullptr, outTransformHint);
+ parentHandle, outLayerId, nullptr, outTransformHint);
}
status_t Client::createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h,
PixelFormat format, uint32_t flags,
const sp<IGraphicBufferProducer>& parent,
LayerMetadata metadata, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp,
+ sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
uint32_t* outTransformHint) {
if (mFlinger->authenticateSurfaceTexture(parent) == false) {
ALOGE("failed to authenticate surface texture");
@@ -103,11 +104,12 @@
}
return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
- nullptr, layer, outTransformHint);
+ nullptr, outLayerId, layer, outTransformHint);
}
-status_t Client::mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle) {
- return mFlinger->mirrorLayer(this, mirrorFromHandle, outHandle);
+status_t Client::mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle,
+ int32_t* outLayerId) {
+ return mFlinger->mirrorLayer(this, mirrorFromHandle, outHandle, outLayerId);
}
status_t Client::clearLayerFrameStats(const sp<IBinder>& handle) const {
diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h
index e9063e5..15cd763 100644
--- a/services/surfaceflinger/Client.h
+++ b/services/surfaceflinger/Client.h
@@ -28,13 +28,9 @@
namespace android {
-// ---------------------------------------------------------------------------
-
class Layer;
class SurfaceFlinger;
-// ---------------------------------------------------------------------------
-
class Client : public BnSurfaceComposerClient
{
public:
@@ -54,17 +50,18 @@
virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
uint32_t flags, const sp<IBinder>& parent,
LayerMetadata metadata, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp,
+ sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
uint32_t* outTransformHint = nullptr);
virtual status_t createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h,
PixelFormat format, uint32_t flags,
const sp<IGraphicBufferProducer>& parent,
LayerMetadata metadata, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp,
+ sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
uint32_t* outTransformHint = nullptr);
- status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* handle);
+ status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* handle,
+ int32_t* outLayerId);
virtual status_t clearLayerFrameStats(const sp<IBinder>& handle) const;
@@ -80,7 +77,6 @@
mutable Mutex mLock;
};
-// ---------------------------------------------------------------------------
}; // namespace android
#endif // ANDROID_SF_CLIENT_H
diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp
index a5be01c..f310738 100644
--- a/services/surfaceflinger/ClientCache.cpp
+++ b/services/surfaceflinger/ClientCache.cpp
@@ -25,6 +25,8 @@
namespace android {
+using base::StringAppendF;
+
ANDROID_SINGLETON_STATIC_INSTANCE(ClientCache);
ClientCache::ClientCache() : mDeathRecipient(new CacheDeathRecipient) {}
@@ -46,7 +48,7 @@
auto bufItr = processBuffers.find(id);
if (bufItr == processBuffers.end()) {
- ALOGE("failed to get buffer, invalid buffer id");
+ ALOGV("failed to get buffer, invalid buffer id");
return false;
}
@@ -100,7 +102,12 @@
return false;
}
- processBuffers[id].buffer = buffer;
+ LOG_ALWAYS_FATAL_IF(mRenderEngine == nullptr,
+ "Attempted to build the ClientCache before a RenderEngine instance was "
+ "ready!");
+ processBuffers[id].buffer = std::make_shared<
+ renderengine::ExternalTexture>(buffer, *mRenderEngine,
+ renderengine::ExternalTexture::Usage::READABLE);
return true;
}
@@ -130,7 +137,7 @@
}
}
-sp<GraphicBuffer> ClientCache::get(const client_cache_t& cacheId) {
+std::shared_ptr<renderengine::ExternalTexture> ClientCache::get(const client_cache_t& cacheId) {
std::lock_guard lock(mMutex);
ClientCacheBuffer* buf = nullptr;
@@ -148,7 +155,7 @@
ClientCacheBuffer* buf = nullptr;
if (!getBuffer(cacheId, &buf)) {
- ALOGE("failed to register erased recipient, could not retrieve buffer");
+ ALOGV("failed to register erased recipient, could not retrieve buffer");
return false;
}
buf->recipients.insert(recipient);
@@ -203,4 +210,18 @@
ClientCache::getInstance().removeProcess(who);
}
+void ClientCache::dump(std::string& result) {
+ std::lock_guard lock(mMutex);
+ for (auto i : mBuffers) {
+ const sp<IBinder>& cacheOwner = i.second.first;
+ StringAppendF(&result," Cache owner: %p\n", cacheOwner.get());
+ auto &buffers = i.second.second;
+ for (auto& [id, clientCacheBuffer] : buffers) {
+ StringAppendF(&result, "\t ID: %d, Width/Height: %d,%d\n", (int)id,
+ (int)clientCacheBuffer.buffer->getBuffer()->getWidth(),
+ (int)clientCacheBuffer.buffer->getBuffer()->getHeight());
+ }
+ }
+}
+
}; // namespace android
diff --git a/services/surfaceflinger/ClientCache.h b/services/surfaceflinger/ClientCache.h
index d7af7c0..a9b8177 100644
--- a/services/surfaceflinger/ClientCache.h
+++ b/services/surfaceflinger/ClientCache.h
@@ -19,6 +19,7 @@
#include <android-base/thread_annotations.h>
#include <binder/IBinder.h>
#include <gui/LayerState.h>
+#include <renderengine/RenderEngine.h>
#include <ui/GraphicBuffer.h>
#include <utils/RefBase.h>
#include <utils/Singleton.h>
@@ -39,7 +40,11 @@
bool add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer);
void erase(const client_cache_t& cacheId);
- sp<GraphicBuffer> get(const client_cache_t& cacheId);
+ std::shared_ptr<renderengine::ExternalTexture> get(const client_cache_t& cacheId);
+
+ // Always called immediately after setup. Will be set to non-null, and then should never be
+ // called again.
+ void setRenderEngine(renderengine::RenderEngine* renderEngine) { mRenderEngine = renderEngine; }
void removeProcess(const wp<IBinder>& processToken);
@@ -53,11 +58,13 @@
void unregisterErasedRecipient(const client_cache_t& cacheId,
const wp<ErasedRecipient>& recipient);
+ void dump(std::string& result);
+
private:
std::mutex mMutex;
struct ClientCacheBuffer {
- sp<GraphicBuffer> buffer;
+ std::shared_ptr<renderengine::ExternalTexture> buffer;
std::set<wp<ErasedRecipient>> recipients;
};
std::map<wp<IBinder> /*caching process*/,
@@ -71,6 +78,7 @@
};
sp<CacheDeathRecipient> mDeathRecipient;
+ renderengine::RenderEngine* mRenderEngine = nullptr;
bool getBuffer(const client_cache_t& cacheId, ClientCacheBuffer** outClientCacheBuffer)
REQUIRES(mMutex);
diff --git a/services/surfaceflinger/Clock.h b/services/surfaceflinger/Clock.h
new file mode 100644
index 0000000..3f23c6d
--- /dev/null
+++ b/services/surfaceflinger/Clock.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#pragma once
+
+#include <chrono>
+
+namespace android {
+
+// Abstract interface for timekeeping which can be injected for unit tests.
+class Clock {
+public:
+ Clock() = default;
+ virtual ~Clock() = default;
+
+ // Returns the current time
+ virtual std::chrono::steady_clock::time_point now() const = 0;
+};
+
+class SteadyClock : public Clock {
+public:
+ virtual ~SteadyClock() = default;
+
+ std::chrono::steady_clock::time_point now() const override {
+ return std::chrono::steady_clock::now();
+ }
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Colorizer.h b/services/surfaceflinger/Colorizer.h
index b7d61ce..b0cc030 100644
--- a/services/surfaceflinger/Colorizer.h
+++ b/services/surfaceflinger/Colorizer.h
@@ -21,8 +21,6 @@
namespace android {
-// ---------------------------------------------------------------------------
-
class Colorizer {
bool mEnabled;
public:
@@ -59,9 +57,6 @@
}
};
-// ---------------------------------------------------------------------------
-
-}; // namespace android
-
+} // namespace android
#endif /* ANDROID_SURFACE_FLINGER_COLORIZER_H */
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 3f63951..08147ed 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -29,6 +29,7 @@
"liblog",
"libnativewindow",
"libprotobuf-cpp-lite",
+ "libSurfaceFlingerProp",
"libtimestats",
"libui",
"libutils",
@@ -51,6 +52,11 @@
name: "libcompositionengine",
defaults: ["libcompositionengine_defaults"],
srcs: [
+ "src/planner/CachedSet.cpp",
+ "src/planner/Flattener.cpp",
+ "src/planner/LayerState.cpp",
+ "src/planner/Planner.cpp",
+ "src/planner/Predictor.cpp",
"src/ClientCompositionRequestCache.cpp",
"src/CompositionEngine.cpp",
"src/Display.cpp",
@@ -97,6 +103,10 @@
test_suites: ["device-tests"],
defaults: ["libcompositionengine_defaults"],
srcs: [
+ "tests/planner/CachedSetTest.cpp",
+ "tests/planner/FlattenerTest.cpp",
+ "tests/planner/LayerStateTest.cpp",
+ "tests/planner/PredictorTest.cpp",
"tests/CompositionEngineTest.cpp",
"tests/DisplayColorProfileTest.cpp",
"tests/DisplayTest.cpp",
@@ -104,8 +114,9 @@
"tests/MockHWC2.cpp",
"tests/MockHWComposer.cpp",
"tests/MockPowerAdvisor.cpp",
- "tests/OutputTest.cpp",
"tests/OutputLayerTest.cpp",
+ "tests/OutputTest.cpp",
+ "tests/ProjectionSpaceTest.cpp",
"tests/RenderSurfaceTest.cpp",
],
static_libs: [
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index a0606b4..289cb11 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -79,6 +79,9 @@
// If set, causes the dirty regions to flash with the delay
std::optional<std::chrono::microseconds> devOptFlashDirtyRegionsDelay;
+
+ // The earliest time to send the present command to the HAL
+ std::chrono::steady_clock::time_point earliestPresentTime;
};
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
index a38d1f3..01dd534 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
@@ -34,8 +34,8 @@
*/
class Display : public virtual Output {
public:
- // Gets the HWC DisplayId for the display if there is one
- virtual const std::optional<DisplayId>& getId() const = 0;
+ // Gets the DisplayId for the display
+ virtual DisplayId getId() const = 0;
// True if the display is secure
virtual bool isSecure() const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
index 67e6deb..df44e75 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
@@ -22,11 +22,12 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
#include <ui/GraphicTypes.h>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
namespace android {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h
index 7eb8eb1..1136e3d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h
@@ -23,11 +23,12 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
#include <ui/GraphicTypes.h>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
#include <ui/HdrCapabilities.h>
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
index 6bc677d..633668e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
@@ -20,12 +20,14 @@
#include <optional>
#include <string>
-#include <ui/DisplayInfo.h>
+#include <ui/DisplayId.h>
#include <ui/PixelFormat.h>
#include <ui/Size.h>
+#include <ui/StaticDisplayInfo.h>
#include "DisplayHardware/DisplayIdentification.h"
#include "DisplayHardware/PowerAdvisor.h"
+#include "DisplayIdGenerator.h"
namespace android::compositionengine {
@@ -37,7 +39,7 @@
struct DisplayCreationArgs {
struct Physical {
DisplayId id;
- DisplayConnectionType type;
+ ui::DisplayConnectionType type;
};
// Required for physical displays. Gives the HWC display id for the existing
@@ -65,6 +67,9 @@
// Debugging. Human readable name for the display.
std::string name;
+
+ // Generator for IDs of virtual displays, which are backed by the GPU.
+ DisplayIdGenerator<GpuVirtualDisplayId>* gpuVirtualDisplayIdGenerator;
};
/**
@@ -95,6 +100,12 @@
return *this;
}
+ DisplayCreationArgsBuilder& setGpuVirtualDisplayIdGenerator(
+ DisplayIdGenerator<GpuVirtualDisplayId>& generator) {
+ mArgs.gpuVirtualDisplayIdGenerator = &generator;
+ return *this;
+ }
+
DisplayCreationArgsBuilder& setIsSecure(bool isSecure) {
mArgs.isSecure = isSecure;
return *this;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
index 6559ed8..4502eee 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
@@ -16,6 +16,7 @@
#pragma once
+#include <ui/Size.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/StrongPointer.h>
@@ -71,7 +72,7 @@
virtual void dumpAsString(String8& result) const = 0;
- virtual void resizeBuffers(const uint32_t w, const uint32_t h) = 0;
+ virtual void resizeBuffers(const ui::Size&) = 0;
virtual const sp<Fence>& getClientTargetAcquireFence() const = 0;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 6cc90cb..791e7db 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -23,11 +23,12 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
#include <renderengine/LayerSettings.h>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
#include <utils/RefBase.h>
#include <utils/Timers.h>
@@ -80,10 +81,6 @@
// The clip region, or visible region that is being rendered to
const Region& clip;
- // If true, the layer should use an identity transform for its position
- // transform. Used only by the captureScreen API call.
- const bool useIdentityTransform;
-
// If set to true, the layer should enable filtering when rendering.
const bool needsFiltering;
@@ -112,6 +109,9 @@
// If set to true, change the layer settings to render a clear output.
// This may be requested by the HWC
const bool clearContent;
+
+ // If set to true, change the layer settings to not use any blurs.
+ const bool disableBlurs;
};
// A superset of LayerSettings required by RenderEngine to compose a layer
@@ -135,6 +135,12 @@
// Gets some kind of identifier for the layer for debug purposes.
virtual const char* getDebugName() const = 0;
+
+ // Gets the sequence number: a serial number that uniquely identifies a Layer
+ virtual int32_t getSequence() const = 0;
+
+ // Whether the layer should be rendered with rounded corners.
+ virtual bool hasRoundedCorners() const = 0;
};
// TODO(b/121291683): Specialize std::hash<> for sp<T> so these and others can
@@ -148,7 +154,6 @@
static inline bool operator==(const LayerFE::ClientCompositionTargetSettings& lhs,
const LayerFE::ClientCompositionTargetSettings& rhs) {
return lhs.clip.hasSameRects(rhs.clip) &&
- lhs.useIdentityTransform == rhs.useIdentityTransform &&
lhs.needsFiltering == rhs.needsFiltering && lhs.isSecure == rhs.isSecure &&
lhs.supportsProtectedContent == rhs.supportsProtectedContent &&
lhs.clearRegion.hasSameRects(rhs.clearRegion) && lhs.viewport == rhs.viewport &&
@@ -170,7 +175,6 @@
*os << "ClientCompositionTargetSettings{";
*os << "\n .clip = \n";
PrintTo(settings.clip, os);
- *os << "\n .useIdentityTransform = " << settings.useIdentityTransform;
*os << "\n .needsFiltering = " << settings.needsFiltering;
*os << "\n .isSecure = " << settings.isSecure;
*os << "\n .supportsProtectedContent = " << settings.supportsProtectedContent;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 77400eb..a45be8a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -20,6 +20,7 @@
#include <gui/HdrMetadata.h>
#include <math/mat4.h>
+#include <ui/BlurRegion.h>
#include <ui/FloatRect.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -28,15 +29,17 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
#include <gui/BufferQueue.h>
#include <ui/GraphicBuffer.h>
#include <ui/GraphicTypes.h>
+#include <ui/StretchEffect.h>
#include "DisplayHardware/Hal.h"
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
namespace android::compositionengine {
@@ -52,6 +55,16 @@
std::vector<uint8_t> value;
std::string dumpAsString() const;
+
+ struct Hasher {
+ size_t operator()(const GenericLayerMetadataEntry& entry) const {
+ size_t hash = 0;
+ for (const auto value : entry.value) {
+ hashCombineSingleHashed(hash, value);
+ }
+ return hash;
+ }
+ };
};
inline bool operator==(const GenericLayerMetadataEntry& lhs, const GenericLayerMetadataEntry& rhs) {
@@ -67,6 +80,8 @@
/*
* Used by LayerFE::getCompositionState
+ * Note that fields that affect HW composer state may need to be mirrored into
+ * android::compositionengine::impl::planner::LayerState
*/
struct LayerFECompositionState {
// If set to true, forces client composition on all output layers until
@@ -118,6 +133,11 @@
// length of the shadow in screen space
float shadowRadius{0.f};
+ // List of regions that require blur
+ std::vector<BlurRegion> blurRegions;
+
+ StretchEffect stretchEffect;
+
/*
* Geometry state
*/
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index baf5258..4976213 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -31,6 +31,7 @@
#include <ui/Region.h>
#include <ui/Transform.h>
#include <utils/StrongPointer.h>
+#include <utils/Vector.h>
#include "DisplayHardware/DisplayIdentification.h"
@@ -163,11 +164,15 @@
virtual void setCompositionEnabled(bool) = 0;
// Sets the projection state to use
- virtual void setProjection(const ui::Transform&, uint32_t orientation, const Rect& frame,
- const Rect& viewport, const Rect& sourceClip,
- const Rect& destinationClip, bool needsFiltering) = 0;
+ virtual void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
+ const Rect& orientedDisplaySpaceRect) = 0;
// Sets the bounds to use
- virtual void setBounds(const ui::Size&) = 0;
+ virtual void setDisplaySize(const ui::Size&) = 0;
+ // Gets the transform hint used in layers that belong to this output. Used to guide
+ // composition orientation so that HW overlay can be used when display isn't in its natural
+ // orientation on some devices. Therefore usually we only use transform hint from display
+ // output.
+ virtual ui::Transform::RotationFlags getTransformHint() const = 0;
// Sets the layer stack filtering settings for this output. See
// belongsInOutput for full details.
@@ -179,6 +184,9 @@
// Outputs a string with a state dump
virtual void dump(std::string&) const = 0;
+ // Outputs planner information
+ virtual void dumpPlannerInfo(const Vector<String16>& args, std::string&) const = 0;
+
// Gets the debug name for the output
virtual const std::string& getName() const = 0;
@@ -260,7 +268,9 @@
virtual void ensureOutputLayerIfVisible(sp<LayerFE>&, CoverageState&) = 0;
virtual void setReleasedLayers(const CompositionRefreshArgs&) = 0;
- virtual void updateAndWriteCompositionState(const CompositionRefreshArgs&) = 0;
+ virtual void updateCompositionState(const CompositionRefreshArgs&) = 0;
+ virtual void planComposition() = 0;
+ virtual void writeCompositionState(const CompositionRefreshArgs&) = 0;
virtual void setColorTransform(const CompositionRefreshArgs&) = 0;
virtual void updateColorProfile(const CompositionRefreshArgs&) = 0;
virtual void beginFrame() = 0;
@@ -270,6 +280,7 @@
virtual std::optional<base::unique_fd> composeSurfaces(
const Region&, const compositionengine::CompositionRefreshArgs& refreshArgs) = 0;
virtual void postFramebuffer() = 0;
+ virtual void renderCachedSets() = 0;
virtual void chooseCompositionStrategy() = 0;
virtual bool getSkipColorTransform() const = 0;
virtual FrameFences presentAndGetFrameFences() = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index aa70ef8..ead941d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -16,6 +16,7 @@
#pragma once
+#include <cstdint>
#include <optional>
#include <string>
@@ -25,12 +26,15 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
#include "DisplayHardware/ComposerHal.h"
#include "DisplayHardware/DisplayIdentification.h"
+#include "LayerFE.h"
+
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
namespace android {
@@ -42,7 +46,6 @@
class CompositionEngine;
class Output;
-class LayerFE;
namespace impl {
struct OutputLayerCompositionState;
@@ -87,8 +90,14 @@
// Writes the geometry state to the HWC, or does nothing if this layer does
// not use the HWC. If includeGeometry is false, the geometry state can be
- // skipped.
- virtual void writeStateToHWC(bool includeGeometry) = 0;
+ // skipped. If skipLayer is true, then the alpha of the layer is forced to
+ // 0 so that HWC will ignore it. z specifies the order to draw the layer in
+ // (starting with 0 for the back layer, and increasing for each following
+ // layer). zIsOverridden specifies whether the layer has been reordered.
+ // isPeekingThrough specifies whether this layer will be shown through a
+ // hole punch in a layer above it.
+ virtual void writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z,
+ bool zIsOverridden, bool isPeekingThrough) = 0;
// Updates the cursor position with the HWC
virtual void writeCursorPositionToHWC() const = 0;
@@ -114,6 +123,10 @@
// Returns true if the composition settings scale pixels
virtual bool needsFiltering() const = 0;
+ // Returns a composition list to be used by RenderEngine if the layer has been overridden
+ // during the composition process
+ virtual std::vector<LayerFE::LayerSettings> getOverrideCompositionList() const = 0;
+
// Debugging
virtual void dump(std::string& result) const = 0;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
new file mode 100644
index 0000000..58bb41a
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <ostream>
+
+#include <android-base/stringprintf.h>
+#include <ui/Rect.h>
+#include <ui/Rotation.h>
+#include <ui/Transform.h>
+
+namespace android {
+namespace compositionengine {
+
+// Geometrical space to which content is projected.
+// For example, this can be the layer space or the physical display space.
+struct ProjectionSpace {
+ ProjectionSpace() = default;
+ ProjectionSpace(ui::Size size, Rect content)
+ : bounds(std::move(size)), content(std::move(content)) {}
+
+ // Bounds of this space. Always starts at (0,0).
+ Rect bounds;
+
+ // Rect onto which content is projected.
+ Rect content;
+
+ // The orientation of this space. This value is meaningful only in relation to the rotation
+ // of another projection space and it's used to determine the rotating transformation when
+ // mapping between the two.
+ // As a convention when using this struct orientation = 0 for the "oriented*" projection
+ // spaces. For example when the display is rotated 90 degress counterclockwise, the orientation
+ // of the display space will become 90, while the orientation of the layer stack space will
+ // remain the same.
+ ui::Rotation orientation = ui::ROTATION_0;
+
+ // Returns a transform which maps this.content into destination.content
+ // and also rotates according to this.orientation and destination.orientation
+ ui::Transform getTransform(const ProjectionSpace& destination) const {
+ ui::Rotation rotation = destination.orientation - orientation;
+
+ // Compute a transformation which rotates the destination in a way it has the same
+ // orientation as us.
+ const uint32_t inverseRotationFlags = ui::Transform::toRotationFlags(-rotation);
+ ui::Transform inverseRotatingTransform;
+ inverseRotatingTransform.set(inverseRotationFlags, destination.bounds.width(),
+ destination.bounds.height());
+ // The destination content rotated so it has the same orientation as us.
+ Rect orientedDestContent = inverseRotatingTransform.transform(destination.content);
+
+ // Compute translation from the source content to (0, 0).
+ const float sourceX = content.left;
+ const float sourceY = content.top;
+ ui::Transform sourceTranslation;
+ sourceTranslation.set(-sourceX, -sourceY);
+
+ // Compute scaling transform which maps source content to destination content, assuming
+ // they are both at (0, 0).
+ ui::Transform scale;
+ const float scaleX = static_cast<float>(orientedDestContent.width()) / content.width();
+ const float scaleY = static_cast<float>(orientedDestContent.height()) / content.height();
+ scale.set(scaleX, 0, 0, scaleY);
+
+ // Compute translation from (0, 0) to the orientated destination content.
+ const float destX = orientedDestContent.left;
+ const float destY = orientedDestContent.top;
+ ui::Transform destTranslation;
+ destTranslation.set(destX, destY);
+
+ // Compute rotation transform.
+ const uint32_t orientationFlags = ui::Transform::toRotationFlags(rotation);
+ auto orientedDestWidth = destination.bounds.width();
+ auto orientedDestHeight = destination.bounds.height();
+ if (rotation == ui::ROTATION_90 || rotation == ui::ROTATION_270) {
+ std::swap(orientedDestWidth, orientedDestHeight);
+ }
+ ui::Transform rotationTransform;
+ rotationTransform.set(orientationFlags, orientedDestWidth, orientedDestHeight);
+
+ // The layerStackSpaceRect and orientedDisplaySpaceRect are both in the logical orientation.
+ // Apply the logical translation, scale to physical size, apply the
+ // physical translation and finally rotate to the physical orientation.
+ return rotationTransform * destTranslation * scale * sourceTranslation;
+ }
+
+ bool operator==(const ProjectionSpace& other) const {
+ return bounds == other.bounds && content == other.content &&
+ orientation == other.orientation;
+ }
+};
+
+} // namespace compositionengine
+
+inline std::string to_string(const android::compositionengine::ProjectionSpace& space) {
+ return android::base::
+ StringPrintf("ProjectionSpace(bounds = %s, content = %s, orientation = %s)",
+ to_string(space.bounds).c_str(), to_string(space.content).c_str(),
+ toCString(space.orientation));
+}
+
+// Defining PrintTo helps with Google Tests.
+inline void PrintTo(const android::compositionengine::ProjectionSpace& space, ::std::ostream* os) {
+ *os << to_string(space);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
index f680460..daee83b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
@@ -16,15 +16,16 @@
#pragma once
-#include <cstdint>
-#include <vector>
-
+#include <renderengine/ExternalTexture.h>
#include <ui/Fence.h>
#include <ui/GraphicTypes.h>
#include <ui/Size.h>
#include <utils/Errors.h>
#include <utils/StrongPointer.h>
+#include <cstdint>
+#include <vector>
+
namespace android {
class GraphicBuffer;
@@ -80,7 +81,8 @@
virtual void prepareFrame(bool usesClientComposition, bool usesDeviceComposition) = 0;
// Allocates a buffer as scratch space for GPU composition
- virtual sp<GraphicBuffer> dequeueBuffer(base::unique_fd* bufferFence) = 0;
+ virtual std::shared_ptr<renderengine::ExternalTexture> dequeueBuffer(
+ base::unique_fd* bufferFence) = 0;
// Queues the drawn buffer for consumption by HWC. readyFence is the fence
// which will fire when the buffer is ready for consumption.
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h
index a1230b3..a8d372c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h
@@ -45,6 +45,8 @@
// The DisplaySurface for this surface
sp<DisplaySurface> displaySurface;
+
+ size_t maxTextureCacheSize;
};
/**
@@ -81,6 +83,11 @@
return *this;
}
+ RenderSurfaceCreationArgsBuilder& setMaxTextureCacheSize(size_t maxTextureCacheSize) {
+ mArgs.maxTextureCacheSize = maxTextureCacheSize;
+ return *this;
+ }
+
private:
RenderSurfaceCreationArgs mArgs;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 7a4f738..54e91ae 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -57,7 +57,7 @@
void finishFrame(const CompositionRefreshArgs&) override;
// compositionengine::Display overrides
- const std::optional<DisplayId>& getId() const override;
+ DisplayId getId() const override;
bool isSecure() const override;
bool isVirtual() const override;
void disconnect() override;
@@ -85,12 +85,14 @@
std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const;
// Testing
- void setDisplayIdForTesting(std::optional<DisplayId> displayId);
+ void setDisplayIdForTesting(DisplayId displayId);
private:
bool mIsVirtual = false;
- std::optional<DisplayId> mId;
+ bool mIsDisconnected = false;
+ DisplayId mId;
Hwc2::PowerAdvisor* mPowerAdvisor = nullptr;
+ DisplayIdGenerator<GpuVirtualDisplayId>* mGpuVirtualDisplayIdGenerator;
};
// This template factory function standardizes the implementation details of the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h
index 782c8d7..6b9597b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h
@@ -24,6 +24,7 @@
#include <ui/FloatRect.h>
#include <ui/Rect.h>
#include <ui/Region.h>
+#include <ui/StretchEffect.h>
#include <ui/Transform.h>
namespace android::compositionengine::impl {
@@ -58,5 +59,6 @@
void dumpVal(std::string& out, const char* name, const ui::Size&);
void dumpVal(std::string& out, const char* name, const mat4& tr);
+void dumpVal(std::string& out, const char* name, const StretchEffect&);
} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
index 2864c10..aa049a8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
@@ -22,11 +22,12 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
#include <gui/BufferQueue.h>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
#include <utils/StrongPointer.h>
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 6f25e63..eeb20fc 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -28,26 +28,32 @@
namespace android::compositionengine::impl {
+namespace planner {
+class Planner;
+} // namespace planner
+
// The implementation class contains the common implementation, but does not
// actually contain the final output state.
class Output : public virtual compositionengine::Output {
public:
+ Output();
~Output() override;
// compositionengine::Output overrides
bool isValid() const override;
std::optional<DisplayId> getDisplayId() const override;
void setCompositionEnabled(bool) override;
- void setProjection(const ui::Transform&, uint32_t orientation, const Rect& frame,
- const Rect& viewport, const Rect& sourceClip, const Rect& destinationClip,
- bool needsFiltering) override;
- void setBounds(const ui::Size&) override;
+ void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
+ const Rect& orientedDisplaySpaceRect) override;
+ void setDisplaySize(const ui::Size&) override;
void setLayerStackFilter(uint32_t layerStackId, bool isInternal) override;
+ ui::Transform::RotationFlags getTransformHint() const override;
void setColorTransform(const compositionengine::CompositionRefreshArgs&) override;
void setColorProfile(const ColorProfile&) override;
void dump(std::string&) const override;
+ void dumpPlannerInfo(const Vector<String16>& args, std::string&) const override;
const std::string& getName() const override;
void setName(const std::string&) override;
@@ -77,7 +83,9 @@
void setReleasedLayers(const compositionengine::CompositionRefreshArgs&) override;
void updateLayerStateFromFE(const CompositionRefreshArgs&) const override;
- void updateAndWriteCompositionState(const compositionengine::CompositionRefreshArgs&) override;
+ void updateCompositionState(const compositionengine::CompositionRefreshArgs&) override;
+ void planComposition() override;
+ void writeCompositionState(const compositionengine::CompositionRefreshArgs&) override;
void updateColorProfile(const compositionengine::CompositionRefreshArgs&) override;
void beginFrame() override;
void prepareFrame() override;
@@ -86,12 +94,14 @@
std::optional<base::unique_fd> composeSurfaces(
const Region&, const compositionengine::CompositionRefreshArgs& refreshArgs) override;
void postFramebuffer() override;
+ void renderCachedSets() override;
void cacheClientCompositionRequests(uint32_t) override;
// Testing
const ReleasedLayers& getReleasedLayersForTest() const;
void setDisplayColorProfileForTest(std::unique_ptr<compositionengine::DisplayColorProfile>);
void setRenderSurfaceForTest(std::unique_ptr<compositionengine::RenderSurface>);
+ bool plannerEnabled() const { return mPlanner != nullptr; }
protected:
std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const;
@@ -130,6 +140,7 @@
ReleasedLayers mReleasedLayers;
OutputLayer* mLayerRequestingBackgroundBlur = nullptr;
std::unique_ptr<ClientCompositionRequestCache> mClientCompositionRequestCache;
+ std::unique_ptr<planner::Planner> mPlanner;
};
// This template factory function standardizes the implementation details of the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 66ed2b6..f0ef6d6 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -23,12 +23,14 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
#include <ui/GraphicTypes.h>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
+#include <compositionengine/ProjectionSpace.h>
#include <ui/Rect.h>
#include <ui/Region.h>
#include <ui/Transform.h>
@@ -38,7 +40,7 @@
namespace compositionengine::impl {
struct OutputCompositionState {
- // If false, composition will not per performed for this display
+ // If false, composition will not be performed for this display
bool isEnabled{false};
// If false, this output is not considered secure
@@ -50,8 +52,7 @@
// If true, the current frame on this output uses device composition
bool usesDeviceComposition{false};
- // If true, the client target should be flipped when performing client
- // composition
+ // If true, the client target should be flipped when performing client composition
bool flipClientTarget{false};
// If true, the current frame reused the buffer from a previous client composition
@@ -63,28 +64,26 @@
// The layer stack to display on this display
uint32_t layerStackId{~0u};
- // The physical space screen bounds
- Rect bounds;
+ // The common space for all layers in the layer stack. layerStackSpace.content is the Rect
+ // which gets projected on the display. The orientation of this space is always ROTATION_0.
+ ProjectionSpace layerStackSpace;
- // The logical to physical transformation to use
+ // Oriented physical display space. It will have the same size as displaySpace oriented to
+ // match the orientation of layerStackSpace. The orientation of this space is always ROTATION_0.
+ ProjectionSpace orientedDisplaySpace;
+
+ // The space of the framebuffer. Its bounds match the size of the framebuffer and its
+ // orientation matches the orientation of the display. Typically the framebuffer space will
+ // be identical to the physical display space.
+ ProjectionSpace framebufferSpace;
+
+ // The space of the physical display. It is as big as the currently active display mode. The
+ // content in this space can be rotated.
+ ProjectionSpace displaySpace;
+
+ // Transformation from layerStackSpace to displaySpace
ui::Transform transform;
- // The physical orientation of the display, expressed as ui::Transform
- // orientation flags.
- uint32_t orientation{0};
-
- // The logical space user visible bounds
- Rect frame;
-
- // The logical space user viewport rectangle
- Rect viewport;
-
- // The physical space source clip rectangle
- Rect sourceClip;
-
- // The physical space destination clip rectangle
- Rect destinationClip;
-
// If true, RenderEngine filtering should be enabled
bool needsFiltering{false};
@@ -116,6 +115,9 @@
// Current target dataspace
ui::Dataspace targetDataspace{ui::Dataspace::UNKNOWN};
+ // The earliest time to send the present command to the HAL
+ std::chrono::steady_clock::time_point earliestPresentTime;
+
// Debugging
void dump(std::string& result) const;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index 8cb5ae8..2488c66 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -16,9 +16,11 @@
#pragma once
+#include <cstdint>
#include <memory>
#include <string>
+#include <compositionengine/LayerFE.h>
#include <compositionengine/OutputLayer.h>
#include <ui/FloatRect.h>
#include <ui/Rect.h>
@@ -41,7 +43,8 @@
void updateCompositionState(bool includeGeometry, bool forceClientComposition,
ui::Transform::RotationFlags) override;
- void writeStateToHWC(bool) override;
+ void writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z, bool zIsOverridden,
+ bool isPeekingThrough) override;
void writeCursorPositionToHWC() const override;
HWC2::Layer* getHwcLayer() const override;
@@ -51,9 +54,9 @@
void prepareForDeviceLayerRequests() override;
void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) override;
bool needsFiltering() const override;
+ std::vector<LayerFE::LayerSettings> getOverrideCompositionList() const override;
void dump(std::string&) const override;
-
virtual FloatRect calculateOutputSourceCrop() const;
virtual Rect calculateOutputDisplayFrame() const;
virtual uint32_t calculateOutputRelativeBufferTransform(
@@ -65,14 +68,17 @@
private:
Rect calculateInitialCrop() const;
- void writeOutputDependentGeometryStateToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition);
- void writeOutputIndependentGeometryStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
+ void writeOutputDependentGeometryStateToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition,
+ uint32_t z);
+ void writeOutputIndependentGeometryStateToHWC(HWC2::Layer*, const LayerFECompositionState&,
+ bool skipLayer);
void writeOutputDependentPerFrameStateToHWC(HWC2::Layer*);
void writeOutputIndependentPerFrameStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
void writeSolidColorStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
void writeSidebandStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
void writeBufferStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
- void writeCompositionTypeToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition);
+ void writeCompositionTypeToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition,
+ bool isPeekingThrough);
void detectDisallowedCompositionTypeChange(Hwc2::IComposerClient::Composition from,
Hwc2::IComposerClient::Composition to) const;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index d2b38d1..356965c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -16,25 +16,27 @@
#pragma once
-#include <cstdint>
-#include <optional>
-#include <string>
-
+#include <compositionengine/ProjectionSpace.h>
#include <compositionengine/impl/HwcBufferCache.h>
-#include <renderengine/Mesh.h>
+#include <renderengine/ExternalTexture.h>
#include <ui/FloatRect.h>
#include <ui/GraphicTypes.h>
#include <ui/Rect.h>
#include <ui/Region.h>
+#include <cstdint>
+#include <optional>
+#include <string>
+
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
#include "DisplayHardware/ComposerHal.h"
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
namespace android {
@@ -44,8 +46,14 @@
class HWComposer;
+namespace compositionengine {
+class OutputLayer;
+} // namespace compositionengine
+
namespace compositionengine::impl {
+// Note that fields that affect HW composer state may need to be mirrored into
+// android::compositionengine::impl::planner::LayerState
struct OutputLayerCompositionState {
// The portion of the layer that is not obscured by opaque layers on top
Region visibleRegion;
@@ -80,8 +88,22 @@
// The dataspace for this layer
ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
- // The Z order index of this layer on this output
- uint32_t z{0};
+ // Overrides the buffer, acquire fence, and display frame stored in LayerFECompositionState
+ struct {
+ std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr;
+ sp<Fence> acquireFence = nullptr;
+ Rect displayFrame = {};
+ ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
+ ProjectionSpace displaySpace;
+ Region damageRegion = Region::INVALID_REGION;
+ Region visibleRegion;
+
+ // The OutputLayer pointed to by this field will be rearranged to draw
+ // behind the OutputLayer represented by this CompositionState and will
+ // be visible through it. Unowned - the OutputLayer's lifetime will
+ // outlast this.)
+ OutputLayer* peekThroughLayer = nullptr;
+ } overrideInfo;
/*
* HWC state
@@ -100,6 +122,9 @@
// The buffer cache for this layer. This is used to lower the
// cost of sending reused buffers to the HWC.
HwcBufferCache hwcBufferCache;
+
+ // Set to true when overridden info has been sent to HW composer
+ bool stateOverridden = false;
};
// The HWC state is optional, and is only set up if there is any potential
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
index 5127a6f..a8a5380 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
@@ -16,12 +16,16 @@
#pragma once
-#include <memory>
-
#include <android-base/unique_fd.h>
#include <compositionengine/RenderSurface.h>
#include <utils/StrongPointer.h>
+#include <memory>
+#include <vector>
+
+#include "renderengine/ExternalTexture.h"
+#include "renderengine/RenderEngine.h"
+
struct ANativeWindow;
namespace android {
@@ -54,7 +58,8 @@
void setProtected(bool useProtected) override;
status_t beginFrame(bool mustRecompose) override;
void prepareFrame(bool usesClientComposition, bool usesDeviceComposition) override;
- sp<GraphicBuffer> dequeueBuffer(base::unique_fd* bufferFence) override;
+ std::shared_ptr<renderengine::ExternalTexture> dequeueBuffer(
+ base::unique_fd* bufferFence) override;
void queueBuffer(base::unique_fd readyFence) override;
void onPresentDisplayCompleted() override;
void flip() override;
@@ -66,7 +71,7 @@
// Testing
void setPageFlipCountForTest(std::uint32_t);
void setSizeForTest(const ui::Size&);
- sp<GraphicBuffer>& mutableGraphicBufferForTest();
+ std::shared_ptr<renderengine::ExternalTexture>& mutableTextureForTest();
base::unique_fd& mutableBufferReadyForTest();
private:
@@ -75,10 +80,13 @@
// ANativeWindow being rendered into
const sp<ANativeWindow> mNativeWindow;
- // Current buffer being rendered into
- sp<GraphicBuffer> mGraphicBuffer;
+
+ std::vector<std::shared_ptr<renderengine::ExternalTexture>> mTextureCache;
+ // Current texture being rendered into
+ std::shared_ptr<renderengine::ExternalTexture> mTexture;
const sp<DisplaySurface> mDisplaySurface;
ui::Size mSize;
+ const size_t mMaxTextureCacheSize;
bool mProtected{false};
std::uint32_t mPageFlipCount{0};
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
new file mode 100644
index 0000000..a6c4eaf
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#pragma once
+
+#include <compositionengine/Output.h>
+#include <compositionengine/ProjectionSpace.h>
+#include <compositionengine/impl/planner/LayerState.h>
+#include <renderengine/RenderEngine.h>
+
+#include <chrono>
+
+namespace android {
+
+namespace compositionengine::impl::planner {
+
+std::string durationString(std::chrono::milliseconds duration);
+
+class LayerState;
+
+class CachedSet {
+public:
+ class Layer {
+ public:
+ Layer(const LayerState*, std::chrono::steady_clock::time_point lastUpdate);
+
+ const LayerState* getState() const { return mState; }
+ const std::string& getName() const { return mState->getName(); }
+ Rect getDisplayFrame() const { return mState->getDisplayFrame(); }
+ const Region& getVisibleRegion() const { return mState->getVisibleRegion(); }
+ const sp<GraphicBuffer>& getBuffer() const {
+ return mState->getOutputLayer()->getLayerFE().getCompositionState()->buffer;
+ }
+ int64_t getFramesSinceBufferUpdate() const { return mState->getFramesSinceBufferUpdate(); }
+ NonBufferHash getHash() const { return mHash; }
+ std::chrono::steady_clock::time_point getLastUpdate() const { return mLastUpdate; }
+
+ private:
+ const LayerState* mState;
+ NonBufferHash mHash;
+ std::chrono::steady_clock::time_point mLastUpdate;
+ };
+
+ CachedSet(const LayerState*, std::chrono::steady_clock::time_point lastUpdate);
+ CachedSet(Layer layer);
+
+ void addLayer(const LayerState*, std::chrono::steady_clock::time_point lastUpdate);
+
+ std::chrono::steady_clock::time_point getLastUpdate() const { return mLastUpdate; }
+ NonBufferHash getFingerprint() const { return mFingerprint; }
+ size_t getLayerCount() const { return mLayers.size(); }
+ const Layer& getFirstLayer() const { return mLayers[0]; }
+ const Rect& getBounds() const { return mBounds; }
+ const Region& getVisibleRegion() const { return mVisibleRegion; }
+ size_t getAge() const { return mAge; }
+ const std::shared_ptr<renderengine::ExternalTexture>& getBuffer() const { return mTexture; }
+ const sp<Fence>& getDrawFence() const { return mDrawFence; }
+ const ProjectionSpace& getOutputSpace() const { return mOutputSpace; }
+ ui::Dataspace getOutputDataspace() const { return mOutputDataspace; }
+ const std::vector<Layer>& getConstituentLayers() const { return mLayers; }
+
+ NonBufferHash getNonBufferHash() const;
+
+ size_t getComponentDisplayCost() const;
+ size_t getCreationCost() const;
+ size_t getDisplayCost() const;
+
+ bool hasBufferUpdate() const;
+ bool hasReadyBuffer() const;
+
+ // Decomposes this CachedSet into a vector of its layers as individual CachedSets
+ std::vector<CachedSet> decompose() const;
+
+ void updateAge(std::chrono::steady_clock::time_point now);
+
+ void setLastUpdate(std::chrono::steady_clock::time_point now) { mLastUpdate = now; }
+ void append(const CachedSet& other) {
+ mTexture = nullptr;
+ mOutputDataspace = ui::Dataspace::UNKNOWN;
+ mDrawFence = nullptr;
+
+ mLayers.insert(mLayers.end(), other.mLayers.cbegin(), other.mLayers.cend());
+ Region boundingRegion;
+ boundingRegion.orSelf(mBounds);
+ boundingRegion.orSelf(other.mBounds);
+ mBounds = boundingRegion.getBounds();
+ mVisibleRegion.orSelf(other.mVisibleRegion);
+ }
+ void incrementAge() { ++mAge; }
+
+ // Renders the cached set with the supplied output composition state.
+ void render(renderengine::RenderEngine& re, const OutputCompositionState& outputState);
+
+ void dump(std::string& result) const;
+
+ // Whether this represents a single layer with a buffer and rounded corners.
+ // If it is, we may be able to draw it by placing it behind another
+ // CachedSet and punching a hole.
+ bool requiresHolePunch() const;
+
+ // Add a layer that will be drawn behind this one. ::render() will render a
+ // hole in this CachedSet's buffer, allowing the supplied layer to peek
+ // through. Must be called before ::render().
+ // Will do nothing if this CachedSet is not opaque where the hole punch
+ // layer is displayed.
+ // If isFirstLayer is true, this CachedSet can be considered opaque because
+ // nothing (besides the hole punch layer) will be drawn behind it.
+ void addHolePunchLayerIfFeasible(const CachedSet&, bool isFirstLayer);
+
+ // Retrieve the layer that will be drawn behind this one.
+ OutputLayer* getHolePunchLayer() const;
+
+private:
+ CachedSet() = default;
+
+ const NonBufferHash mFingerprint;
+ std::chrono::steady_clock::time_point mLastUpdate = std::chrono::steady_clock::now();
+ std::vector<Layer> mLayers;
+
+ // Unowned.
+ const LayerState* mHolePunchLayer = nullptr;
+ Rect mBounds = Rect::EMPTY_RECT;
+ Region mVisibleRegion;
+ size_t mAge = 0;
+
+ std::shared_ptr<renderengine::ExternalTexture> mTexture;
+ sp<Fence> mDrawFence;
+ ProjectionSpace mOutputSpace;
+ ui::Dataspace mOutputDataspace;
+ ui::Transform::RotationFlags mOrientation = ui::Transform::ROT_0;
+
+ static const bool sDebugHighlighLayers;
+};
+
+} // namespace compositionengine::impl::planner
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
new file mode 100644
index 0000000..942592a
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#pragma once
+
+#include <compositionengine/Output.h>
+#include <compositionengine/impl/planner/CachedSet.h>
+#include <compositionengine/impl/planner/LayerState.h>
+
+#include <vector>
+
+namespace android {
+
+namespace renderengine {
+class RenderEngine;
+} // namespace renderengine
+
+namespace compositionengine::impl::planner {
+using namespace std::chrono_literals;
+
+class LayerState;
+class Predictor;
+
+class Flattener {
+public:
+ Flattener(Predictor& predictor, bool enableHolePunch = false)
+ : mEnableHolePunch(enableHolePunch), mPredictor(predictor) {}
+
+ void setDisplaySize(ui::Size size) { mDisplaySize = size; }
+
+ NonBufferHash flattenLayers(const std::vector<const LayerState*>& layers, NonBufferHash,
+ std::chrono::steady_clock::time_point now);
+
+ // Renders the newest cached sets with the supplied output composition state
+ void renderCachedSets(renderengine::RenderEngine& re,
+ const OutputCompositionState& outputState);
+
+ void dump(std::string& result) const;
+
+private:
+ size_t calculateDisplayCost(const std::vector<const LayerState*>& layers) const;
+
+ void resetActivities(NonBufferHash, std::chrono::steady_clock::time_point now);
+
+ NonBufferHash computeLayersHash() const;
+
+ bool mergeWithCachedSets(const std::vector<const LayerState*>& layers,
+ std::chrono::steady_clock::time_point now);
+
+ void buildCachedSets(std::chrono::steady_clock::time_point now);
+
+ const bool mEnableHolePunch;
+ Predictor& mPredictor;
+
+ ui::Size mDisplaySize;
+
+ NonBufferHash mCurrentGeometry;
+ std::chrono::steady_clock::time_point mLastGeometryUpdate;
+
+ std::vector<CachedSet> mLayers;
+ std::optional<CachedSet> mNewCachedSet;
+
+ // Statistics
+ size_t mUnflattenedDisplayCost = 0;
+ size_t mFlattenedDisplayCost = 0;
+ std::unordered_map<size_t, size_t> mInitialLayerCounts;
+ std::unordered_map<size_t, size_t> mFinalLayerCounts;
+ size_t mCachedSetCreationCount = 0;
+ size_t mCachedSetCreationCost = 0;
+ std::unordered_map<size_t, size_t> mInvalidatedCachedSetAges;
+
+ static constexpr auto kActiveLayerTimeout = std::chrono::nanoseconds(150ms);
+};
+
+} // namespace compositionengine::impl::planner
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
new file mode 100644
index 0000000..3391273
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -0,0 +1,427 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#pragma once
+
+#include <android-base/strings.h>
+#include <compositionengine/LayerFE.h>
+#include <compositionengine/LayerFECompositionState.h>
+#include <compositionengine/OutputLayer.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <input/Flags.h>
+
+#include <string>
+
+#include "DisplayHardware/Hal.h"
+
+namespace std {
+template <typename T>
+struct hash<android::sp<T>> {
+ size_t operator()(const android::sp<T>& p) { return std::hash<void*>()(p.get()); }
+};
+
+template <typename T>
+struct hash<android::wp<T>> {
+ size_t operator()(const android::wp<T>& p) {
+ android::sp<T> promoted = p.promote();
+ return std::hash<void*>()(promoted ? promoted.get() : nullptr);
+ }
+};
+} // namespace std
+
+namespace android::compositionengine::impl::planner {
+
+using LayerId = int32_t;
+
+// clang-format off
+enum class LayerStateField : uint32_t {
+ None = 0u,
+ Id = 1u << 0,
+ Name = 1u << 1,
+ DisplayFrame = 1u << 2,
+ SourceCrop = 1u << 3,
+ BufferTransform = 1u << 4,
+ BlendMode = 1u << 5,
+ Alpha = 1u << 6,
+ LayerMetadata = 1u << 7,
+ VisibleRegion = 1u << 8,
+ Dataspace = 1u << 9,
+ PixelFormat = 1u << 10,
+ ColorTransform = 1u << 11,
+ SurfaceDamage = 1u << 12,
+ CompositionType = 1u << 13,
+ SidebandStream = 1u << 14,
+ Buffer = 1u << 15,
+ SolidColor = 1u << 16,
+};
+// clang-format on
+
+std::string to_string(LayerStateField field);
+
+// An abstract interface allows us to iterate over all of the OutputLayerState fields
+// without having to worry about their templated types.
+// See `LayerState::getNonUniqueFields` below.
+class StateInterface {
+public:
+ virtual ~StateInterface() = default;
+
+ virtual Flags<LayerStateField> update(const compositionengine::OutputLayer* layer) = 0;
+
+ virtual size_t getHash() const = 0;
+
+ virtual LayerStateField getField() const = 0;
+
+ virtual Flags<LayerStateField> getFieldIfDifferent(const StateInterface* other) const = 0;
+
+ virtual bool equals(const StateInterface* other) const = 0;
+
+ virtual std::vector<std::string> toStrings() const = 0;
+};
+
+template <typename T, LayerStateField FIELD>
+class OutputLayerState : public StateInterface {
+public:
+ using ReadFromLayerState = std::function<T(const compositionengine::OutputLayer* layer)>;
+ using ToStrings = std::function<std::vector<std::string>(const T&)>;
+ using Equals = std::function<bool(const T&, const T&)>;
+ using Hashes = std::function<size_t(const T&)>;
+
+ static ToStrings getDefaultToStrings() {
+ return [](const T& value) {
+ using std::to_string;
+ return std::vector<std::string>{to_string(value)};
+ };
+ }
+
+ static ToStrings getHalToStrings() {
+ return [](const T& value) { return std::vector<std::string>{toString(value)}; };
+ }
+
+ static ToStrings getRegionToStrings() {
+ return [](const Region& region) {
+ using namespace std::string_literals;
+ std::string dump;
+ region.dump(dump, "");
+ std::vector<std::string> split = base::Split(dump, "\n"s);
+ split.erase(split.begin()); // Strip the header
+ split.pop_back(); // Strip the last (empty) line
+ for (std::string& line : split) {
+ line.erase(0, 4); // Strip leading padding before each rect
+ }
+ return split;
+ };
+ }
+
+ static Equals getDefaultEquals() {
+ return [](const T& lhs, const T& rhs) { return lhs == rhs; };
+ }
+
+ static Equals getRegionEquals() {
+ return [](const Region& lhs, const Region& rhs) { return lhs.hasSameRects(rhs); };
+ }
+
+ static Hashes getDefaultHashes() {
+ return [](const T& value) { return std::hash<T>{}(value); };
+ }
+
+ OutputLayerState(ReadFromLayerState reader,
+ ToStrings toStrings = OutputLayerState::getDefaultToStrings(),
+ Equals equals = OutputLayerState::getDefaultEquals(),
+ Hashes hashes = OutputLayerState::getDefaultHashes())
+ : mReader(reader), mToStrings(toStrings), mEquals(equals), mHashes(hashes) {}
+
+ ~OutputLayerState() override = default;
+
+ // Returns this member's field flag if it was changed
+ Flags<LayerStateField> update(const compositionengine::OutputLayer* layer) override {
+ T newValue = mReader(layer);
+ return update(newValue);
+ }
+
+ Flags<LayerStateField> update(const T& newValue) {
+ if (!mEquals(mValue, newValue)) {
+ mValue = newValue;
+ mHash = {};
+ return FIELD;
+ }
+ return {};
+ }
+
+ LayerStateField getField() const override { return FIELD; }
+ const T& get() const { return mValue; }
+
+ size_t getHash() const override {
+ if (!mHash) {
+ mHash = mHashes(mValue);
+ }
+ return *mHash;
+ }
+
+ Flags<LayerStateField> getFieldIfDifferent(const StateInterface* other) const override {
+ if (other->getField() != FIELD) {
+ return {};
+ }
+
+ // The early return ensures that this downcast is sound
+ const OutputLayerState* otherState = static_cast<const OutputLayerState*>(other);
+ return *this != *otherState ? FIELD : Flags<LayerStateField>{};
+ }
+
+ bool equals(const StateInterface* other) const override {
+ if (other->getField() != FIELD) {
+ return false;
+ }
+
+ // The early return ensures that this downcast is sound
+ const OutputLayerState* otherState = static_cast<const OutputLayerState*>(other);
+ return *this == *otherState;
+ }
+
+ std::vector<std::string> toStrings() const override { return mToStrings(mValue); }
+
+ bool operator==(const OutputLayerState& other) const { return mEquals(mValue, other.mValue); }
+ bool operator!=(const OutputLayerState& other) const { return !(*this == other); }
+
+private:
+ const ReadFromLayerState mReader;
+ const ToStrings mToStrings;
+ const Equals mEquals;
+ const Hashes mHashes;
+ T mValue = {};
+ mutable std::optional<size_t> mHash = {};
+};
+
+class LayerState {
+public:
+ LayerState(compositionengine::OutputLayer* layer);
+
+ // Returns which fields were updated
+ Flags<LayerStateField> update(compositionengine::OutputLayer*);
+
+ // Computes a hash for this LayerState.
+ // The hash is only computed from NonUniqueFields, and excludes GraphicBuffers since they are
+ // not guaranteed to live longer than the LayerState object.
+ size_t getHash() const;
+
+ // Returns the bit-set of differing fields between this LayerState and another LayerState.
+ // This bit-set is based on NonUniqueFields only, and excludes GraphicBuffers.
+ Flags<LayerStateField> getDifferingFields(const LayerState& other) const;
+
+ compositionengine::OutputLayer* getOutputLayer() const { return mOutputLayer; }
+ int32_t getId() const { return mId.get(); }
+ const std::string& getName() const { return mName.get(); }
+ Rect getDisplayFrame() const { return mDisplayFrame.get(); }
+ const Region& getVisibleRegion() const { return mVisibleRegion.get(); }
+ hardware::graphics::composer::hal::Composition getCompositionType() const {
+ return mCompositionType.get();
+ }
+
+ void incrementFramesSinceBufferUpdate() { ++mFramesSinceBufferUpdate; }
+ void resetFramesSinceBufferUpdate() { mFramesSinceBufferUpdate = 0; }
+ int64_t getFramesSinceBufferUpdate() const { return mFramesSinceBufferUpdate; }
+
+ void dump(std::string& result) const;
+ std::optional<std::string> compare(const LayerState& other) const;
+
+ // This makes LayerState's private members accessible to the operator
+ friend bool operator==(const LayerState& lhs, const LayerState& rhs);
+ friend bool operator!=(const LayerState& lhs, const LayerState& rhs) { return !(lhs == rhs); }
+
+private:
+ compositionengine::OutputLayer* mOutputLayer = nullptr;
+
+ OutputLayerState<LayerId, LayerStateField::Id> mId{
+ [](const compositionengine::OutputLayer* layer) {
+ return layer->getLayerFE().getSequence();
+ }};
+
+ OutputLayerState<std::string, LayerStateField::Name>
+ mName{[](auto layer) { return layer->getLayerFE().getDebugName(); },
+ [](const std::string& name) { return std::vector<std::string>{name}; }};
+
+ // Output-dependent geometry state
+
+ OutputLayerState<Rect, LayerStateField::DisplayFrame>
+ mDisplayFrame{[](auto layer) { return layer->getState().displayFrame; },
+ [](const Rect& rect) {
+ return std::vector<std::string>{
+ base::StringPrintf("[%d, %d, %d, %d]", rect.left, rect.top,
+ rect.right, rect.bottom)};
+ }};
+
+ OutputLayerState<FloatRect, LayerStateField::SourceCrop>
+ mSourceCrop{[](auto layer) { return layer->getState().sourceCrop; },
+ [](const FloatRect& rect) {
+ return std::vector<std::string>{
+ base::StringPrintf("[%.2f, %.2f, %.2f, %.2f]", rect.left,
+ rect.top, rect.right, rect.bottom)};
+ }};
+
+ using BufferTransformState = OutputLayerState<hardware::graphics::composer::hal::Transform,
+ LayerStateField::BufferTransform>;
+ BufferTransformState mBufferTransform{[](auto layer) {
+ return layer->getState().bufferTransform;
+ },
+ BufferTransformState::getHalToStrings()};
+
+ // Output-independent geometry state
+
+ using BlendModeState = OutputLayerState<hardware::graphics::composer::hal::BlendMode,
+ LayerStateField::BlendMode>;
+ BlendModeState mBlendMode{[](auto layer) {
+ return layer->getLayerFE().getCompositionState()->blendMode;
+ },
+ BlendModeState::getHalToStrings()};
+
+ OutputLayerState<float, LayerStateField::Alpha> mAlpha{
+ [](auto layer) { return layer->getLayerFE().getCompositionState()->alpha; }};
+
+ using LayerMetadataState =
+ OutputLayerState<GenericLayerMetadataMap, LayerStateField::LayerMetadata>;
+ LayerMetadataState
+ mLayerMetadata{[](auto layer) {
+ return layer->getLayerFE().getCompositionState()->metadata;
+ },
+ [](const GenericLayerMetadataMap& metadata) {
+ std::vector<std::string> result;
+ if (metadata.empty()) {
+ result.push_back("{}");
+ return result;
+ }
+ result.push_back("{");
+ for (const auto& [key, value] : metadata) {
+ std::string keyValueDump;
+ keyValueDump.append(" ");
+ keyValueDump.append(key);
+ keyValueDump.append("=");
+ keyValueDump.append(value.dumpAsString());
+ result.push_back(keyValueDump);
+ }
+ result.push_back("}");
+ return result;
+ },
+ LayerMetadataState::getDefaultEquals(),
+ [](const GenericLayerMetadataMap& metadata) {
+ size_t hash = 0;
+ for (const auto& [key, value] : metadata) {
+ size_t entryHash = 0;
+ hashCombineSingleHashed(entryHash,
+ std::hash<std::string>{}(key));
+ hashCombineSingleHashed(entryHash,
+ GenericLayerMetadataEntry::Hasher{}(
+ value));
+ hash ^= entryHash;
+ }
+ return hash;
+ }};
+
+ // Output-dependent per-frame state
+
+ using VisibleRegionState = OutputLayerState<Region, LayerStateField::VisibleRegion>;
+ VisibleRegionState mVisibleRegion{[](auto layer) { return layer->getState().visibleRegion; },
+ VisibleRegionState::getRegionToStrings(),
+ VisibleRegionState::getRegionEquals()};
+
+ using DataspaceState = OutputLayerState<ui::Dataspace, LayerStateField::Dataspace>;
+ DataspaceState mOutputDataspace{[](auto layer) { return layer->getState().dataspace; },
+ DataspaceState::getHalToStrings()};
+
+ // Output-independent per-frame state
+
+ using PixelFormatState = OutputLayerState<hardware::graphics::composer::hal::PixelFormat,
+ LayerStateField::PixelFormat>;
+ PixelFormatState
+ mPixelFormat{[](auto layer) {
+ return layer->getLayerFE().getCompositionState()->buffer
+ ? static_cast<hardware::graphics::composer::hal::PixelFormat>(
+ layer->getLayerFE()
+ .getCompositionState()
+ ->buffer->getPixelFormat())
+ : hardware::graphics::composer::hal::PixelFormat::RGBA_8888;
+ },
+ PixelFormatState::getHalToStrings()};
+
+ OutputLayerState<mat4, LayerStateField::ColorTransform> mColorTransform;
+
+ using CompositionTypeState = OutputLayerState<hardware::graphics::composer::hal::Composition,
+ LayerStateField::CompositionType>;
+ CompositionTypeState
+ mCompositionType{[](auto layer) {
+ return layer->getState().forceClientComposition
+ ? hardware::graphics::composer::hal::Composition::CLIENT
+ : layer->getLayerFE()
+ .getCompositionState()
+ ->compositionType;
+ },
+ CompositionTypeState::getHalToStrings()};
+
+ OutputLayerState<void*, LayerStateField::SidebandStream>
+ mSidebandStream{[](auto layer) {
+ return layer->getLayerFE()
+ .getCompositionState()
+ ->sidebandStream.get();
+ },
+ [](void* p) {
+ return std::vector<std::string>{base::StringPrintf("%p", p)};
+ }};
+
+ OutputLayerState<wp<GraphicBuffer>, LayerStateField::Buffer>
+ mBuffer{[](auto layer) { return layer->getLayerFE().getCompositionState()->buffer; },
+ [](const wp<GraphicBuffer>& buffer) {
+ sp<GraphicBuffer> promotedBuffer = buffer.promote();
+ return std::vector<std::string>{
+ base::StringPrintf("%p",
+ promotedBuffer ? promotedBuffer.get()
+ : nullptr)};
+ }};
+
+ int64_t mFramesSinceBufferUpdate = 0;
+
+ OutputLayerState<half4, LayerStateField::SolidColor>
+ mSolidColor{[](auto layer) { return layer->getLayerFE().getCompositionState()->color; },
+ [](const half4& vec) {
+ std::stringstream stream;
+ stream << vec;
+ return std::vector<std::string>{stream.str()};
+ }};
+
+ static const constexpr size_t kNumNonUniqueFields = 14;
+
+ std::array<StateInterface*, kNumNonUniqueFields> getNonUniqueFields() {
+ std::array<const StateInterface*, kNumNonUniqueFields> constFields =
+ const_cast<const LayerState*>(this)->getNonUniqueFields();
+ std::array<StateInterface*, kNumNonUniqueFields> fields;
+ std::transform(constFields.cbegin(), constFields.cend(), fields.begin(),
+ [](const StateInterface* constField) {
+ return const_cast<StateInterface*>(constField);
+ });
+ return fields;
+ }
+
+ std::array<const StateInterface*, kNumNonUniqueFields> getNonUniqueFields() const {
+ return {
+ &mDisplayFrame, &mSourceCrop, &mBufferTransform, &mBlendMode,
+ &mAlpha, &mLayerMetadata, &mVisibleRegion, &mOutputDataspace,
+ &mPixelFormat, &mColorTransform, &mCompositionType, &mSidebandStream,
+ &mBuffer, &mSolidColor,
+ };
+ }
+};
+
+using NonBufferHash = size_t;
+NonBufferHash getNonBufferHash(const std::vector<const LayerState*>&);
+
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
new file mode 100644
index 0000000..c2037a8
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#pragma once
+
+#include <compositionengine/Output.h>
+#include <compositionengine/impl/planner/Flattener.h>
+#include <compositionengine/impl/planner/LayerState.h>
+#include <compositionengine/impl/planner/Predictor.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+#include <optional>
+#include <string>
+#include <unordered_map>
+
+namespace android {
+
+namespace renderengine {
+class RenderEngine;
+} // namespace renderengine
+
+namespace compositionengine::impl::planner {
+
+// This is the top level class for layer caching. It is responsible for
+// heuristically determining the composition strategy of the current layer stack,
+// and flattens inactive layers into an override buffer so it can be used
+// as a more efficient representation of parts of the layer stack.
+class Planner {
+public:
+ Planner();
+
+ void setDisplaySize(ui::Size);
+
+ // Updates the Planner with the current set of layers before a composition strategy is
+ // determined.
+ // The Planner will call to the Flattener to determine to:
+ // 1. Replace any cached sets with a newly available flattened cached set
+ // 2. Create a new cached set if possible
+ void plan(
+ compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers);
+
+ // Updates the Planner with the current set of layers after a composition strategy is
+ // determined.
+ void reportFinalPlan(
+ compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers);
+
+ // The planner will call to the Flattener to render any pending cached set
+ void renderCachedSets(renderengine::RenderEngine& re,
+ const OutputCompositionState& outputState);
+
+ void dump(const Vector<String16>& args, std::string&);
+
+private:
+ void dumpUsage(std::string&) const;
+
+ std::unordered_map<LayerId, LayerState> mPreviousLayers;
+
+ std::vector<const LayerState*> mCurrentLayers;
+
+ Predictor mPredictor;
+ Flattener mFlattener;
+
+ std::optional<Predictor::PredictedPlan> mPredictedPlan;
+ NonBufferHash mFlattenedHash = 0;
+};
+
+} // namespace compositionengine::impl::planner
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h
new file mode 100644
index 0000000..fe486d3
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h
@@ -0,0 +1,321 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#pragma once
+
+#include <compositionengine/impl/planner/LayerState.h>
+
+namespace android::compositionengine::impl::planner {
+
+class LayerStack {
+public:
+ LayerStack(const std::vector<const LayerState*>& layers) : mLayers(copyLayers(layers)) {}
+
+ // Describes an approximate match between two layer stacks
+ struct ApproximateMatch {
+ bool operator==(const ApproximateMatch& other) const {
+ return differingIndex == other.differingIndex &&
+ differingFields == other.differingFields;
+ }
+
+ // The index of the single differing layer between the two stacks.
+ // This implies that only one layer is allowed to differ in an approximate match.
+ size_t differingIndex;
+ // Set of fields that differ for the differing layer in the approximate match.
+ Flags<LayerStateField> differingFields;
+ };
+
+ // Returns an approximate match when comparing this layer stack with the provided list of
+ // layers, for the purposes of scoring how closely the two layer stacks will match composition
+ // strategies.
+ //
+ // If the two layer stacks are identical, then an approximate match is still returned, but the
+ // differing fields will be empty to represent an exact match.
+ //
+ // If the two layer stacks differ by too much, then an empty optional is returned.
+ std::optional<ApproximateMatch> getApproximateMatch(
+ const std::vector<const LayerState*>& other) const;
+
+ void compare(const LayerStack& other, std::string& result) const {
+ if (mLayers.size() != other.mLayers.size()) {
+ base::StringAppendF(&result, "Cannot compare stacks of different sizes (%zd vs. %zd)\n",
+ mLayers.size(), other.mLayers.size());
+ return;
+ }
+
+ for (size_t l = 0; l < mLayers.size(); ++l) {
+ const auto& thisLayer = mLayers[l];
+ const auto& otherLayer = other.mLayers[l];
+ base::StringAppendF(&result, "\n+ - - - - - - - - - Layer %d [%s]\n", thisLayer.getId(),
+ thisLayer.getName().c_str());
+ auto comparisonOpt = thisLayer.compare(otherLayer);
+ base::StringAppendF(&result,
+ " %s + - - - - - - - - - - - - - - - - - - - - - - - "
+ "- Layer %d [%s]\n",
+ comparisonOpt ? " " : "Identical", otherLayer.getId(),
+ otherLayer.getName().c_str());
+ if (comparisonOpt) {
+ result.append(*comparisonOpt);
+ }
+ }
+ }
+
+ void dump(std::string& result) const {
+ for (const LayerState& layer : mLayers) {
+ base::StringAppendF(&result, "+ - - - - - - - - - Layer %d [%s]\n", layer.getId(),
+ layer.getName().c_str());
+ layer.dump(result);
+ }
+ }
+
+ void dumpLayerNames(std::string& result, const std::string& prefix = " ") const {
+ for (const LayerState& layer : mLayers) {
+ result.append(prefix);
+ result.append(layer.getName());
+ result.append("\n");
+ }
+ }
+
+private:
+ std::vector<const LayerState> copyLayers(const std::vector<const LayerState*>& layers) {
+ std::vector<const LayerState> copiedLayers;
+ copiedLayers.reserve(layers.size());
+ std::transform(layers.cbegin(), layers.cend(), std::back_inserter(copiedLayers),
+ [](const LayerState* layerState) { return *layerState; });
+ return copiedLayers;
+ }
+
+ std::vector<const LayerState> mLayers;
+
+ // TODO(b/180976743): Tune kMaxDifferingFields
+ constexpr static int kMaxDifferingFields = 6;
+};
+
+class Plan {
+public:
+ static std::optional<Plan> fromString(const std::string&);
+
+ void reset() { mLayerTypes.clear(); }
+ void addLayerType(hardware::graphics::composer::hal::Composition type) {
+ mLayerTypes.emplace_back(type);
+ }
+
+ friend std::string to_string(const Plan& plan);
+
+ friend bool operator==(const Plan& lhs, const Plan& rhs) {
+ return lhs.mLayerTypes == rhs.mLayerTypes;
+ }
+ friend bool operator!=(const Plan& lhs, const Plan& rhs) { return !(lhs == rhs); }
+
+ friend std::ostream& operator<<(std::ostream& os, const Plan& plan) {
+ return os << to_string(plan);
+ }
+
+private:
+ std::vector<hardware::graphics::composer::hal::Composition> mLayerTypes;
+};
+
+} // namespace android::compositionengine::impl::planner
+
+namespace std {
+template <>
+struct hash<android::compositionengine::impl::planner::Plan> {
+ size_t operator()(const android::compositionengine::impl::planner::Plan& plan) const {
+ return std::hash<std::string>{}(to_string(plan));
+ }
+};
+} // namespace std
+
+namespace android::compositionengine::impl::planner {
+
+class Prediction {
+public:
+ enum class Type {
+ Exact,
+ Approximate,
+ Total,
+ };
+
+ friend std::string to_string(Type type) {
+ using namespace std::string_literals;
+
+ switch (type) {
+ case Type::Exact:
+ return "Exact";
+ case Type::Approximate:
+ return "Approximate";
+ case Type::Total:
+ return "Total";
+ }
+ }
+
+ friend std::ostream& operator<<(std::ostream& os, const Type& type) {
+ return os << to_string(type);
+ }
+
+ Prediction(const std::vector<const LayerState*>& layers, Plan plan)
+ : mExampleLayerStack(layers), mPlan(std::move(plan)) {}
+
+ const LayerStack& getExampleLayerStack() const { return mExampleLayerStack; }
+ const Plan& getPlan() const { return mPlan; }
+
+ size_t getHitCount(Type type) const {
+ if (type == Type::Total) {
+ return getHitCount(Type::Exact) + getHitCount(Type::Approximate);
+ }
+ return getStatsForType(type).hitCount;
+ }
+
+ size_t getMissCount(Type type) const {
+ if (type == Type::Total) {
+ return getMissCount(Type::Exact) + getMissCount(Type::Approximate);
+ }
+ return getStatsForType(type).missCount;
+ }
+
+ void recordHit(Type type) { ++getStatsForType(type).hitCount; }
+
+ void recordMiss(Type type) { ++getStatsForType(type).missCount; }
+
+ void dump(std::string&) const;
+
+private:
+ struct Stats {
+ void dump(std::string& result) const {
+ const size_t totalAttempts = hitCount + missCount;
+ base::StringAppendF(&result, "%.2f%% (%zd/%zd)", 100.0f * hitCount / totalAttempts,
+ hitCount, totalAttempts);
+ }
+
+ size_t hitCount = 0;
+ size_t missCount = 0;
+ };
+
+ const Stats& getStatsForType(Type type) const {
+ return (type == Type::Exact) ? mExactStats : mApproximateStats;
+ }
+
+ Stats& getStatsForType(Type type) {
+ return const_cast<Stats&>(const_cast<const Prediction*>(this)->getStatsForType(type));
+ }
+
+ LayerStack mExampleLayerStack;
+ Plan mPlan;
+
+ Stats mExactStats;
+ Stats mApproximateStats;
+};
+
+class Predictor {
+public:
+ struct PredictedPlan {
+ NonBufferHash hash;
+ Plan plan;
+ Prediction::Type type;
+
+ friend bool operator==(const PredictedPlan& lhs, const PredictedPlan& rhs) {
+ return lhs.hash == rhs.hash && lhs.plan == rhs.plan && lhs.type == rhs.type;
+ }
+ };
+
+ // Retrieves the predicted plan based on a layer stack alongside its hash.
+ //
+ // If the exact layer stack has previously been seen by the predictor, then report the plan used
+ // for that layer stack.
+ //
+ // Otherwise, try to match to the best approximate stack to retireve the most likely plan.
+ std::optional<PredictedPlan> getPredictedPlan(const std::vector<const LayerState*>& layers,
+ NonBufferHash hash) const;
+
+ // Records a comparison between the predicted plan and the resulting plan, alongside the layer
+ // stack we used.
+ //
+ // This method is intended to help with scoring how effective the prediction engine is.
+ void recordResult(std::optional<PredictedPlan> predictedPlan, NonBufferHash flattenedHash,
+ const std::vector<const LayerState*>&, bool hasSkippedLayers, Plan result);
+
+ void dump(std::string&) const;
+
+ void compareLayerStacks(NonBufferHash leftHash, NonBufferHash rightHash, std::string&) const;
+ void describeLayerStack(NonBufferHash, std::string&) const;
+ void listSimilarStacks(Plan, std::string&) const;
+
+private:
+ // Retrieves a prediction from either the main prediction list or from the candidate list
+ const Prediction& getPrediction(NonBufferHash) const;
+ Prediction& getPrediction(NonBufferHash);
+
+ std::optional<Plan> getExactMatch(NonBufferHash) const;
+ std::optional<NonBufferHash> getApproximateMatch(
+ const std::vector<const LayerState*>& layers) const;
+
+ void promoteIfCandidate(NonBufferHash);
+ void recordPredictedResult(PredictedPlan, const std::vector<const LayerState*>& layers,
+ Plan result);
+ bool findSimilarPrediction(const std::vector<const LayerState*>& layers, Plan result);
+
+ void dumpPredictionsByFrequency(std::string&) const;
+
+ struct PromotionCandidate {
+ PromotionCandidate(NonBufferHash hash, Prediction&& prediction)
+ : hash(hash), prediction(std::move(prediction)) {}
+
+ NonBufferHash hash;
+ Prediction prediction;
+ };
+
+ static constexpr const size_t MAX_CANDIDATES = 4;
+ std::deque<PromotionCandidate> mCandidates;
+ decltype(mCandidates)::const_iterator getCandidateEntryByHash(NonBufferHash hash) const {
+ const auto candidateMatches = [&](const PromotionCandidate& candidate) {
+ return candidate.hash == hash;
+ };
+
+ return std::find_if(mCandidates.cbegin(), mCandidates.cend(), candidateMatches);
+ }
+
+ std::unordered_map<NonBufferHash, Prediction> mPredictions;
+ std::unordered_map<Plan, std::vector<NonBufferHash>> mSimilarStacks;
+
+ struct ApproximateStack {
+ ApproximateStack(NonBufferHash hash, LayerStack::ApproximateMatch match)
+ : hash(hash), match(match) {}
+
+ bool operator==(const ApproximateStack& other) const {
+ return hash == other.hash && match == other.match;
+ }
+
+ NonBufferHash hash;
+ LayerStack::ApproximateMatch match;
+ };
+
+ std::vector<ApproximateStack> mApproximateStacks;
+
+ mutable size_t mExactHitCount = 0;
+ mutable size_t mApproximateHitCount = 0;
+ mutable size_t mMissCount = 0;
+};
+
+// Defining PrintTo helps with Google Tests.
+inline void PrintTo(Predictor::PredictedPlan plan, ::std::ostream* os) {
+ *os << "PredictedPlan {";
+ *os << "\n .hash = " << plan.hash;
+ *os << "\n .plan = " << plan.plan;
+ *os << "\n .type = " << plan.type;
+ *os << "\n}";
+}
+
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
index 3a4c70f..08a8b84 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
@@ -32,7 +32,7 @@
Display();
virtual ~Display();
- MOCK_CONST_METHOD0(getId, const std::optional<DisplayId>&());
+ MOCK_CONST_METHOD0(getId, DisplayId());
MOCK_CONST_METHOD0(isSecure, bool());
MOCK_CONST_METHOD0(isVirtual, bool());
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplaySurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplaySurface.h
index 31b5f95..168e433 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplaySurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplaySurface.h
@@ -18,6 +18,7 @@
#include <compositionengine/DisplaySurface.h>
#include <gmock/gmock.h>
+#include <ui/Size.h>
#include <utils/String8.h>
namespace android::compositionengine::mock {
@@ -32,7 +33,7 @@
MOCK_METHOD0(advanceFrame, status_t());
MOCK_METHOD0(onFrameCommitted, void());
MOCK_CONST_METHOD1(dumpAsString, void(String8& result));
- MOCK_METHOD2(resizeBuffers, void(uint32_t, uint32_t));
+ MOCK_METHOD1(resizeBuffers, void(const ui::Size&));
MOCK_CONST_METHOD0(getClientTargetAcquireFence, const sp<Fence>&());
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index 45891a7..d215bda 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -42,6 +42,8 @@
MOCK_METHOD1(onLayerDisplayed, void(const sp<Fence>&));
MOCK_CONST_METHOD0(getDebugName, const char*());
+ MOCK_CONST_METHOD0(getSequence, int32_t());
+ MOCK_CONST_METHOD0(hasRoundedCorners, bool());
};
} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 4661c5d..5aa53e5 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -36,16 +36,16 @@
MOCK_CONST_METHOD0(getDisplayId, std::optional<DisplayId>());
MOCK_METHOD1(setCompositionEnabled, void(bool));
- MOCK_METHOD7(setProjection,
- void(const ui::Transform&, uint32_t, const Rect&, const Rect&, const Rect&,
- const Rect&, bool));
- MOCK_METHOD1(setBounds, void(const ui::Size&));
+ MOCK_METHOD3(setProjection, void(ui::Rotation, const Rect&, const Rect&));
+ MOCK_METHOD1(setDisplaySize, void(const ui::Size&));
MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
+ MOCK_CONST_METHOD0(getTransformHint, ui::Transform::RotationFlags());
MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD1(setColorProfile, void(const ColorProfile&));
MOCK_CONST_METHOD1(dump, void(std::string&));
+ MOCK_CONST_METHOD2(dumpPlannerInfo, void(const Vector<String16>&, std::string&));
MOCK_CONST_METHOD0(getName, const std::string&());
MOCK_METHOD1(setName, void(const std::string&));
@@ -86,7 +86,9 @@
MOCK_METHOD1(setReleasedLayers, void(const compositionengine::CompositionRefreshArgs&));
MOCK_CONST_METHOD1(updateLayerStateFromFE, void(const CompositionRefreshArgs&));
- MOCK_METHOD1(updateAndWriteCompositionState, void(const CompositionRefreshArgs&));
+ MOCK_METHOD1(updateCompositionState, void(const CompositionRefreshArgs&));
+ MOCK_METHOD0(planComposition, void());
+ MOCK_METHOD1(writeCompositionState, void(const CompositionRefreshArgs&));
MOCK_METHOD1(updateColorProfile, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD0(beginFrame, void());
@@ -105,6 +107,7 @@
MOCK_CONST_METHOD0(getSkipColorTransform, bool());
MOCK_METHOD0(postFramebuffer, void());
+ MOCK_METHOD0(renderCachedSets, void());
MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences());
MOCK_METHOD3(generateClientCompositionRequests,
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index 81e1fc7..358ed5a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -22,6 +22,7 @@
#include <compositionengine/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <gmock/gmock.h>
+#include <cstdint>
namespace android::compositionengine::mock {
@@ -39,7 +40,7 @@
MOCK_METHOD0(editState, impl::OutputLayerCompositionState&());
MOCK_METHOD3(updateCompositionState, void(bool, bool, ui::Transform::RotationFlags));
- MOCK_METHOD1(writeStateToHWC, void(bool));
+ MOCK_METHOD5(writeStateToHWC, void(bool, bool, uint32_t, bool, bool));
MOCK_CONST_METHOD0(writeCursorPositionToHWC, void());
MOCK_CONST_METHOD0(getHwcLayer, HWC2::Layer*());
@@ -49,6 +50,7 @@
MOCK_METHOD0(prepareForDeviceLayerRequests, void());
MOCK_METHOD1(applyDeviceLayerRequest, void(Hwc2::IComposerClient::LayerRequest request));
MOCK_CONST_METHOD0(needsFiltering, bool());
+ MOCK_CONST_METHOD0(getOverrideCompositionList, std::vector<LayerFE::LayerSettings>());
MOCK_CONST_METHOD1(dump, void(std::string&));
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
index a0cae6f..fe858c2 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
@@ -39,7 +39,7 @@
MOCK_METHOD1(setBufferPixelFormat, void(ui::PixelFormat));
MOCK_METHOD1(beginFrame, status_t(bool mustRecompose));
MOCK_METHOD2(prepareFrame, void(bool, bool));
- MOCK_METHOD1(dequeueBuffer, sp<GraphicBuffer>(base::unique_fd*));
+ MOCK_METHOD1(dequeueBuffer, std::shared_ptr<renderengine::ExternalTexture>(base::unique_fd*));
MOCK_METHOD1(queueBuffer, void(base::unique_fd));
MOCK_METHOD0(onPresentDisplayCompleted, void());
MOCK_METHOD0(flip, void());
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index d201104..1ffb1c8 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -51,18 +51,27 @@
void Display::setConfiguration(const compositionengine::DisplayCreationArgs& args) {
mIsVirtual = !args.physical;
- mId = args.physical ? std::make_optional(args.physical->id) : std::nullopt;
mPowerAdvisor = args.powerAdvisor;
-
editState().isSecure = args.isSecure;
-
+ editState().displaySpace.bounds = Rect(args.pixels);
setLayerStackFilter(args.layerStackId,
- args.physical ? args.physical->type == DisplayConnectionType::Internal
- : false);
+ args.physical &&
+ args.physical->type == ui::DisplayConnectionType::Internal);
setName(args.name);
+ mGpuVirtualDisplayIdGenerator = args.gpuVirtualDisplayIdGenerator;
- if (!args.physical && args.useHwcVirtualDisplays) {
- mId = maybeAllocateDisplayIdForVirtualDisplay(args.pixels, args.pixelFormat);
+ if (args.physical) {
+ mId = args.physical->id;
+ } else {
+ std::optional<DisplayId> id;
+ if (args.useHwcVirtualDisplays) {
+ id = maybeAllocateDisplayIdForVirtualDisplay(args.pixels, args.pixelFormat);
+ }
+ if (!id) {
+ id = mGpuVirtualDisplayIdGenerator->nextId();
+ }
+ LOG_ALWAYS_FATAL_IF(!id, "Failed to generate display ID");
+ mId = *id;
}
}
@@ -77,7 +86,7 @@
return Output::isValid() && mPowerAdvisor;
}
-const std::optional<DisplayId>& Display::getId() const {
+DisplayId Display::getId() const {
return mId;
}
@@ -93,31 +102,36 @@
return mId;
}
-void Display::setDisplayIdForTesting(std::optional<DisplayId> displayId) {
+void Display::setDisplayIdForTesting(DisplayId displayId) {
mId = displayId;
}
void Display::disconnect() {
- if (!mId) {
+ if (mIsDisconnected) {
return;
}
- auto& hwc = getCompositionEngine().getHwComposer();
- hwc.disconnectDisplay(*mId);
- mId.reset();
+ mIsDisconnected = true;
+ if (const auto id = GpuVirtualDisplayId::tryCast(mId)) {
+ mGpuVirtualDisplayIdGenerator->markUnused(*id);
+ return;
+ }
+ const auto halDisplayId = HalDisplayId::tryCast(mId);
+ LOG_FATAL_IF(!halDisplayId);
+ getCompositionEngine().getHwComposer().disconnectDisplay(*halDisplayId);
}
void Display::setColorTransform(const compositionengine::CompositionRefreshArgs& args) {
Output::setColorTransform(args);
-
- if (!mId || CC_LIKELY(!args.colorTransformMatrix)) {
+ const auto halDisplayId = HalDisplayId::tryCast(mId);
+ if (mIsDisconnected || !halDisplayId || CC_LIKELY(!args.colorTransformMatrix)) {
return;
}
auto& hwc = getCompositionEngine().getHwComposer();
- status_t result = hwc.setColorTransform(*mId, *args.colorTransformMatrix);
+ status_t result = hwc.setColorTransform(*halDisplayId, *args.colorTransformMatrix);
ALOGE_IF(result != NO_ERROR, "Failed to set color transform on display \"%s\": %d",
- mId ? to_string(*mId).c_str() : "", result);
+ to_string(mId).c_str(), result);
}
void Display::setColorProfile(const ColorProfile& colorProfile) {
@@ -139,8 +153,10 @@
Output::setColorProfile(colorProfile);
- auto& hwc = getCompositionEngine().getHwComposer();
- hwc.setActiveColorMode(*mId, colorProfile.mode, colorProfile.renderIntent);
+ const auto physicalId = PhysicalDisplayId::tryCast(mId);
+ LOG_FATAL_IF(!physicalId);
+ getCompositionEngine().getHwComposer().setActiveColorMode(*physicalId, colorProfile.mode,
+ colorProfile.renderIntent);
}
void Display::dump(std::string& out) const {
@@ -149,14 +165,8 @@
StringAppendF(&out, " Composition Display State: [\"%s\"]", getName().c_str());
out.append("\n ");
-
dumpVal(out, "isVirtual", mIsVirtual);
- if (mId) {
- dumpVal(out, "hwcId", to_string(*mId));
- } else {
- StringAppendF(&out, "no hwcId, ");
- }
-
+ dumpVal(out, "DisplayId", to_string(mId));
out.append("\n");
Output::dumpBase(out);
@@ -177,31 +187,33 @@
std::unique_ptr<compositionengine::OutputLayer> Display::createOutputLayer(
const sp<compositionengine::LayerFE>& layerFE) const {
- auto result = impl::createOutputLayer(*this, layerFE);
+ auto outputLayer = impl::createOutputLayer(*this, layerFE);
- if (result && mId) {
+ if (const auto halDisplayId = HalDisplayId::tryCast(mId);
+ outputLayer && !mIsDisconnected && halDisplayId) {
auto& hwc = getCompositionEngine().getHwComposer();
- auto displayId = *mId;
// Note: For the moment we ensure it is safe to take a reference to the
// HWComposer implementation by destroying all the OutputLayers (and
// hence the HWC2::Layers they own) before setting a new HWComposer. See
// for example SurfaceFlinger::updateVrFlinger().
// TODO(b/121291683): Make this safer.
- auto hwcLayer = std::shared_ptr<HWC2::Layer>(hwc.createLayer(displayId),
- [&hwc, displayId](HWC2::Layer* layer) {
- hwc.destroyLayer(displayId, layer);
- });
+ auto hwcLayer =
+ std::shared_ptr<HWC2::Layer>(hwc.createLayer(*halDisplayId),
+ [&hwc, id = *halDisplayId](HWC2::Layer* layer) {
+ hwc.destroyLayer(id, layer);
+ });
ALOGE_IF(!hwcLayer, "Failed to create a HWC layer for a HWC supported display %s",
getName().c_str());
- result->setHwcLayer(std::move(hwcLayer));
+ outputLayer->setHwcLayer(std::move(hwcLayer));
}
- return result;
+ return outputLayer;
}
void Display::setReleasedLayers(const compositionengine::CompositionRefreshArgs& refreshArgs) {
Output::setReleasedLayers(refreshArgs);
- if (!mId || refreshArgs.layersWithQueuedFrames.empty()) {
+ if (mIsDisconnected || GpuVirtualDisplayId::tryCast(mId) ||
+ refreshArgs.layersWithQueuedFrames.empty()) {
return;
}
@@ -237,19 +249,25 @@
ATRACE_CALL();
ALOGV(__FUNCTION__);
+ if (mIsDisconnected) {
+ return;
+ }
+
// Default to the base settings -- client composition only.
Output::chooseCompositionStrategy();
- // If we don't have a HWC display, then we are done
- if (!mId) {
+ // If we don't have a HWC display, then we are done.
+ const auto halDisplayId = HalDisplayId::tryCast(mId);
+ if (!halDisplayId) {
return;
}
// Get any composition changes requested by the HWC device, and apply them.
std::optional<android::HWComposer::DeviceRequestedChanges> changes;
auto& hwc = getCompositionEngine().getHwComposer();
- if (status_t result = hwc.getDeviceCompositionChanges(*mId, anyLayersRequireClientComposition(),
- &changes);
+ if (status_t result =
+ hwc.getDeviceCompositionChanges(*halDisplayId, anyLayersRequireClientComposition(),
+ &changes);
result != NO_ERROR) {
ALOGE("chooseCompositionStrategy failed for %s: %d (%s)", getName().c_str(), result,
strerror(-result));
@@ -270,8 +288,12 @@
bool Display::getSkipColorTransform() const {
const auto& hwc = getCompositionEngine().getHwComposer();
- return mId ? hwc.hasDisplayCapability(*mId, hal::DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM)
- : hwc.hasCapability(hal::Capability::SKIP_CLIENT_COLOR_TRANSFORM);
+ if (const auto halDisplayId = HalDisplayId::tryCast(mId)) {
+ return hwc.hasDisplayCapability(*halDisplayId,
+ hal::DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM);
+ }
+
+ return hwc.hasCapability(hal::Capability::SKIP_CLIENT_COLOR_TRANSFORM);
}
bool Display::anyLayersRequireClientComposition() const {
@@ -338,16 +360,22 @@
}
compositionengine::Output::FrameFences Display::presentAndGetFrameFences() {
- auto result = impl::Output::presentAndGetFrameFences();
+ auto fences = impl::Output::presentAndGetFrameFences();
- if (!mId) {
- return result;
+ const auto halDisplayIdOpt = HalDisplayId::tryCast(mId);
+ if (mIsDisconnected || !halDisplayIdOpt) {
+ return fences;
+ }
+
+ {
+ ATRACE_NAME("wait for earliest present time");
+ std::this_thread::sleep_until(getState().earliestPresentTime);
}
auto& hwc = getCompositionEngine().getHwComposer();
- hwc.presentAndGetReleaseFences(*mId);
+ hwc.presentAndGetReleaseFences(*halDisplayIdOpt);
- result.presentFence = hwc.getPresentFence(*mId);
+ fences.presentFence = hwc.getPresentFence(*halDisplayIdOpt);
// TODO(b/121291683): Change HWComposer call to return entire map
for (const auto* layer : getOutputLayersOrderedByZ()) {
@@ -356,19 +384,19 @@
continue;
}
- result.layerFences.emplace(hwcLayer, hwc.getLayerReleaseFence(*mId, hwcLayer));
+ fences.layerFences.emplace(hwcLayer, hwc.getLayerReleaseFence(*halDisplayIdOpt, hwcLayer));
}
- hwc.clearReleaseFences(*mId);
+ hwc.clearReleaseFences(*halDisplayIdOpt);
- return result;
+ return fences;
}
void Display::setExpensiveRenderingExpected(bool enabled) {
Output::setExpensiveRenderingExpected(enabled);
- if (mPowerAdvisor && mId) {
- mPowerAdvisor->setExpensiveRenderingExpected(*mId, enabled);
+ if (mPowerAdvisor && !GpuVirtualDisplayId::tryCast(mId)) {
+ mPowerAdvisor->setExpensiveRenderingExpected(mId, enabled);
}
}
@@ -377,11 +405,10 @@
// 1) It is being handled by hardware composer, which may need this to
// keep its virtual display state machine in sync, or
// 2) There is work to be done (the dirty region isn't empty)
- if (!mId) {
- if (getDirtyRegion(refreshArgs.repaintEverything).isEmpty()) {
- ALOGV("Skipping display composition");
- return;
- }
+ if (GpuVirtualDisplayId::tryCast(mId) &&
+ getDirtyRegion(refreshArgs.repaintEverything).isEmpty()) {
+ ALOGV("Skipping display composition");
+ return;
}
impl::Output::finishFrame(refreshArgs);
diff --git a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
index 9598430..0cc2c6e 100644
--- a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
@@ -77,6 +77,7 @@
void dumpVal(std::string& out, const char* name, const ui::Transform& transform) {
transform.dump(out, name);
+ out.append(" ");
}
void dumpVal(std::string& out, const char* name, const ui::Size& size) {
@@ -99,4 +100,10 @@
); /* clang-format on */
}
+void dumpVal(std::string& out, const char* name, const StretchEffect& effect) {
+ StringAppendF(&out, "%s={ area=[%f, %f, %f, %f], vec=(%f, %f), max=%f } ", name,
+ effect.area.left, effect.area.top, effect.area.right, effect.area.bottom,
+ effect.vectorX, effect.vectorY, effect.maxAmount);
+}
+
} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index 1338538..ff7d430 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -74,6 +74,23 @@
dumpVal(out, "blend", toString(blendMode), blendMode);
dumpVal(out, "alpha", alpha);
dumpVal(out, "backgroundBlurRadius", backgroundBlurRadius);
+ if (stretchEffect.hasEffect()) {
+ dumpVal(out, "stretchEffect", stretchEffect);
+ }
+
+ if (!blurRegions.empty()) {
+ out.append("\n blurRegions {");
+ for (const auto& region : blurRegions) {
+ out.append("\n ");
+ base::StringAppendF(&out,
+ "{radius=%du, cornerRadii=[%f, %f, %f, %f], alpha=%f, rect=[%d, "
+ "%d, %d, %d]",
+ region.blurRadius, region.cornerRadiusTL, region.cornerRadiusTR,
+ region.cornerRadiusBL, region.cornerRadiusBR, region.alpha,
+ region.left, region.top, region.right, region.bottom);
+ }
+ out.append("\n }\n ");
+ }
if (!metadata.empty()) {
out.append("\n metadata {");
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index d3247e1..c809e1a 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#include <thread>
-
+#include <SurfaceFlingerProperties.sysprop.h>
#include <android-base/stringprintf.h>
#include <compositionengine/CompositionEngine.h>
#include <compositionengine/CompositionRefreshArgs.h>
@@ -27,6 +26,11 @@
#include <compositionengine/impl/OutputCompositionState.h>
#include <compositionengine/impl/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <compositionengine/impl/planner/Planner.h>
+
+#include <thread>
+
+#include "renderengine/ExternalTexture.h"
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
@@ -38,6 +42,7 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion"
+#include <android-base/properties.h>
#include <ui/DebugUtils.h>
#include <ui/HdrCapabilities.h>
#include <utils/Trace.h>
@@ -50,6 +55,18 @@
namespace impl {
+Output::Output() {
+ const bool enableLayerCaching = [] {
+ const bool enable =
+ android::sysprop::SurfaceFlingerProperties::enable_layer_caching().value_or(false);
+ return base::GetBoolProperty(std::string("debug.sf.enable_layer_caching"), enable);
+ }();
+
+ if (enableLayerCaching) {
+ mPlanner = std::make_unique<planner::Planner>();
+ }
+}
+
namespace {
template <typename T>
@@ -69,6 +86,19 @@
return Reversed<T>(c);
}
+struct ScaleVector {
+ float x;
+ float y;
+};
+
+// Returns a ScaleVector (x, y) such that from.scale(x, y) = to',
+// where to' will have the same size as "to". In the case where "from" and "to"
+// start at the origin to'=to.
+ScaleVector getScale(const Rect& from, const Rect& to) {
+ return {.x = static_cast<float>(to.width()) / from.width(),
+ .y = static_cast<float>(to.height()) / from.height()};
+}
+
} // namespace
std::shared_ptr<Output> createOutput(
@@ -105,28 +135,79 @@
dirtyEntireOutput();
}
-void Output::setProjection(const ui::Transform& transform, uint32_t orientation, const Rect& frame,
- const Rect& viewport, const Rect& sourceClip,
- const Rect& destinationClip, bool needsFiltering) {
+void Output::setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
+ const Rect& orientedDisplaySpaceRect) {
auto& outputState = editState();
- outputState.transform = transform;
- outputState.orientation = orientation;
- outputState.sourceClip = sourceClip;
- outputState.destinationClip = destinationClip;
- outputState.frame = frame;
- outputState.viewport = viewport;
- outputState.needsFiltering = needsFiltering;
+
+ outputState.displaySpace.orientation = orientation;
+ LOG_FATAL_IF(outputState.displaySpace.bounds == Rect::INVALID_RECT,
+ "The display bounds are unknown.");
+
+ // Compute orientedDisplaySpace
+ ui::Size orientedSize = outputState.displaySpace.bounds.getSize();
+ if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
+ std::swap(orientedSize.width, orientedSize.height);
+ }
+ outputState.orientedDisplaySpace.bounds = Rect(orientedSize);
+ outputState.orientedDisplaySpace.content = orientedDisplaySpaceRect;
+
+ // Compute displaySpace.content
+ const uint32_t transformOrientationFlags = ui::Transform::toRotationFlags(orientation);
+ ui::Transform rotation;
+ if (transformOrientationFlags != ui::Transform::ROT_INVALID) {
+ const auto displaySize = outputState.displaySpace.bounds;
+ rotation.set(transformOrientationFlags, displaySize.width(), displaySize.height());
+ }
+ outputState.displaySpace.content = rotation.transform(orientedDisplaySpaceRect);
+
+ // Compute framebufferSpace
+ outputState.framebufferSpace.orientation = orientation;
+ LOG_FATAL_IF(outputState.framebufferSpace.bounds == Rect::INVALID_RECT,
+ "The framebuffer bounds are unknown.");
+ const auto scale =
+ getScale(outputState.displaySpace.bounds, outputState.framebufferSpace.bounds);
+ outputState.framebufferSpace.content = outputState.displaySpace.content.scale(scale.x, scale.y);
+
+ // Compute layerStackSpace
+ outputState.layerStackSpace.content = layerStackSpaceRect;
+ outputState.layerStackSpace.bounds = layerStackSpaceRect;
+
+ outputState.transform = outputState.layerStackSpace.getTransform(outputState.displaySpace);
+ outputState.needsFiltering = outputState.transform.needsBilinearFiltering();
+ dirtyEntireOutput();
+}
+
+void Output::setDisplaySize(const ui::Size& size) {
+ mRenderSurface->setDisplaySize(size);
+
+ auto& state = editState();
+
+ // Update framebuffer space
+ const Rect newBounds(size);
+ state.framebufferSpace.bounds = newBounds;
+
+ // Update display space
+ state.displaySpace.bounds = newBounds;
+ state.transform = state.layerStackSpace.getTransform(state.displaySpace);
+
+ // Update oriented display space
+ const auto orientation = state.displaySpace.orientation;
+ ui::Size orientedSize = size;
+ if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
+ std::swap(orientedSize.width, orientedSize.height);
+ }
+ const Rect newOrientedBounds(orientedSize);
+ state.orientedDisplaySpace.bounds = newOrientedBounds;
+
+ if (mPlanner) {
+ mPlanner->setDisplaySize(size);
+ }
dirtyEntireOutput();
}
-// TODO(b/121291683): Rename setSize() once more is moved.
-void Output::setBounds(const ui::Size& size) {
- mRenderSurface->setDisplaySize(size);
- // TODO(b/121291683): Rename outputState.size once more is moved.
- editState().bounds = Rect(mRenderSurface->getSize());
-
- dirtyEntireOutput();
+ui::Transform::RotationFlags Output::getTransformHint() const {
+ return static_cast<ui::Transform::RotationFlags>(getState().transform.getOrientation());
}
void Output::setLayerStackFilter(uint32_t layerStackId, bool isInternal) {
@@ -209,6 +290,15 @@
}
}
+void Output::dumpPlannerInfo(const Vector<String16>& args, std::string& out) const {
+ if (!mPlanner) {
+ base::StringAppendF(&out, "Planner is disabled\n");
+ return;
+ }
+ base::StringAppendF(&out, "Planner info for display [%s]\n", mName.c_str());
+ mPlanner->dump(args, out);
+}
+
compositionengine::DisplayColorProfile* Output::getDisplayColorProfile() const {
return mDisplayColorProfile.get();
}
@@ -232,8 +322,11 @@
void Output::setRenderSurface(std::unique_ptr<compositionengine::RenderSurface> surface) {
mRenderSurface = std::move(surface);
- editState().bounds = Rect(mRenderSurface->getSize());
-
+ const auto size = mRenderSurface->getSize();
+ editState().framebufferSpace.bounds = Rect(size);
+ if (mPlanner) {
+ mPlanner->setDisplaySize(size);
+ }
dirtyEntireOutput();
}
@@ -251,7 +344,7 @@
Region Output::getDirtyRegion(bool repaintEverything) const {
const auto& outputState = getState();
- Region dirty(outputState.viewport);
+ Region dirty(outputState.layerStackSpace.content);
if (!repaintEverything) {
dirty.andSelf(outputState.dirtyRegion);
}
@@ -309,13 +402,16 @@
ALOGV(__FUNCTION__);
updateColorProfile(refreshArgs);
- updateAndWriteCompositionState(refreshArgs);
+ updateCompositionState(refreshArgs);
+ planComposition();
+ writeCompositionState(refreshArgs);
setColorTransform(refreshArgs);
beginFrame();
prepareFrame();
devOptRepaintFlash(refreshArgs);
finishFrame(refreshArgs);
postFramebuffer();
+ renderCachedSets();
}
void Output::rebuildLayerStacks(const compositionengine::CompositionRefreshArgs& refreshArgs,
@@ -336,7 +432,7 @@
// Compute the resulting coverage for this output, and store it for later
const ui::Transform& tr = outputState.transform;
- Region undefinedRegion{outputState.bounds};
+ Region undefinedRegion{outputState.displaySpace.bounds};
undefinedRegion.subtractSelf(tr.transform(coverage.aboveOpaqueLayers));
outputState.undefinedRegion = undefinedRegion;
@@ -359,12 +455,6 @@
setReleasedLayers(refreshArgs);
finalizePendingOutputLayers();
-
- // Generate a simple Z-order values to each visible output layer
- uint32_t zOrder = 0;
- for (auto* outputLayer : getOutputLayersOrderedByZ()) {
- outputLayer->editState().z = zOrder++;
- }
}
void Output::ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>& layerFE,
@@ -539,7 +629,7 @@
// TODO(b/121291683): Why does this not use visibleRegion? (see outputSpaceVisibleRegion below)
const auto& outputState = getState();
Region drawRegion(outputState.transform.transform(visibleNonTransparentRegion));
- drawRegion.andSelf(outputState.bounds);
+ drawRegion.andSelf(outputState.displaySpace.bounds);
if (drawRegion.isEmpty()) {
return;
}
@@ -556,8 +646,8 @@
outputLayerState.visibleRegion = visibleRegion;
outputLayerState.visibleNonTransparentRegion = visibleNonTransparentRegion;
outputLayerState.coveredRegion = coveredRegion;
- outputLayerState.outputSpaceVisibleRegion =
- outputState.transform.transform(visibleNonShadowRegion.intersect(outputState.viewport));
+ outputLayerState.outputSpaceVisibleRegion = outputState.transform.transform(
+ visibleNonShadowRegion.intersect(outputState.layerStackSpace.content));
outputLayerState.shadowRegion = shadowRegion;
}
@@ -573,8 +663,7 @@
}
}
-void Output::updateAndWriteCompositionState(
- const compositionengine::CompositionRefreshArgs& refreshArgs) {
+void Output::updateCompositionState(const compositionengine::CompositionRefreshArgs& refreshArgs) {
ATRACE_CALL();
ALOGV(__FUNCTION__);
@@ -594,16 +683,83 @@
if (mLayerRequestingBackgroundBlur == layer) {
forceClientComposition = false;
}
+ }
+}
- // Send the updated state to the HWC, if appropriate.
- layer->writeStateToHWC(refreshArgs.updatingGeometryThisFrame);
+void Output::planComposition() {
+ if (!mPlanner || !getState().isEnabled) {
+ return;
+ }
+
+ ATRACE_CALL();
+ ALOGV(__FUNCTION__);
+
+ mPlanner->plan(getOutputLayersOrderedByZ());
+}
+
+void Output::writeCompositionState(const compositionengine::CompositionRefreshArgs& refreshArgs) {
+ ATRACE_CALL();
+ ALOGV(__FUNCTION__);
+
+ if (!getState().isEnabled) {
+ return;
+ }
+
+ editState().earliestPresentTime = refreshArgs.earliestPresentTime;
+
+ OutputLayer* peekThroughLayer = nullptr;
+ sp<GraphicBuffer> previousOverride = nullptr;
+ bool includeGeometry = refreshArgs.updatingGeometryThisFrame;
+ uint32_t z = 0;
+ bool overrideZ = false;
+ for (auto* layer : getOutputLayersOrderedByZ()) {
+ if (layer == peekThroughLayer) {
+ // No longer needed, although it should not show up again, so
+ // resetting it is not truly needed either.
+ peekThroughLayer = nullptr;
+
+ // peekThroughLayer was already drawn ahead of its z order.
+ continue;
+ }
+ bool skipLayer = false;
+ const auto& overrideInfo = layer->getState().overrideInfo;
+ if (overrideInfo.buffer != nullptr) {
+ if (previousOverride && overrideInfo.buffer->getBuffer() == previousOverride) {
+ ALOGV("Skipping redundant buffer");
+ skipLayer = true;
+ } else {
+ // First layer with the override buffer.
+ if (overrideInfo.peekThroughLayer) {
+ peekThroughLayer = overrideInfo.peekThroughLayer;
+
+ // Draw peekThroughLayer first.
+ overrideZ = true;
+ includeGeometry = true;
+ constexpr bool isPeekingThrough = true;
+ peekThroughLayer->writeStateToHWC(includeGeometry, false, z++, overrideZ,
+ isPeekingThrough);
+ }
+
+ previousOverride = overrideInfo.buffer->getBuffer();
+ }
+ }
+
+ constexpr bool isPeekingThrough = false;
+ layer->writeStateToHWC(includeGeometry, skipLayer, z++, overrideZ, isPeekingThrough);
}
}
compositionengine::OutputLayer* Output::findLayerRequestingBackgroundComposition() const {
compositionengine::OutputLayer* layerRequestingBgComposition = nullptr;
for (auto* layer : getOutputLayersOrderedByZ()) {
- if (layer->getLayerFE().getCompositionState()->backgroundBlurRadius > 0) {
+ auto* compState = layer->getLayerFE().getCompositionState();
+
+ // If any layer has a sideband stream, we will disable blurs. In that case, we don't
+ // want to force client composition because of the blur.
+ if (compState->sidebandStream != nullptr) {
+ return nullptr;
+ }
+ if (compState->backgroundBlurRadius > 0 || compState->blurRegions.size() > 0) {
layerRequestingBgComposition = layer;
}
}
@@ -760,6 +916,10 @@
chooseCompositionStrategy();
+ if (mPlanner) {
+ mPlanner->reportFinalPlan(getOutputLayersOrderedByZ());
+ }
+
mRenderSurface->prepareFrame(outputState.usesClientComposition,
outputState.usesDeviceComposition);
}
@@ -840,14 +1000,15 @@
}
base::unique_fd fd;
- sp<GraphicBuffer> buf;
+
+ std::shared_ptr<renderengine::ExternalTexture> tex;
// If we aren't doing client composition on this output, but do have a
// flipClientTarget request for this frame on this output, we still need to
// dequeue a buffer.
if (hasClientComposition || outputState.flipClientTarget) {
- buf = mRenderSurface->dequeueBuffer(&fd);
- if (buf == nullptr) {
+ tex = mRenderSurface->dequeueBuffer(&fd);
+ if (tex == nullptr) {
ALOGW("Dequeuing buffer for display [%s] failed, bailing out of "
"client composition for this frame",
mName.c_str());
@@ -864,9 +1025,10 @@
ALOGV("hasClientComposition");
renderengine::DisplaySettings clientCompositionDisplay;
- clientCompositionDisplay.physicalDisplay = outputState.destinationClip;
- clientCompositionDisplay.clip = outputState.sourceClip;
- clientCompositionDisplay.orientation = outputState.orientation;
+ clientCompositionDisplay.physicalDisplay = outputState.framebufferSpace.content;
+ clientCompositionDisplay.clip = outputState.layerStackSpace.content;
+ clientCompositionDisplay.orientation =
+ ui::Transform::toRotationFlags(outputState.displaySpace.orientation);
clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut()
? outputState.dataspace
: ui::Dataspace::UNKNOWN;
@@ -891,13 +1053,14 @@
// Check if the client composition requests were rendered into the provided graphic buffer. If
// so, we can reuse the buffer and avoid client composition.
if (mClientCompositionRequestCache) {
- if (mClientCompositionRequestCache->exists(buf->getId(), clientCompositionDisplay,
+ if (mClientCompositionRequestCache->exists(tex->getBuffer()->getId(),
+ clientCompositionDisplay,
clientCompositionLayers)) {
outputCompositionState.reusedClientComposition = true;
setExpensiveRenderingExpected(false);
return readyFence;
}
- mClientCompositionRequestCache->add(buf->getId(), clientCompositionDisplay,
+ mClientCompositionRequestCache->add(tex->getBuffer()->getId(), clientCompositionDisplay,
clientCompositionLayers);
}
@@ -922,14 +1085,20 @@
});
const nsecs_t renderEngineStart = systemTime();
+ // Only use the framebuffer cache when rendering to an internal display
+ // TODO(b/173560331): This is only to help mitigate memory leaks from virtual displays because
+ // right now we don't have a concrete eviction policy for output buffers: GLESRenderEngine
+ // bounds its framebuffer cache but Skia RenderEngine has no current policy. The best fix is
+ // probably to encapsulate the output buffer into a structure that dispatches resource cleanup
+ // over to RenderEngine, in which case this flag can be removed from the drawLayers interface.
+ const bool useFramebufferCache = outputState.layerStackInternal;
status_t status =
- renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayerPointers,
- buf->getNativeBuffer(), /*useFramebufferCache=*/true,
- std::move(fd), &readyFence);
+ renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, tex,
+ useFramebufferCache, std::move(fd), &readyFence);
if (status != NO_ERROR && mClientCompositionRequestCache) {
// If rendering was not successful, remove the request from the cache.
- mClientCompositionRequestCache->remove(buf->getId());
+ mClientCompositionRequestCache->remove(tex->getBuffer()->getId());
}
auto& timeStats = getCompositionEngine().getTimeStats();
@@ -950,12 +1119,14 @@
ALOGV("Rendering client layers");
const auto& outputState = getState();
- const Region viewportRegion(outputState.viewport);
- const bool useIdentityTransform = false;
+ const Region viewportRegion(outputState.layerStackSpace.content);
bool firstLayer = true;
// Used when a layer clears part of the buffer.
Region stubRegion;
+ bool disableBlurs = false;
+ sp<GraphicBuffer> previousOverrideBuffer = nullptr;
+
for (auto* layer : getOutputLayersOrderedByZ()) {
const auto& layerState = layer->getState();
const auto* layerFEState = layer->getLayerFE().getCompositionState();
@@ -969,6 +1140,8 @@
continue;
}
+ disableBlurs |= layerFEState->sidebandStream != nullptr;
+
const bool clientComposition = layer->requiresClientComposition();
// We clear the client target for non-client composed layers if
@@ -987,22 +1160,34 @@
!layerState.visibleRegion.subtract(layerState.shadowRegion).isEmpty();
if (clientComposition || clearClientComposition) {
- compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{
- clip,
- useIdentityTransform,
- layer->needsFiltering() || outputState.needsFiltering,
- outputState.isSecure,
- supportsProtectedContent,
- clientComposition ? clearRegion : stubRegion,
- outputState.viewport,
- outputDataspace,
- realContentIsVisible,
- !clientComposition, /* clearContent */
- };
- std::vector<LayerFE::LayerSettings> results =
- layerFE.prepareClientCompositionList(targetSettings);
- if (realContentIsVisible && !results.empty()) {
- layer->editState().clientCompositionTimestamp = systemTime();
+ compositionengine::LayerFE::ClientCompositionTargetSettings
+ targetSettings{.clip = clip,
+ .needsFiltering =
+ layer->needsFiltering() || outputState.needsFiltering,
+ .isSecure = outputState.isSecure,
+ .supportsProtectedContent = supportsProtectedContent,
+ .clearRegion = clientComposition ? clearRegion : stubRegion,
+ .viewport = outputState.layerStackSpace.content,
+ .dataspace = outputDataspace,
+ .realContentIsVisible = realContentIsVisible,
+ .clearContent = !clientComposition,
+ .disableBlurs = disableBlurs};
+
+ std::vector<LayerFE::LayerSettings> results;
+ if (layer->getState().overrideInfo.buffer != nullptr) {
+ if (layer->getState().overrideInfo.buffer->getBuffer() != previousOverrideBuffer) {
+ results = layer->getOverrideCompositionList();
+ previousOverrideBuffer = layer->getState().overrideInfo.buffer->getBuffer();
+ ALOGV("Replacing [%s] with override in RE", layer->getLayerFE().getDebugName());
+ } else {
+ ALOGV("Skipping redundant override buffer for [%s] in RE",
+ layer->getLayerFE().getDebugName());
+ }
+ } else {
+ results = layerFE.prepareClientCompositionList(targetSettings);
+ if (realContentIsVisible && !results.empty()) {
+ layer->editState().clientCompositionTimestamp = systemTime();
+ }
}
clientCompositionLayers.insert(clientCompositionLayers.end(),
@@ -1093,9 +1278,15 @@
mReleasedLayers.clear();
}
+void Output::renderCachedSets() {
+ if (mPlanner) {
+ mPlanner->renderCachedSets(getCompositionEngine().getRenderEngine(), getState());
+ }
+}
+
void Output::dirtyEntireOutput() {
auto& outputState = editState();
- outputState.dirtyRegion.set(outputState.bounds);
+ outputState.dirtyRegion.set(outputState.displaySpace.bounds);
}
void Output::chooseCompositionStrategy() {
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index 4835aef..ee30ad8 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -37,12 +37,14 @@
dumpVal(out, "transform", transform);
out.append("\n ");
-
- dumpVal(out, "bounds", bounds);
- dumpVal(out, "frame", frame);
- dumpVal(out, "viewport", viewport);
- dumpVal(out, "sourceClip", sourceClip);
- dumpVal(out, "destinationClip", destinationClip);
+ dumpVal(out, "layerStackSpace", to_string(layerStackSpace));
+ out.append("\n ");
+ dumpVal(out, "framebufferSpace", to_string(framebufferSpace));
+ out.append("\n ");
+ dumpVal(out, "orientedDisplaySpace", to_string(orientedDisplaySpace));
+ out.append("\n ");
+ dumpVal(out, "displaySpace", to_string(displaySpace));
+ out.append("\n ");
dumpVal(out, "needsFiltering", needsFiltering);
out.append("\n ");
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 3aed7f5..7f5c01c 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -16,12 +16,12 @@
#include <android-base/stringprintf.h>
#include <compositionengine/DisplayColorProfile.h>
-#include <compositionengine/LayerFE.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/Output.h>
#include <compositionengine/impl/OutputCompositionState.h>
#include <compositionengine/impl/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <cstdint>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
@@ -77,7 +77,7 @@
FloatRect activeCropFloat =
reduce(layerState.geomLayerBounds, layerState.transparentRegionHint);
- const Rect& viewport = getOutput().getState().viewport;
+ const Rect& viewport = getOutput().getState().layerStackSpace.content;
const ui::Transform& layerTransform = layerState.geomLayerTransform;
const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform;
// Transform to screen space.
@@ -133,7 +133,8 @@
* the code below applies the primary display's inverse transform to the
* buffer
*/
- uint32_t invTransformOrient = outputState.orientation;
+ uint32_t invTransformOrient =
+ ui::Transform::toRotationFlags(outputState.displaySpace.orientation);
// calculate the inverse transform
if (invTransformOrient & HAL_TRANSFORM_ROT_90) {
invTransformOrient ^= HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H;
@@ -189,7 +190,7 @@
Rect activeCrop = layerState.geomCrop;
if (!activeCrop.isEmpty() && bufferSize.isValid()) {
activeCrop = layerTransform.transform(activeCrop);
- if (!activeCrop.intersect(outputState.viewport, &activeCrop)) {
+ if (!activeCrop.intersect(outputState.layerStackSpace.content, &activeCrop)) {
activeCrop.clear();
}
activeCrop = inverseLayerTransform.transform(activeCrop, true);
@@ -215,7 +216,7 @@
// transformation. We then round upon constructing 'frame'.
Rect frame{
layerTransform.transform(reduce(layerState.geomLayerBounds, activeTransparentRegion))};
- if (!frame.intersect(outputState.viewport, &frame)) {
+ if (!frame.intersect(outputState.layerStackSpace.content, &frame)) {
frame.clear();
}
const ui::Transform displayTransform{outputState.transform};
@@ -312,7 +313,8 @@
}
}
-void OutputLayer::writeStateToHWC(bool includeGeometry) {
+void OutputLayer::writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z,
+ bool zIsOverridden, bool isPeekingThrough) {
const auto& state = getState();
// Skip doing this if there is no HWC interface
if (!state.hwc) {
@@ -333,50 +335,70 @@
auto requestedCompositionType = outputIndependentState->compositionType;
- if (includeGeometry) {
- writeOutputDependentGeometryStateToHWC(hwcLayer.get(), requestedCompositionType);
- writeOutputIndependentGeometryStateToHWC(hwcLayer.get(), *outputIndependentState);
+ // TODO(b/181172795): We now update geometry for all flattened layers. We should update it
+ // only when the geometry actually changes
+ const bool isOverridden =
+ state.overrideInfo.buffer != nullptr || isPeekingThrough || zIsOverridden;
+ const bool prevOverridden = state.hwc->stateOverridden;
+ if (isOverridden || prevOverridden || skipLayer || includeGeometry) {
+ writeOutputDependentGeometryStateToHWC(hwcLayer.get(), requestedCompositionType, z);
+ writeOutputIndependentGeometryStateToHWC(hwcLayer.get(), *outputIndependentState,
+ skipLayer);
}
writeOutputDependentPerFrameStateToHWC(hwcLayer.get());
writeOutputIndependentPerFrameStateToHWC(hwcLayer.get(), *outputIndependentState);
- writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType);
+ writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType, isPeekingThrough);
// Always set the layer color after setting the composition type.
writeSolidColorStateToHWC(hwcLayer.get(), *outputIndependentState);
+
+ editState().hwc->stateOverridden = isOverridden;
}
-void OutputLayer::writeOutputDependentGeometryStateToHWC(
- HWC2::Layer* hwcLayer, hal::Composition requestedCompositionType) {
+void OutputLayer::writeOutputDependentGeometryStateToHWC(HWC2::Layer* hwcLayer,
+ hal::Composition requestedCompositionType,
+ uint32_t z) {
const auto& outputDependentState = getState();
- if (auto error = hwcLayer->setDisplayFrame(outputDependentState.displayFrame);
- error != hal::Error::NONE) {
- ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)",
- getLayerFE().getDebugName(), outputDependentState.displayFrame.left,
- outputDependentState.displayFrame.top, outputDependentState.displayFrame.right,
- outputDependentState.displayFrame.bottom, to_string(error).c_str(),
- static_cast<int32_t>(error));
+ Rect displayFrame = outputDependentState.displayFrame;
+ FloatRect sourceCrop = outputDependentState.sourceCrop;
+
+ if (outputDependentState.overrideInfo.buffer != nullptr) {
+ displayFrame = outputDependentState.overrideInfo.displayFrame;
+ sourceCrop =
+ FloatRect(0.f, 0.f,
+ static_cast<float>(outputDependentState.overrideInfo.buffer->getBuffer()
+ ->getWidth()),
+ static_cast<float>(outputDependentState.overrideInfo.buffer->getBuffer()
+ ->getHeight()));
}
- if (auto error = hwcLayer->setSourceCrop(outputDependentState.sourceCrop);
- error != hal::Error::NONE) {
+ ALOGV("Writing display frame [%d, %d, %d, %d]", displayFrame.left, displayFrame.top,
+ displayFrame.right, displayFrame.bottom);
+
+ if (auto error = hwcLayer->setDisplayFrame(displayFrame); error != hal::Error::NONE) {
+ ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)",
+ getLayerFE().getDebugName(), displayFrame.left, displayFrame.top, displayFrame.right,
+ displayFrame.bottom, to_string(error).c_str(), static_cast<int32_t>(error));
+ }
+
+ if (auto error = hwcLayer->setSourceCrop(sourceCrop); error != hal::Error::NONE) {
ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: "
"%s (%d)",
- getLayerFE().getDebugName(), outputDependentState.sourceCrop.left,
- outputDependentState.sourceCrop.top, outputDependentState.sourceCrop.right,
- outputDependentState.sourceCrop.bottom, to_string(error).c_str(),
- static_cast<int32_t>(error));
+ getLayerFE().getDebugName(), sourceCrop.left, sourceCrop.top, sourceCrop.right,
+ sourceCrop.bottom, to_string(error).c_str(), static_cast<int32_t>(error));
}
- if (auto error = hwcLayer->setZOrder(outputDependentState.z); error != hal::Error::NONE) {
- ALOGE("[%s] Failed to set Z %u: %s (%d)", getLayerFE().getDebugName(),
- outputDependentState.z, to_string(error).c_str(), static_cast<int32_t>(error));
+ if (auto error = hwcLayer->setZOrder(z); error != hal::Error::NONE) {
+ ALOGE("[%s] Failed to set Z %u: %s (%d)", getLayerFE().getDebugName(), z,
+ to_string(error).c_str(), static_cast<int32_t>(error));
}
- // Solid-color layers should always use an identity transform.
- const auto bufferTransform = requestedCompositionType != hal::Composition::SOLID_COLOR
+ // Solid-color layers and overridden buffers should always use an identity transform.
+ const auto bufferTransform = (requestedCompositionType != hal::Composition::SOLID_COLOR &&
+ getState().overrideInfo.buffer == nullptr)
? outputDependentState.bufferTransform
: static_cast<hal::Transform>(0);
if (auto error = hwcLayer->setTransform(static_cast<hal::Transform>(bufferTransform));
@@ -388,18 +410,27 @@
}
void OutputLayer::writeOutputIndependentGeometryStateToHWC(
- HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState) {
- if (auto error = hwcLayer->setBlendMode(outputIndependentState.blendMode);
- error != hal::Error::NONE) {
+ HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState,
+ bool skipLayer) {
+ // If there is a peekThroughLayer, then this layer has a hole in it. We need to use
+ // PREMULTIPLIED so it will peek through.
+ const auto& overrideInfo = getState().overrideInfo;
+ const auto blendMode = overrideInfo.buffer || overrideInfo.peekThroughLayer
+ ? hardware::graphics::composer::hal::BlendMode::PREMULTIPLIED
+ : outputIndependentState.blendMode;
+ if (auto error = hwcLayer->setBlendMode(blendMode); error != hal::Error::NONE) {
ALOGE("[%s] Failed to set blend mode %s: %s (%d)", getLayerFE().getDebugName(),
- toString(outputIndependentState.blendMode).c_str(), to_string(error).c_str(),
- static_cast<int32_t>(error));
+ toString(blendMode).c_str(), to_string(error).c_str(), static_cast<int32_t>(error));
}
- if (auto error = hwcLayer->setPlaneAlpha(outputIndependentState.alpha);
- error != hal::Error::NONE) {
- ALOGE("[%s] Failed to set plane alpha %.3f: %s (%d)", getLayerFE().getDebugName(),
- outputIndependentState.alpha, to_string(error).c_str(), static_cast<int32_t>(error));
+ const float alpha = skipLayer
+ ? 0.0f
+ : (getState().overrideInfo.buffer ? 1.0f : outputIndependentState.alpha);
+ ALOGV("Writing alpha %f", alpha);
+
+ if (auto error = hwcLayer->setPlaneAlpha(alpha); error != hal::Error::NONE) {
+ ALOGE("[%s] Failed to set plane alpha %.3f: %s (%d)", getLayerFE().getDebugName(), alpha,
+ to_string(error).c_str(), static_cast<int32_t>(error));
}
for (const auto& [name, entry] : outputIndependentState.metadata) {
@@ -416,18 +447,22 @@
// TODO(lpique): b/121291683 outputSpaceVisibleRegion is output-dependent geometry
// state and should not change every frame.
- if (auto error = hwcLayer->setVisibleRegion(outputDependentState.outputSpaceVisibleRegion);
- error != hal::Error::NONE) {
+ Region visibleRegion = outputDependentState.overrideInfo.buffer
+ ? Region(outputDependentState.overrideInfo.visibleRegion)
+ : outputDependentState.outputSpaceVisibleRegion;
+ if (auto error = hwcLayer->setVisibleRegion(visibleRegion); error != hal::Error::NONE) {
ALOGE("[%s] Failed to set visible region: %s (%d)", getLayerFE().getDebugName(),
to_string(error).c_str(), static_cast<int32_t>(error));
outputDependentState.outputSpaceVisibleRegion.dump(LOG_TAG);
}
- if (auto error = hwcLayer->setDataspace(outputDependentState.dataspace);
- error != hal::Error::NONE) {
- ALOGE("[%s] Failed to set dataspace %d: %s (%d)", getLayerFE().getDebugName(),
- outputDependentState.dataspace, to_string(error).c_str(),
- static_cast<int32_t>(error));
+ const auto dataspace = outputDependentState.overrideInfo.buffer
+ ? outputDependentState.overrideInfo.dataspace
+ : outputDependentState.dataspace;
+
+ if (auto error = hwcLayer->setDataspace(dataspace); error != hal::Error::NONE) {
+ ALOGE("[%s] Failed to set dataspace %d: %s (%d)", getLayerFE().getDebugName(), dataspace,
+ to_string(error).c_str(), static_cast<int32_t>(error));
}
}
@@ -444,8 +479,11 @@
to_string(error).c_str(), static_cast<int32_t>(error));
}
- if (auto error = hwcLayer->setSurfaceDamage(outputIndependentState.surfaceDamage);
- error != hal::Error::NONE) {
+ const Region& surfaceDamage = getState().overrideInfo.buffer
+ ? getState().overrideInfo.damageRegion
+ : outputIndependentState.surfaceDamage;
+
+ if (auto error = hwcLayer->setSurfaceDamage(surfaceDamage); error != hal::Error::NONE) {
ALOGE("[%s] Failed to set surface damage: %s (%d)", getLayerFE().getDebugName(),
to_string(error).c_str(), static_cast<int32_t>(error));
outputIndependentState.surfaceDamage.dump(LOG_TAG);
@@ -508,28 +546,37 @@
to_string(error).c_str(), static_cast<int32_t>(error));
}
+ sp<GraphicBuffer> buffer = outputIndependentState.buffer;
+ sp<Fence> acquireFence = outputIndependentState.acquireFence;
+ if (getState().overrideInfo.buffer != nullptr) {
+ buffer = getState().overrideInfo.buffer->getBuffer();
+ acquireFence = getState().overrideInfo.acquireFence;
+ }
+
+ ALOGV("Writing buffer %p", buffer.get());
+
uint32_t hwcSlot = 0;
sp<GraphicBuffer> hwcBuffer;
// We need access to the output-dependent state for the buffer cache there,
// though otherwise the buffer is not output-dependent.
- editState().hwc->hwcBufferCache.getHwcBuffer(outputIndependentState.bufferSlot,
- outputIndependentState.buffer, &hwcSlot,
- &hwcBuffer);
+ editState().hwc->hwcBufferCache.getHwcBuffer(outputIndependentState.bufferSlot, buffer,
+ &hwcSlot, &hwcBuffer);
- if (auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, outputIndependentState.acquireFence);
+ if (auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence);
error != hal::Error::NONE) {
- ALOGE("[%s] Failed to set buffer %p: %s (%d)", getLayerFE().getDebugName(),
- outputIndependentState.buffer->handle, to_string(error).c_str(),
- static_cast<int32_t>(error));
+ ALOGE("[%s] Failed to set buffer %p: %s (%d)", getLayerFE().getDebugName(), buffer->handle,
+ to_string(error).c_str(), static_cast<int32_t>(error));
}
}
void OutputLayer::writeCompositionTypeToHWC(HWC2::Layer* hwcLayer,
- hal::Composition requestedCompositionType) {
+ hal::Composition requestedCompositionType,
+ bool isPeekingThrough) {
auto& outputDependentState = editState();
// If we are forcing client composition, we need to tell the HWC
- if (outputDependentState.forceClientComposition) {
+ if (outputDependentState.forceClientComposition ||
+ (!isPeekingThrough && getLayerFE().hasRoundedCorners())) {
requestedCompositionType = hal::Composition::CLIENT;
}
@@ -561,7 +608,7 @@
const auto& outputState = getOutput().getState();
Rect frame = layerFEState->cursorFrame;
- frame.intersect(outputState.viewport, &frame);
+ frame.intersect(outputState.layerStackSpace.content, &frame);
Rect position = outputState.transform.transform(frame);
if (auto error = hwcLayer->setCursorPosition(position.left, position.top);
@@ -651,6 +698,37 @@
sourceCrop.getWidth() != displayFrame.getWidth();
}
+std::vector<LayerFE::LayerSettings> OutputLayer::getOverrideCompositionList() const {
+ if (getState().overrideInfo.buffer == nullptr) {
+ return {};
+ }
+
+ // Compute the geometry boundaries in layer stack space: we need to transform from the
+ // framebuffer space of the override buffer to layer space.
+ const ProjectionSpace& layerSpace = getOutput().getState().layerStackSpace;
+ const ui::Transform transform = getState().overrideInfo.displaySpace.getTransform(layerSpace);
+ const Rect boundaries = transform.transform(getState().overrideInfo.displayFrame);
+
+ LayerFE::LayerSettings settings;
+ settings.geometry = renderengine::Geometry{
+ .boundaries = boundaries.toFloatRect(),
+ };
+ settings.bufferId = getState().overrideInfo.buffer->getBuffer()->getId();
+ settings.source = renderengine::PixelSource{
+ .buffer = renderengine::Buffer{
+ .buffer = getState().overrideInfo.buffer,
+ .fence = getState().overrideInfo.acquireFence,
+ // If the transform from layer space to display space contains a rotation, we
+ // need to undo the rotation in the texture transform
+ .textureTransform =
+ ui::Transform(transform.inverse().getOrientation(), 1, 1).asMatrix4(),
+ }};
+ settings.sourceDataspace = getState().overrideInfo.dataspace;
+ settings.alpha = 1.0f;
+
+ return {static_cast<LayerFE::LayerSettings>(settings)};
+}
+
void OutputLayer::dump(std::string& out) const {
using android::base::StringAppendF;
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
index 165e320..b4c314c 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
@@ -67,7 +67,17 @@
dumpVal(out, "sourceCrop", sourceCrop);
dumpVal(out, "bufferTransform", toString(bufferTransform), bufferTransform);
dumpVal(out, "dataspace", toString(dataspace), dataspace);
- dumpVal(out, "z-index", z);
+ dumpVal(out, "override buffer", overrideInfo.buffer.get());
+ dumpVal(out, "override acquire fence", overrideInfo.acquireFence.get());
+ dumpVal(out, "override display frame", overrideInfo.displayFrame);
+ dumpVal(out, "override dataspace", toString(overrideInfo.dataspace), overrideInfo.dataspace);
+ dumpVal(out, "override display space", to_string(overrideInfo.displaySpace));
+ std::string damageRegionString;
+ overrideInfo.damageRegion.dump(damageRegionString, "");
+ dumpVal(out, "override damage region", damageRegionString);
+ std::string visibleRegionString;
+ overrideInfo.visibleRegion.dump(visibleRegionString, "");
+ dumpVal(out, "override visible region", visibleRegionString);
if (hwc) {
dumpHwc(*hwc, out);
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
index 2773fd3..ef50870 100644
--- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -25,8 +25,8 @@
#include <compositionengine/impl/DumpHelpers.h>
#include <compositionengine/impl/OutputCompositionState.h>
#include <compositionengine/impl/RenderSurface.h>
-
#include <log/log.h>
+#include <renderengine/ExternalTexture.h>
#include <renderengine/RenderEngine.h>
#include <system/window.h>
#include <ui/GraphicBuffer.h>
@@ -48,6 +48,8 @@
namespace impl {
+constexpr auto DEFAULT_USAGE = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
+
std::unique_ptr<compositionengine::RenderSurface> createRenderSurface(
const compositionengine::CompositionEngine& compositionEngine,
compositionengine::Display& display,
@@ -61,7 +63,8 @@
mDisplay(display),
mNativeWindow(args.nativeWindow),
mDisplaySurface(args.displaySurface),
- mSize(args.displayWidth, args.displayHeight) {
+ mSize(args.displayWidth, args.displayHeight),
+ mMaxTextureCacheSize(args.maxTextureCacheSize) {
LOG_ALWAYS_FATAL_IF(!mNativeWindow);
}
@@ -81,7 +84,7 @@
ALOGE_IF(status != NO_ERROR, "Unable to connect BQ producer: %d", status);
status = native_window_set_buffers_format(window, HAL_PIXEL_FORMAT_RGBA_8888);
ALOGE_IF(status != NO_ERROR, "Unable to set BQ format to RGBA888: %d", status);
- status = native_window_set_usage(window, GRALLOC_USAGE_HW_RENDER);
+ status = native_window_set_usage(window, DEFAULT_USAGE);
ALOGE_IF(status != NO_ERROR, "Unable to set BQ usage bits for GPU rendering: %d", status);
}
@@ -94,8 +97,7 @@
}
void RenderSurface::setDisplaySize(const ui::Size& size) {
- mDisplaySurface->resizeBuffers(static_cast<uint32_t>(size.width),
- static_cast<uint32_t>(size.height));
+ mDisplaySurface->resizeBuffers(size);
mSize = size;
}
@@ -109,7 +111,7 @@
}
void RenderSurface::setProtected(bool useProtected) {
- uint64_t usageFlags = GRALLOC_USAGE_HW_RENDER;
+ uint64_t usageFlags = DEFAULT_USAGE;
if (useProtected) {
usageFlags |= GRALLOC_USAGE_PROTECTED;
}
@@ -145,7 +147,8 @@
}
}
-sp<GraphicBuffer> RenderSurface::dequeueBuffer(base::unique_fd* bufferFence) {
+std::shared_ptr<renderengine::ExternalTexture> RenderSurface::dequeueBuffer(
+ base::unique_fd* bufferFence) {
ATRACE_CALL();
int fd = -1;
ANativeWindowBuffer* buffer = nullptr;
@@ -157,16 +160,41 @@
mDisplay.getName().c_str(), result);
// Return fast here as we can't do much more - any rendering we do
// now will just be wrong.
- return mGraphicBuffer;
+ return mTexture;
}
- ALOGW_IF(mGraphicBuffer != nullptr, "Clobbering a non-null pointer to a buffer [%p].",
- mGraphicBuffer->getNativeBuffer()->handle);
- mGraphicBuffer = GraphicBuffer::from(buffer);
+ ALOGW_IF(mTexture != nullptr, "Clobbering a non-null pointer to a buffer [%p].",
+ mTexture->getBuffer()->getNativeBuffer()->handle);
+
+ sp<GraphicBuffer> newBuffer = GraphicBuffer::from(buffer);
+
+ std::shared_ptr<renderengine::ExternalTexture> texture;
+
+ for (auto it = mTextureCache.begin(); it != mTextureCache.end(); it++) {
+ const auto& cachedTexture = *it;
+ if (cachedTexture->getBuffer()->getId() == newBuffer->getId()) {
+ texture = cachedTexture;
+ mTextureCache.erase(it);
+ break;
+ }
+ }
+
+ if (texture) {
+ mTexture = texture;
+ } else {
+ mTexture = std::make_shared<
+ renderengine::ExternalTexture>(GraphicBuffer::from(buffer),
+ mCompositionEngine.getRenderEngine(),
+ renderengine::ExternalTexture::Usage::WRITEABLE);
+ }
+ mTextureCache.push_back(mTexture);
+ if (mTextureCache.size() > mMaxTextureCacheSize) {
+ mTextureCache.erase(mTextureCache.begin());
+ }
*bufferFence = base::unique_fd(fd);
- return mGraphicBuffer;
+ return mTexture;
}
void RenderSurface::queueBuffer(base::unique_fd readyFence) {
@@ -176,24 +204,24 @@
// hasFlipClientTargetRequest could return true even if we haven't
// dequeued a buffer before. Try dequeueing one if we don't have a
// buffer ready.
- if (mGraphicBuffer == nullptr) {
+ if (mTexture == nullptr) {
ALOGI("Attempting to queue a client composited buffer without one "
"previously dequeued for display [%s]. Attempting to dequeue "
"a scratch buffer now",
mDisplay.getName().c_str());
- // We shouldn't deadlock here, since mGraphicBuffer == nullptr only
+ // We shouldn't deadlock here, since mTexture == nullptr only
// after a successful call to queueBuffer, or if dequeueBuffer has
// never been called.
base::unique_fd unused;
dequeueBuffer(&unused);
}
- if (mGraphicBuffer == nullptr) {
+ if (mTexture == nullptr) {
ALOGE("No buffer is ready for display [%s]", mDisplay.getName().c_str());
} else {
- status_t result =
- mNativeWindow->queueBuffer(mNativeWindow.get(),
- mGraphicBuffer->getNativeBuffer(), dup(readyFence));
+ status_t result = mNativeWindow->queueBuffer(mNativeWindow.get(),
+ mTexture->getBuffer()->getNativeBuffer(),
+ dup(readyFence));
if (result != NO_ERROR) {
ALOGE("Error when queueing buffer for display [%s]: %d", mDisplay.getName().c_str(),
result);
@@ -203,11 +231,12 @@
LOG_ALWAYS_FATAL("ANativeWindow::queueBuffer failed with error: %d", result);
} else {
mNativeWindow->cancelBuffer(mNativeWindow.get(),
- mGraphicBuffer->getNativeBuffer(), dup(readyFence));
+ mTexture->getBuffer()->getNativeBuffer(),
+ dup(readyFence));
}
}
- mGraphicBuffer = nullptr;
+ mTexture = nullptr;
}
}
@@ -255,8 +284,8 @@
mSize = size;
}
-sp<GraphicBuffer>& RenderSurface::mutableGraphicBufferForTest() {
- return mGraphicBuffer;
+std::shared_ptr<renderengine::ExternalTexture>& RenderSurface::mutableTextureForTest() {
+ return mTexture;
}
} // namespace impl
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
new file mode 100644
index 0000000..4cf83d9
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -0,0 +1,340 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "Planner"
+// #define LOG_NDEBUG 0
+
+#include <android-base/properties.h>
+#include <compositionengine/impl/OutputCompositionState.h>
+#include <compositionengine/impl/planner/CachedSet.h>
+#include <math/HashCombine.h>
+#include <renderengine/DisplaySettings.h>
+#include <renderengine/RenderEngine.h>
+
+namespace android::compositionengine::impl::planner {
+
+const bool CachedSet::sDebugHighlighLayers =
+ base::GetBoolProperty(std::string("debug.sf.layer_caching_highlight"), false);
+
+std::string durationString(std::chrono::milliseconds duration) {
+ using namespace std::chrono_literals;
+
+ std::string result;
+
+ if (duration >= 1h) {
+ const auto hours = std::chrono::duration_cast<std::chrono::hours>(duration);
+ base::StringAppendF(&result, "%d hr ", static_cast<int>(hours.count()));
+ duration -= hours;
+ }
+ if (duration >= 1min) {
+ const auto minutes = std::chrono::duration_cast<std::chrono::minutes>(duration);
+ base::StringAppendF(&result, "%d min ", static_cast<int>(minutes.count()));
+ duration -= minutes;
+ }
+ base::StringAppendF(&result, "%.3f sec ", duration.count() / 1000.0f);
+
+ return result;
+}
+
+CachedSet::Layer::Layer(const LayerState* state, std::chrono::steady_clock::time_point lastUpdate)
+ : mState(state), mHash(state->getHash()), mLastUpdate(lastUpdate) {}
+
+CachedSet::CachedSet(const LayerState* layer, std::chrono::steady_clock::time_point lastUpdate)
+ : mFingerprint(layer->getHash()), mLastUpdate(lastUpdate) {
+ addLayer(layer, lastUpdate);
+}
+
+CachedSet::CachedSet(Layer layer)
+ : mFingerprint(layer.getHash()),
+ mLastUpdate(layer.getLastUpdate()),
+ mBounds(layer.getDisplayFrame()),
+ mVisibleRegion(layer.getVisibleRegion()) {
+ mLayers.emplace_back(std::move(layer));
+}
+
+void CachedSet::addLayer(const LayerState* layer,
+ std::chrono::steady_clock::time_point lastUpdate) {
+ mLayers.emplace_back(layer, lastUpdate);
+
+ Region boundingRegion;
+ boundingRegion.orSelf(mBounds);
+ boundingRegion.orSelf(layer->getDisplayFrame());
+ mBounds = boundingRegion.getBounds();
+ mVisibleRegion.orSelf(layer->getVisibleRegion());
+}
+
+NonBufferHash CachedSet::getNonBufferHash() const {
+ if (mLayers.size() == 1) {
+ return mFingerprint;
+ }
+
+ // TODO(b/182614524): We sometimes match this with LayerState hashes. Determine if that is
+ // necessary (and therefore we need to match implementations).
+ size_t hash = 0;
+ android::hashCombineSingle(hash, mBounds);
+ android::hashCombineSingle(hash, mOutputDataspace);
+ android::hashCombineSingle(hash, mOrientation);
+ return hash;
+}
+
+size_t CachedSet::getComponentDisplayCost() const {
+ size_t displayCost = 0;
+
+ for (const Layer& layer : mLayers) {
+ displayCost += static_cast<size_t>(layer.getDisplayFrame().width() *
+ layer.getDisplayFrame().height());
+ }
+
+ return displayCost;
+}
+
+size_t CachedSet::getCreationCost() const {
+ if (mLayers.size() == 1) {
+ return 0;
+ }
+
+ // Reads
+ size_t creationCost = getComponentDisplayCost();
+
+ // Write - assumes that the output buffer only gets written once per pixel
+ creationCost += static_cast<size_t>(mBounds.width() * mBounds.height());
+
+ return creationCost;
+}
+
+size_t CachedSet::getDisplayCost() const {
+ return static_cast<size_t>(mBounds.width() * mBounds.height());
+}
+
+bool CachedSet::hasBufferUpdate() const {
+ for (const Layer& layer : mLayers) {
+ if (layer.getFramesSinceBufferUpdate() == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool CachedSet::hasReadyBuffer() const {
+ return mTexture != nullptr && mDrawFence->getStatus() == Fence::Status::Signaled;
+}
+
+std::vector<CachedSet> CachedSet::decompose() const {
+ std::vector<CachedSet> layers;
+
+ std::transform(mLayers.begin(), mLayers.end(), std::back_inserter(layers),
+ [](Layer layer) { return CachedSet(std::move(layer)); });
+
+ return layers;
+}
+
+void CachedSet::updateAge(std::chrono::steady_clock::time_point now) {
+ LOG_ALWAYS_FATAL_IF(mLayers.size() > 1, "[%s] This should only be called on single-layer sets",
+ __func__);
+
+ if (mLayers[0].getFramesSinceBufferUpdate() == 0) {
+ mLastUpdate = now;
+ mAge = 0;
+ }
+}
+
+void CachedSet::render(renderengine::RenderEngine& renderEngine,
+ const OutputCompositionState& outputState) {
+ const Rect& viewport = outputState.layerStackSpace.content;
+ const ui::Dataspace& outputDataspace = outputState.dataspace;
+ const ui::Transform::RotationFlags orientation =
+ ui::Transform::toRotationFlags(outputState.framebufferSpace.orientation);
+
+ renderengine::DisplaySettings displaySettings{
+ .physicalDisplay = Rect(-mBounds.left + outputState.framebufferSpace.content.left,
+ -mBounds.top + outputState.framebufferSpace.content.top,
+ -mBounds.left + outputState.framebufferSpace.content.right,
+ -mBounds.top + outputState.framebufferSpace.content.bottom),
+ .clip = viewport,
+ .outputDataspace = outputDataspace,
+ .orientation = orientation,
+ };
+
+ Region clearRegion = Region::INVALID_REGION;
+ LayerFE::ClientCompositionTargetSettings targetSettings{
+ .clip = Region(viewport),
+ .needsFiltering = false,
+ .isSecure = true,
+ .supportsProtectedContent = false,
+ .clearRegion = clearRegion,
+ .viewport = viewport,
+ .dataspace = outputDataspace,
+ .realContentIsVisible = true,
+ .clearContent = false,
+ .disableBlurs = false,
+ };
+
+ std::vector<renderengine::LayerSettings> layerSettings;
+ renderengine::LayerSettings highlight;
+ for (const auto& layer : mLayers) {
+ const auto clientCompositionList =
+ layer.getState()->getOutputLayer()->getLayerFE().prepareClientCompositionList(
+ targetSettings);
+ layerSettings.insert(layerSettings.end(), clientCompositionList.cbegin(),
+ clientCompositionList.cend());
+ }
+
+ std::vector<const renderengine::LayerSettings*> layerSettingsPointers;
+ std::transform(layerSettings.cbegin(), layerSettings.cend(),
+ std::back_inserter(layerSettingsPointers),
+ [](const renderengine::LayerSettings& settings) { return &settings; });
+ renderengine::LayerSettings holePunchSettings;
+ if (mHolePunchLayer) {
+ auto clientCompositionList =
+ mHolePunchLayer->getOutputLayer()->getLayerFE().prepareClientCompositionList(
+ targetSettings);
+ // Assume that the final layer contains the buffer that we want to
+ // replace with a hole punch.
+ holePunchSettings = clientCompositionList.back();
+ LOG_ALWAYS_FATAL_IF(!holePunchSettings.source.buffer.buffer, "Expected to have a buffer!");
+ // This mimics Layer::prepareClearClientComposition
+ holePunchSettings.source.buffer.buffer = nullptr;
+ holePunchSettings.source.solidColor = half3(0.0f, 0.0f, 0.0f);
+ holePunchSettings.disableBlending = true;
+ holePunchSettings.alpha = 0.0f;
+ holePunchSettings.name = std::string("hole punch layer");
+ layerSettingsPointers.push_back(&holePunchSettings);
+ }
+
+ if (sDebugHighlighLayers) {
+ highlight = {
+ .geometry =
+ renderengine::Geometry{
+ .boundaries = FloatRect(0.0f, 0.0f,
+ static_cast<float>(mBounds.getWidth()),
+ static_cast<float>(mBounds.getHeight())),
+ },
+ .source =
+ renderengine::PixelSource{
+ .solidColor = half3(0.25f, 0.0f, 0.5f),
+ },
+ .alpha = half(0.05f),
+ };
+
+ layerSettingsPointers.emplace_back(&highlight);
+ }
+
+ const uint64_t usageFlags = GraphicBuffer::USAGE_HW_RENDER | GraphicBuffer::USAGE_HW_COMPOSER |
+ GraphicBuffer::USAGE_HW_TEXTURE;
+ sp<GraphicBuffer> buffer = new GraphicBuffer(static_cast<uint32_t>(mBounds.getWidth()),
+ static_cast<uint32_t>(mBounds.getHeight()),
+ HAL_PIXEL_FORMAT_RGBA_8888, 1, usageFlags);
+ const auto texture = std::make_shared<
+ renderengine::ExternalTexture>(buffer, renderEngine,
+ renderengine::ExternalTexture::Usage::READABLE |
+ renderengine::ExternalTexture::Usage::WRITEABLE);
+ LOG_ALWAYS_FATAL_IF(buffer->initCheck() != OK);
+ base::unique_fd drawFence;
+
+ status_t result = renderEngine.drawLayers(displaySettings, layerSettingsPointers, texture,
+ false, base::unique_fd(), &drawFence);
+
+ if (result == NO_ERROR) {
+ mDrawFence = new Fence(drawFence.release());
+ mOutputSpace = outputState.framebufferSpace;
+ mTexture = std::move(texture);
+ mOutputSpace.orientation = outputState.framebufferSpace.orientation;
+ mOutputDataspace = outputDataspace;
+ mOrientation = orientation;
+ } else {
+ mTexture = nullptr;
+ }
+}
+
+bool CachedSet::requiresHolePunch() const {
+ // In order for the hole punch to be beneficial, the layer must be updating
+ // regularly, meaning it should not have been merged with other layers.
+ if (getLayerCount() != 1) {
+ return false;
+ }
+
+ // There is no benefit to a hole punch unless the layer has a buffer.
+ if (!mLayers[0].getBuffer()) {
+ return false;
+ }
+
+ const auto& layerFE = mLayers[0].getState()->getOutputLayer()->getLayerFE();
+ if (layerFE.getCompositionState()->forceClientComposition) {
+ return false;
+ }
+
+ return layerFE.hasRoundedCorners();
+}
+
+namespace {
+bool contains(const Rect& outer, const Rect& inner) {
+ return outer.left <= inner.left && outer.right >= inner.right && outer.top <= inner.top &&
+ outer.bottom >= inner.bottom;
+}
+}; // namespace
+
+void CachedSet::addHolePunchLayerIfFeasible(const CachedSet& holePunchLayer, bool isFirstLayer) {
+ // Verify that this CachedSet is opaque where the hole punch layer
+ // will draw.
+ const Rect& holePunchBounds = holePunchLayer.getBounds();
+ for (const auto& layer : mLayers) {
+ // The first layer is considered opaque because nothing is behind it.
+ // Note that isOpaque is always false for a layer with rounded
+ // corners, even if the interior is opaque. In theory, such a layer
+ // could be used for a hole punch, but this is unlikely to happen in
+ // practice.
+ const auto* outputLayer = layer.getState()->getOutputLayer();
+ if (contains(outputLayer->getState().displayFrame, holePunchBounds) &&
+ (isFirstLayer || outputLayer->getLayerFE().getCompositionState()->isOpaque)) {
+ mHolePunchLayer = holePunchLayer.getFirstLayer().getState();
+ return;
+ }
+ }
+}
+
+OutputLayer* CachedSet::getHolePunchLayer() const {
+ return mHolePunchLayer ? mHolePunchLayer->getOutputLayer() : nullptr;
+}
+
+void CachedSet::dump(std::string& result) const {
+ const auto now = std::chrono::steady_clock::now();
+
+ const auto lastUpdate =
+ std::chrono::duration_cast<std::chrono::milliseconds>(now - mLastUpdate);
+ base::StringAppendF(&result, " + Fingerprint %016zx, last update %sago, age %zd\n",
+ mFingerprint, durationString(lastUpdate).c_str(), mAge);
+ {
+ const auto b = mTexture ? mTexture->getBuffer().get() : nullptr;
+ base::StringAppendF(&result, " Override buffer: %p\n", b);
+ }
+ base::StringAppendF(&result, " HolePunchLayer: %p\n", mHolePunchLayer);
+
+ if (mLayers.size() == 1) {
+ base::StringAppendF(&result, " Layer [%s]\n", mLayers[0].getName().c_str());
+ base::StringAppendF(&result, " Buffer %p", mLayers[0].getBuffer().get());
+ } else {
+ result.append(" Cached set of:");
+ for (const Layer& layer : mLayers) {
+ base::StringAppendF(&result, "\n Layer [%s]", layer.getName().c_str());
+ }
+ }
+
+ base::StringAppendF(&result, "\n Creation cost: %zd", getCreationCost());
+ base::StringAppendF(&result, "\n Display cost: %zd\n", getDisplayCost());
+}
+
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
new file mode 100644
index 0000000..b4e7610
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -0,0 +1,412 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "Planner"
+// #define LOG_NDEBUG 0
+
+#include <compositionengine/impl/planner/Flattener.h>
+#include <compositionengine/impl/planner/LayerState.h>
+#include <compositionengine/impl/planner/Predictor.h>
+
+using time_point = std::chrono::steady_clock::time_point;
+using namespace std::chrono_literals;
+
+namespace android::compositionengine::impl::planner {
+
+namespace {
+
+// True if the underlying layer stack is the same modulo state that would be expected to be
+// different like specific buffers, false otherwise.
+bool isSameStack(const std::vector<const LayerState*>& incomingLayers,
+ const std::vector<CachedSet>& cachedSets) {
+ std::vector<const LayerState*> existingLayers;
+ for (auto& cachedSet : cachedSets) {
+ for (auto& layer : cachedSet.getConstituentLayers()) {
+ existingLayers.push_back(layer.getState());
+ }
+ }
+
+ if (incomingLayers.size() != existingLayers.size()) {
+ return false;
+ }
+
+ for (size_t i = 0; i < incomingLayers.size(); i++) {
+ if (incomingLayers[i]->getDifferingFields(*(existingLayers[i])) != LayerStateField::None) {
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+NonBufferHash Flattener::flattenLayers(const std::vector<const LayerState*>& layers,
+ NonBufferHash hash, time_point now) {
+ const size_t unflattenedDisplayCost = calculateDisplayCost(layers);
+ mUnflattenedDisplayCost += unflattenedDisplayCost;
+
+ // We invalidate the layer cache if:
+ // 1. We're not tracking any layers, or
+ // 2. The last seen hashed geometry changed between frames, or
+ // 3. A stricter equality check demonstrates that the layer stack really did change, since the
+ // hashed geometry does not guarantee uniqueness.
+ if (mCurrentGeometry != hash || (!mLayers.empty() && !isSameStack(layers, mLayers))) {
+ resetActivities(hash, now);
+ mFlattenedDisplayCost += unflattenedDisplayCost;
+ return hash;
+ }
+
+ ++mInitialLayerCounts[layers.size()];
+
+ // Only buildCachedSets if these layers are already stored in mLayers.
+ // Otherwise (i.e. mergeWithCachedSets returns false), the time has not
+ // changed, so buildCachedSets will never find any runs.
+ const bool alreadyHadCachedSets = mergeWithCachedSets(layers, now);
+
+ ++mFinalLayerCounts[mLayers.size()];
+
+ if (alreadyHadCachedSets) {
+ buildCachedSets(now);
+ hash = computeLayersHash();
+ }
+
+ return hash;
+}
+
+void Flattener::renderCachedSets(renderengine::RenderEngine& renderEngine,
+ const OutputCompositionState& outputState) {
+ if (!mNewCachedSet) {
+ return;
+ }
+
+ mNewCachedSet->render(renderEngine, outputState);
+}
+
+void Flattener::dump(std::string& result) const {
+ const auto now = std::chrono::steady_clock::now();
+
+ base::StringAppendF(&result, "Flattener state:\n");
+
+ result.append("\n Statistics:\n");
+
+ result.append(" Display cost (in screen-size buffers):\n");
+ const size_t displayArea = static_cast<size_t>(mDisplaySize.width * mDisplaySize.height);
+ base::StringAppendF(&result, " Unflattened: %.2f\n",
+ static_cast<float>(mUnflattenedDisplayCost) / displayArea);
+ base::StringAppendF(&result, " Flattened: %.2f\n",
+ static_cast<float>(mFlattenedDisplayCost) / displayArea);
+
+ const auto compareLayerCounts = [](const std::pair<size_t, size_t>& left,
+ const std::pair<size_t, size_t>& right) {
+ return left.first < right.first;
+ };
+
+ const size_t maxLayerCount = std::max_element(mInitialLayerCounts.cbegin(),
+ mInitialLayerCounts.cend(), compareLayerCounts)
+ ->first;
+
+ result.append("\n Initial counts:\n");
+ for (size_t count = 1; count < maxLayerCount; ++count) {
+ size_t initial = mInitialLayerCounts.count(count) > 0 ? mInitialLayerCounts.at(count) : 0;
+ base::StringAppendF(&result, " % 2zd: %zd\n", count, initial);
+ }
+
+ result.append("\n Final counts:\n");
+ for (size_t count = 1; count < maxLayerCount; ++count) {
+ size_t final = mFinalLayerCounts.count(count) > 0 ? mFinalLayerCounts.at(count) : 0;
+ base::StringAppendF(&result, " % 2zd: %zd\n", count, final);
+ }
+
+ base::StringAppendF(&result, "\n Cached sets created: %zd\n", mCachedSetCreationCount);
+ base::StringAppendF(&result, " Cost: %.2f\n",
+ static_cast<float>(mCachedSetCreationCost) / displayArea);
+
+ const auto lastUpdate =
+ std::chrono::duration_cast<std::chrono::milliseconds>(now - mLastGeometryUpdate);
+ base::StringAppendF(&result, "\n Current hash %016zx, last update %sago\n\n", mCurrentGeometry,
+ durationString(lastUpdate).c_str());
+
+ result.append(" Current layers:");
+ for (const CachedSet& layer : mLayers) {
+ result.append("\n");
+ layer.dump(result);
+ }
+}
+
+size_t Flattener::calculateDisplayCost(const std::vector<const LayerState*>& layers) const {
+ Region coveredRegion;
+ size_t displayCost = 0;
+ bool hasClientComposition = false;
+
+ for (const LayerState* layer : layers) {
+ coveredRegion.orSelf(layer->getDisplayFrame());
+
+ // Regardless of composition type, we always have to read each input once
+ displayCost += static_cast<size_t>(layer->getDisplayFrame().width() *
+ layer->getDisplayFrame().height());
+
+ hasClientComposition |= layer->getCompositionType() == hal::Composition::CLIENT;
+ }
+
+ if (hasClientComposition) {
+ // If there is client composition, the client target buffer has to be both written by the
+ // GPU and read by the DPU, so we pay its cost twice
+ displayCost += 2 *
+ static_cast<size_t>(coveredRegion.bounds().width() *
+ coveredRegion.bounds().height());
+ }
+
+ return displayCost;
+}
+
+void Flattener::resetActivities(NonBufferHash hash, time_point now) {
+ ALOGV("[%s]", __func__);
+
+ mCurrentGeometry = hash;
+ mLastGeometryUpdate = now;
+
+ for (const CachedSet& cachedSet : mLayers) {
+ if (cachedSet.getLayerCount() > 1) {
+ ++mInvalidatedCachedSetAges[cachedSet.getAge()];
+ }
+ }
+
+ mLayers.clear();
+
+ if (mNewCachedSet) {
+ ++mInvalidatedCachedSetAges[mNewCachedSet->getAge()];
+ mNewCachedSet = std::nullopt;
+ }
+}
+
+NonBufferHash Flattener::computeLayersHash() const{
+ size_t hash = 0;
+ for (const auto& layer : mLayers) {
+ android::hashCombineSingleHashed(hash, layer.getNonBufferHash());
+ }
+ return hash;
+}
+
+// Only called if the geometry matches the last frame. Return true if mLayers
+// was already populated with these layers, i.e. on the second and following
+// calls with the same geometry.
+bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers, time_point now) {
+ std::vector<CachedSet> merged;
+
+ if (mLayers.empty()) {
+ merged.reserve(layers.size());
+ for (const LayerState* layer : layers) {
+ merged.emplace_back(layer, now);
+ mFlattenedDisplayCost += merged.back().getDisplayCost();
+ }
+ mLayers = std::move(merged);
+ return false;
+ }
+
+ ALOGV("[%s] Incoming layers:", __func__);
+ for (const LayerState* layer : layers) {
+ ALOGV("%s", layer->getName().c_str());
+ }
+
+ ALOGV("[%s] Current layers:", __func__);
+ for (const CachedSet& layer : mLayers) {
+ std::string dump;
+ layer.dump(dump);
+ ALOGV("%s", dump.c_str());
+ }
+
+ auto currentLayerIter = mLayers.begin();
+ auto incomingLayerIter = layers.begin();
+ while (incomingLayerIter != layers.end()) {
+ if (mNewCachedSet && mNewCachedSet->getFingerprint() == (*incomingLayerIter)->getHash()) {
+ if (mNewCachedSet->hasBufferUpdate()) {
+ ALOGV("[%s] Dropping new cached set", __func__);
+ ++mInvalidatedCachedSetAges[0];
+ mNewCachedSet = std::nullopt;
+ } else if (mNewCachedSet->hasReadyBuffer()) {
+ ALOGV("[%s] Found ready buffer", __func__);
+ size_t skipCount = mNewCachedSet->getLayerCount();
+ while (skipCount != 0) {
+ auto* peekThroughLayer = mNewCachedSet->getHolePunchLayer();
+ const size_t layerCount = currentLayerIter->getLayerCount();
+ for (size_t i = 0; i < layerCount; ++i) {
+ OutputLayer::CompositionState& state =
+ (*incomingLayerIter)->getOutputLayer()->editState();
+ state.overrideInfo = {
+ .buffer = mNewCachedSet->getBuffer(),
+ .acquireFence = mNewCachedSet->getDrawFence(),
+ .displayFrame = mNewCachedSet->getBounds(),
+ .dataspace = mNewCachedSet->getOutputDataspace(),
+ .displaySpace = mNewCachedSet->getOutputSpace(),
+ .damageRegion = Region::INVALID_REGION,
+ .visibleRegion = mNewCachedSet->getVisibleRegion(),
+ .peekThroughLayer = peekThroughLayer,
+ };
+ ++incomingLayerIter;
+ }
+
+ if (currentLayerIter->getLayerCount() > 1) {
+ ++mInvalidatedCachedSetAges[currentLayerIter->getAge()];
+ }
+ ++currentLayerIter;
+
+ skipCount -= layerCount;
+ }
+ merged.emplace_back(std::move(*mNewCachedSet));
+ mNewCachedSet = std::nullopt;
+ continue;
+ }
+ }
+
+ if (!currentLayerIter->hasBufferUpdate()) {
+ currentLayerIter->incrementAge();
+ merged.emplace_back(*currentLayerIter);
+
+ // Skip the incoming layers corresponding to this valid current layer
+ const size_t layerCount = currentLayerIter->getLayerCount();
+ auto* peekThroughLayer = currentLayerIter->getHolePunchLayer();
+ for (size_t i = 0; i < layerCount; ++i) {
+ OutputLayer::CompositionState& state =
+ (*incomingLayerIter)->getOutputLayer()->editState();
+ state.overrideInfo = {
+ .buffer = currentLayerIter->getBuffer(),
+ .acquireFence = currentLayerIter->getDrawFence(),
+ .displayFrame = currentLayerIter->getBounds(),
+ .dataspace = currentLayerIter->getOutputDataspace(),
+ .displaySpace = currentLayerIter->getOutputSpace(),
+ .damageRegion = Region(),
+ .visibleRegion = currentLayerIter->getVisibleRegion(),
+ .peekThroughLayer = peekThroughLayer,
+ };
+ ++incomingLayerIter;
+ }
+ } else if (currentLayerIter->getLayerCount() > 1) {
+ // Break the current layer into its constituent layers
+ ++mInvalidatedCachedSetAges[currentLayerIter->getAge()];
+ for (CachedSet& layer : currentLayerIter->decompose()) {
+ layer.updateAge(now);
+ merged.emplace_back(layer);
+ ++incomingLayerIter;
+ }
+ } else {
+ currentLayerIter->updateAge(now);
+ merged.emplace_back(*currentLayerIter);
+ ++incomingLayerIter;
+ }
+ ++currentLayerIter;
+ }
+
+ for (const CachedSet& layer : merged) {
+ mFlattenedDisplayCost += layer.getDisplayCost();
+ }
+
+ mLayers = std::move(merged);
+ return true;
+}
+
+void Flattener::buildCachedSets(time_point now) {
+ struct Run {
+ Run(std::vector<CachedSet>::const_iterator start, size_t length)
+ : start(start), length(length) {}
+
+ std::vector<CachedSet>::const_iterator start;
+ size_t length;
+ };
+
+ if (mLayers.empty()) {
+ ALOGV("[%s] No layers found, returning", __func__);
+ return;
+ }
+
+ std::vector<Run> runs;
+ bool isPartOfRun = false;
+
+ // Keep track of the layer that follows a run. It's possible that we will
+ // render it with a hole-punch.
+ const CachedSet* holePunchLayer = nullptr;
+
+ for (auto currentSet = mLayers.cbegin(); currentSet != mLayers.cend(); ++currentSet) {
+ if (now - currentSet->getLastUpdate() > kActiveLayerTimeout) {
+ // Layer is inactive
+ if (isPartOfRun) {
+ runs.back().length += currentSet->getLayerCount();
+ } else {
+ // Runs can't start with a non-buffer layer
+ if (currentSet->getFirstLayer().getBuffer() == nullptr) {
+ ALOGV("[%s] Skipping initial non-buffer layer", __func__);
+ } else {
+ runs.emplace_back(currentSet, currentSet->getLayerCount());
+ isPartOfRun = true;
+ }
+ }
+ } else if (isPartOfRun) {
+ // Runs must be at least 2 sets long or there's nothing to combine
+ if (runs.back().start->getLayerCount() == runs.back().length) {
+ runs.pop_back();
+ } else {
+ // The prior run contained at least two sets. Currently, we'll
+ // only possibly merge a single run, so only keep track of a
+ // holePunchLayer if this is the first run.
+ if (runs.size() == 1) {
+ holePunchLayer = &(*currentSet);
+ }
+
+ // TODO(b/185114532: Break out of the loop? We may find more runs, but we
+ // won't do anything with them.
+ }
+
+ isPartOfRun = false;
+ }
+ }
+
+ // Check for at least 2 sets one more time in case the set includes the last layer
+ if (isPartOfRun && runs.back().start->getLayerCount() == runs.back().length) {
+ runs.pop_back();
+ }
+
+ ALOGV("[%s] Found %zu candidate runs", __func__, runs.size());
+
+ if (runs.empty()) {
+ return;
+ }
+
+ mNewCachedSet.emplace(*runs[0].start);
+ mNewCachedSet->setLastUpdate(now);
+ auto currentSet = runs[0].start;
+ while (mNewCachedSet->getLayerCount() < runs[0].length) {
+ ++currentSet;
+ mNewCachedSet->append(*currentSet);
+ }
+
+ if (mEnableHolePunch && holePunchLayer && holePunchLayer->requiresHolePunch()) {
+ // Add the pip layer to mNewCachedSet, but in a special way - it should
+ // replace the buffer with a clear round rect.
+ mNewCachedSet->addHolePunchLayerIfFeasible(*holePunchLayer,
+ runs[0].start == mLayers.cbegin());
+ }
+
+ // TODO(b/181192467): Actually compute new LayerState vector and corresponding hash for each run
+ mPredictor.getPredictedPlan({}, 0);
+
+ ++mCachedSetCreationCount;
+ mCachedSetCreationCost += mNewCachedSet->getCreationCost();
+ std::string setDump;
+ mNewCachedSet->dump(setDump);
+ ALOGV("[%s] Added new cached set:\n%s", __func__, setDump.c_str());
+}
+
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
new file mode 100644
index 0000000..8423a12
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2021 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 <compositionengine/impl/planner/LayerState.h>
+
+namespace {
+extern "C" const char* __attribute__((unused)) __asan_default_options() {
+ return "detect_container_overflow=0";
+}
+} // namespace
+
+namespace android::compositionengine::impl::planner {
+
+LayerState::LayerState(compositionengine::OutputLayer* layer)
+ : mOutputLayer(layer),
+ mColorTransform({[](auto layer) {
+ const auto state = layer->getLayerFE().getCompositionState();
+ return state->colorTransformIsIdentity ? mat4{}
+ : state->colorTransform;
+ },
+ [](const mat4& mat) {
+ using namespace std::string_literals;
+ std::vector<std::string> split =
+ base::Split(std::string(mat.asString().string()), "\n"s);
+ split.pop_back(); // Strip the last (empty) line
+ return split;
+ }}) {
+ update(layer);
+}
+
+Flags<LayerStateField> LayerState::update(compositionengine::OutputLayer* layer) {
+ ALOGE_IF(layer != mOutputLayer, "[%s] Expected mOutputLayer to never change", __func__);
+
+ Flags<LayerStateField> differences;
+
+ // Update the unique fields as well, since we have to set them at least
+ // once from the OutputLayer
+ differences |= mId.update(layer);
+ differences |= mName.update(layer);
+
+ for (StateInterface* field : getNonUniqueFields()) {
+ differences |= field->update(layer);
+ }
+
+ return differences;
+}
+
+size_t LayerState::getHash() const {
+ size_t hash = 0;
+ for (const StateInterface* field : getNonUniqueFields()) {
+ if (field->getField() == LayerStateField::Buffer) {
+ continue;
+ }
+ android::hashCombineSingleHashed(hash, field->getHash());
+ }
+
+ return hash;
+}
+
+Flags<LayerStateField> LayerState::getDifferingFields(const LayerState& other) const {
+ Flags<LayerStateField> differences;
+ auto myFields = getNonUniqueFields();
+ auto otherFields = other.getNonUniqueFields();
+ for (size_t i = 0; i < myFields.size(); ++i) {
+ if (myFields[i]->getField() == LayerStateField::Buffer) {
+ continue;
+ }
+
+ differences |= myFields[i]->getFieldIfDifferent(otherFields[i]);
+ }
+
+ return differences;
+}
+
+void LayerState::dump(std::string& result) const {
+ for (const StateInterface* field : getNonUniqueFields()) {
+ if (auto viewOpt = flag_name(field->getField()); viewOpt) {
+ base::StringAppendF(&result, " %16s: ", std::string(*viewOpt).c_str());
+ } else {
+ result.append("<UNKNOWN FIELD>:\n");
+ }
+
+ bool first = true;
+ for (const std::string& line : field->toStrings()) {
+ base::StringAppendF(&result, "%s%s\n", first ? "" : " ",
+ line.c_str());
+ first = false;
+ }
+ }
+ result.append("\n");
+}
+
+std::optional<std::string> LayerState::compare(const LayerState& other) const {
+ std::string result;
+
+ const auto& thisFields = getNonUniqueFields();
+ const auto& otherFields = other.getNonUniqueFields();
+ for (size_t f = 0; f < thisFields.size(); ++f) {
+ const auto& thisField = thisFields[f];
+ const auto& otherField = otherFields[f];
+ // Skip comparing buffers
+ if (thisField->getField() == LayerStateField::Buffer) {
+ continue;
+ }
+
+ if (thisField->equals(otherField)) {
+ continue;
+ }
+
+ if (auto viewOpt = flag_name(thisField->getField()); viewOpt) {
+ base::StringAppendF(&result, " %16s: ", std::string(*viewOpt).c_str());
+ } else {
+ result.append("<UNKNOWN FIELD>:\n");
+ }
+
+ const auto& thisStrings = thisField->toStrings();
+ const auto& otherStrings = otherField->toStrings();
+ bool first = true;
+ for (size_t line = 0; line < std::max(thisStrings.size(), otherStrings.size()); ++line) {
+ if (!first) {
+ result.append(" ");
+ }
+ first = false;
+
+ if (line < thisStrings.size()) {
+ base::StringAppendF(&result, "%-48.48s", thisStrings[line].c_str());
+ } else {
+ result.append(" ");
+ }
+
+ if (line < otherStrings.size()) {
+ base::StringAppendF(&result, "%-48.48s", otherStrings[line].c_str());
+ } else {
+ result.append(" ");
+ }
+ result.append("\n");
+ }
+ }
+
+ return result.empty() ? std::nullopt : std::make_optional(result);
+}
+
+bool operator==(const LayerState& lhs, const LayerState& rhs) {
+ return lhs.mId == rhs.mId && lhs.mName == rhs.mName && lhs.mDisplayFrame == rhs.mDisplayFrame &&
+ lhs.mSourceCrop == rhs.mSourceCrop && lhs.mBufferTransform == rhs.mBufferTransform &&
+ lhs.mBlendMode == rhs.mBlendMode && lhs.mAlpha == rhs.mAlpha &&
+ lhs.mLayerMetadata == rhs.mLayerMetadata && lhs.mVisibleRegion == rhs.mVisibleRegion &&
+ lhs.mOutputDataspace == rhs.mOutputDataspace && lhs.mPixelFormat == rhs.mPixelFormat &&
+ lhs.mColorTransform == rhs.mColorTransform &&
+ lhs.mCompositionType == rhs.mCompositionType &&
+ lhs.mSidebandStream == rhs.mSidebandStream && lhs.mBuffer == rhs.mBuffer &&
+ (lhs.mCompositionType.get() != hal::Composition::SOLID_COLOR ||
+ lhs.mSolidColor == rhs.mSolidColor);
+}
+
+NonBufferHash getNonBufferHash(const std::vector<const LayerState*>& layers) {
+ size_t hash = 0;
+ for (const auto layer : layers) {
+ android::hashCombineSingleHashed(hash, layer->getHash());
+ }
+
+ return hash;
+}
+
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
new file mode 100644
index 0000000..7d2bf06
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -0,0 +1,273 @@
+/*
+ * Copyright 2021 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.
+ */
+
+// #define LOG_NDEBUG 0
+
+#undef LOG_TAG
+#define LOG_TAG "Planner"
+
+#include <android-base/properties.h>
+#include <compositionengine/LayerFECompositionState.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <compositionengine/impl/planner/Planner.h>
+
+namespace android::compositionengine::impl::planner {
+
+Planner::Planner()
+ : mFlattener(mPredictor,
+ base::GetBoolProperty(std::string("debug.sf.enable_hole_punch_pip"), false)) {}
+
+void Planner::setDisplaySize(ui::Size size) {
+ mFlattener.setDisplaySize(size);
+}
+
+void Planner::plan(
+ compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers) {
+ std::unordered_set<LayerId> removedLayers;
+ removedLayers.reserve(mPreviousLayers.size());
+
+ std::transform(mPreviousLayers.begin(), mPreviousLayers.end(),
+ std::inserter(removedLayers, removedLayers.begin()),
+ [](const auto& layer) { return layer.first; });
+
+ std::vector<LayerId> currentLayerIds;
+ for (auto layer : layers) {
+ LayerId id = layer->getLayerFE().getSequence();
+ if (const auto layerEntry = mPreviousLayers.find(id); layerEntry != mPreviousLayers.end()) {
+ // Track changes from previous info
+ LayerState& state = layerEntry->second;
+ Flags<LayerStateField> differences = state.update(layer);
+ if (differences.get() == 0) {
+ state.incrementFramesSinceBufferUpdate();
+ } else {
+ ALOGV("Layer %s changed: %s", state.getName().c_str(),
+ differences.string().c_str());
+
+ if (differences.test(LayerStateField::Buffer)) {
+ state.resetFramesSinceBufferUpdate();
+ } else {
+ state.incrementFramesSinceBufferUpdate();
+ }
+ }
+ } else {
+ LayerState state(layer);
+ ALOGV("Added layer %s", state.getName().c_str());
+ mPreviousLayers.emplace(std::make_pair(id, std::move(state)));
+ }
+
+ currentLayerIds.emplace_back(id);
+
+ if (const auto found = removedLayers.find(id); found != removedLayers.end()) {
+ removedLayers.erase(found);
+ }
+ }
+
+ for (LayerId removedLayer : removedLayers) {
+ if (const auto layerEntry = mPreviousLayers.find(removedLayer);
+ layerEntry != mPreviousLayers.end()) {
+ const auto& [id, state] = *layerEntry;
+ ALOGV("Removed layer %s", state.getName().c_str());
+ mPreviousLayers.erase(removedLayer);
+ }
+ }
+
+ mCurrentLayers.clear();
+ mCurrentLayers.reserve(currentLayerIds.size());
+ std::transform(currentLayerIds.cbegin(), currentLayerIds.cend(),
+ std::back_inserter(mCurrentLayers), [this](LayerId id) {
+ LayerState* state = &mPreviousLayers.at(id);
+ state->getOutputLayer()->editState().overrideInfo = {};
+ return state;
+ });
+
+ const NonBufferHash hash = getNonBufferHash(mCurrentLayers);
+ mFlattenedHash =
+ mFlattener.flattenLayers(mCurrentLayers, hash, std::chrono::steady_clock::now());
+ const bool layersWereFlattened = hash != mFlattenedHash;
+ ALOGV("[%s] Initial hash %zx flattened hash %zx", __func__, hash, mFlattenedHash);
+
+ mPredictedPlan =
+ mPredictor.getPredictedPlan(layersWereFlattened ? std::vector<const LayerState*>()
+ : mCurrentLayers,
+ mFlattenedHash);
+ if (mPredictedPlan) {
+ ALOGV("[%s] Predicting plan %s", __func__, to_string(mPredictedPlan->plan).c_str());
+ } else {
+ ALOGV("[%s] No prediction found\n", __func__);
+ }
+}
+
+void Planner::reportFinalPlan(
+ compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers) {
+ Plan finalPlan;
+ const GraphicBuffer* currentOverrideBuffer = nullptr;
+ bool hasSkippedLayers = false;
+ for (auto layer : layers) {
+ if (!layer->getState().overrideInfo.buffer) {
+ continue;
+ }
+
+ const GraphicBuffer* overrideBuffer =
+ layer->getState().overrideInfo.buffer->getBuffer().get();
+ if (overrideBuffer != nullptr && overrideBuffer == currentOverrideBuffer) {
+ // Skip this layer since it is part of a previous cached set
+ hasSkippedLayers = true;
+ continue;
+ }
+
+ currentOverrideBuffer = overrideBuffer;
+
+ const bool forcedOrRequestedClient =
+ layer->getState().forceClientComposition || layer->requiresClientComposition();
+
+ finalPlan.addLayerType(
+ forcedOrRequestedClient
+ ? hardware::graphics::composer::hal::Composition::CLIENT
+ : layer->getLayerFE().getCompositionState()->compositionType);
+ }
+
+ mPredictor.recordResult(mPredictedPlan, mFlattenedHash, mCurrentLayers, hasSkippedLayers,
+ finalPlan);
+}
+
+void Planner::renderCachedSets(renderengine::RenderEngine& renderEngine,
+ const OutputCompositionState& outputState) {
+ mFlattener.renderCachedSets(renderEngine, outputState);
+}
+
+void Planner::dump(const Vector<String16>& args, std::string& result) {
+ if (args.size() > 1) {
+ const String8 command(args[1]);
+ if (command == "--compare" || command == "-c") {
+ if (args.size() < 4) {
+ base::StringAppendF(&result,
+ "Expected two layer stack hashes, e.g. '--planner %s "
+ "<left_hash> <right_hash>'\n",
+ command.string());
+ return;
+ }
+ if (args.size() > 4) {
+ base::StringAppendF(&result,
+ "Too many arguments found, expected '--planner %s <left_hash> "
+ "<right_hash>'\n",
+ command.string());
+ return;
+ }
+
+ const String8 leftHashString(args[2]);
+ size_t leftHash = 0;
+ int fieldsRead = sscanf(leftHashString.string(), "%zx", &leftHash);
+ if (fieldsRead != 1) {
+ base::StringAppendF(&result, "Failed to parse %s as a size_t\n",
+ leftHashString.string());
+ return;
+ }
+
+ const String8 rightHashString(args[3]);
+ size_t rightHash = 0;
+ fieldsRead = sscanf(rightHashString.string(), "%zx", &rightHash);
+ if (fieldsRead != 1) {
+ base::StringAppendF(&result, "Failed to parse %s as a size_t\n",
+ rightHashString.string());
+ return;
+ }
+
+ mPredictor.compareLayerStacks(leftHash, rightHash, result);
+ } else if (command == "--describe" || command == "-d") {
+ if (args.size() < 3) {
+ base::StringAppendF(&result,
+ "Expected a layer stack hash, e.g. '--planner %s <hash>'\n",
+ command.string());
+ return;
+ }
+ if (args.size() > 3) {
+ base::StringAppendF(&result,
+ "Too many arguments found, expected '--planner %s <hash>'\n",
+ command.string());
+ return;
+ }
+
+ const String8 hashString(args[2]);
+ size_t hash = 0;
+ const int fieldsRead = sscanf(hashString.string(), "%zx", &hash);
+ if (fieldsRead != 1) {
+ base::StringAppendF(&result, "Failed to parse %s as a size_t\n",
+ hashString.string());
+ return;
+ }
+
+ mPredictor.describeLayerStack(hash, result);
+ } else if (command == "--help" || command == "-h") {
+ dumpUsage(result);
+ } else if (command == "--similar" || command == "-s") {
+ if (args.size() < 3) {
+ base::StringAppendF(&result, "Expected a plan string, e.g. '--planner %s <plan>'\n",
+ command.string());
+ return;
+ }
+ if (args.size() > 3) {
+ base::StringAppendF(&result,
+ "Too many arguments found, expected '--planner %s <plan>'\n",
+ command.string());
+ return;
+ }
+
+ const String8 planString(args[2]);
+ std::optional<Plan> plan = Plan::fromString(std::string(planString.string()));
+ if (!plan) {
+ base::StringAppendF(&result, "Failed to parse %s as a Plan\n", planString.string());
+ return;
+ }
+
+ mPredictor.listSimilarStacks(*plan, result);
+ } else {
+ base::StringAppendF(&result, "Unknown command '%s'\n\n", command.string());
+ dumpUsage(result);
+ }
+ return;
+ }
+
+ // If there are no specific commands, dump the usual state
+
+ mFlattener.dump(result);
+ result.append("\n");
+
+ mPredictor.dump(result);
+}
+
+void Planner::dumpUsage(std::string& result) const {
+ result.append("Planner command line interface usage\n");
+ result.append(" dumpsys SurfaceFlinger --planner <command> [arguments]\n\n");
+
+ result.append("If run without a command, dumps current Planner state\n\n");
+
+ result.append("Commands:\n");
+
+ result.append("[--compare|-c] <left_hash> <right_hash>\n");
+ result.append(" Compares the predictions <left_hash> and <right_hash> by showing differences"
+ " in their example layer stacks\n");
+
+ result.append("[--describe|-d] <hash>\n");
+ result.append(" Prints the example layer stack and prediction statistics for <hash>\n");
+
+ result.append("[--help|-h]\n");
+ result.append(" Shows this message\n");
+
+ result.append("[--similar|-s] <plan>\n");
+ result.append(" Prints the example layer names for similar stacks matching <plan>\n");
+}
+
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
new file mode 100644
index 0000000..8226ef7
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
@@ -0,0 +1,481 @@
+/*
+ * Copyright 2021 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.
+ */
+
+// #define LOG_NDEBUG 0
+
+#undef LOG_TAG
+#define LOG_TAG "Planner"
+
+#include <compositionengine/impl/planner/Predictor.h>
+
+namespace android::compositionengine::impl::planner {
+
+std::optional<LayerStack::ApproximateMatch> LayerStack::getApproximateMatch(
+ const std::vector<const LayerState*>& other) const {
+ // Differing numbers of layers are never an approximate match
+ if (mLayers.size() != other.size()) {
+ return std::nullopt;
+ }
+
+ std::optional<ApproximateMatch> approximateMatch = {};
+ for (size_t i = 0; i < mLayers.size(); ++i) {
+ // Skip identical layers
+ if (mLayers[i].getHash() == other[i]->getHash()) {
+ continue;
+ }
+
+ // Skip layers where both are client-composited, since that doesn't change the
+ // composition plan
+ if (mLayers[i].getCompositionType() == hal::Composition::CLIENT &&
+ other[i]->getCompositionType() == hal::Composition::CLIENT) {
+ continue;
+ }
+
+ // If layers differ in composition type, their stacks are too different
+ if (mLayers[i].getCompositionType() != other[i]->getCompositionType()) {
+ return std::nullopt;
+ }
+
+ // If layers are not identical, but we already detected a prior approximate match for a
+ // previous layer, the LayerStacks differ by too much, so return nothing
+ if (approximateMatch) {
+ return std::nullopt;
+ }
+
+ Flags<LayerStateField> differingFields = mLayers[i].getDifferingFields(*other[i]);
+
+ // If we don't find an approximate match on this layer, then the LayerStacks differ
+ // by too much, so return nothing
+ const int differingFieldCount = __builtin_popcount(differingFields.get());
+ if (differingFieldCount <= kMaxDifferingFields) {
+ approximateMatch = ApproximateMatch{
+ .differingIndex = i,
+ .differingFields = differingFields,
+ };
+ } else {
+ return std::nullopt;
+ }
+ }
+
+ if (approximateMatch) {
+ return approximateMatch;
+ }
+
+ // If we make it through the layer-by-layer comparison without an approximate match,
+ // it means that all layers were either identical or had client-composited layers in common,
+ // which don't affect the composition strategy, so return a successful result with
+ // no differences.
+ return ApproximateMatch{
+ .differingIndex = 0,
+ .differingFields = {},
+ };
+}
+
+std::optional<Plan> Plan::fromString(const std::string& string) {
+ Plan plan;
+ for (char c : string) {
+ switch (c) {
+ case 'C':
+ plan.addLayerType(hal::Composition::CLIENT);
+ continue;
+ case 'U':
+ plan.addLayerType(hal::Composition::CURSOR);
+ continue;
+ case 'D':
+ plan.addLayerType(hal::Composition::DEVICE);
+ continue;
+ case 'I':
+ plan.addLayerType(hal::Composition::INVALID);
+ continue;
+ case 'B':
+ plan.addLayerType(hal::Composition::SIDEBAND);
+ continue;
+ case 'S':
+ plan.addLayerType(hal::Composition::SOLID_COLOR);
+ continue;
+ default:
+ return std::nullopt;
+ }
+ }
+ return plan;
+}
+
+std::string to_string(const Plan& plan) {
+ std::string result;
+ for (auto type : plan.mLayerTypes) {
+ switch (type) {
+ case hal::Composition::CLIENT:
+ result.append("C");
+ break;
+ case hal::Composition::CURSOR:
+ result.append("U");
+ break;
+ case hal::Composition::DEVICE:
+ result.append("D");
+ break;
+ case hal::Composition::INVALID:
+ result.append("I");
+ break;
+ case hal::Composition::SIDEBAND:
+ result.append("B");
+ break;
+ case hal::Composition::SOLID_COLOR:
+ result.append("S");
+ break;
+ }
+ }
+ return result;
+}
+
+void Prediction::dump(std::string& result) const {
+ result.append(to_string(mPlan));
+ result.append(" [Exact ");
+ mExactStats.dump(result);
+ result.append("] [Approximate ");
+ mApproximateStats.dump(result);
+ result.append("]");
+}
+
+std::optional<Predictor::PredictedPlan> Predictor::getPredictedPlan(
+ const std::vector<const LayerState*>& layers, NonBufferHash hash) const {
+ // First check for an exact match
+ if (std::optional<Plan> exactMatch = getExactMatch(hash); exactMatch) {
+ ALOGV("[%s] Found an exact match for %zx", __func__, hash);
+ return PredictedPlan{.hash = hash, .plan = *exactMatch, .type = Prediction::Type::Exact};
+ }
+
+ // If only a hash was passed in for a layer stack with a cached set, don't perform
+ // approximate matches and return early
+ if (layers.empty()) {
+ ALOGV("[%s] Only hash was passed, but no exact match was found", __func__);
+ return std::nullopt;
+ }
+
+ // Then check for approximate matches
+ if (std::optional<NonBufferHash> approximateMatch = getApproximateMatch(layers);
+ approximateMatch) {
+ ALOGV("[%s] Found an approximate match for %zx", __func__, *approximateMatch);
+ const Prediction& prediction = getPrediction(*approximateMatch);
+ return PredictedPlan{.hash = *approximateMatch,
+ .plan = prediction.getPlan(),
+ .type = Prediction::Type::Approximate};
+ }
+
+ return std::nullopt;
+}
+
+void Predictor::recordResult(std::optional<PredictedPlan> predictedPlan,
+ NonBufferHash flattenedHash,
+ const std::vector<const LayerState*>& layers, bool hasSkippedLayers,
+ Plan result) {
+ if (predictedPlan) {
+ recordPredictedResult(*predictedPlan, layers, std::move(result));
+ return;
+ }
+
+ ++mMissCount;
+
+ if (!hasSkippedLayers && findSimilarPrediction(layers, result)) {
+ return;
+ }
+
+ ALOGV("[%s] Adding novel candidate %zx", __func__, flattenedHash);
+ mCandidates.emplace_front(flattenedHash, Prediction(layers, result));
+ if (mCandidates.size() > MAX_CANDIDATES) {
+ mCandidates.pop_back();
+ }
+}
+
+void Predictor::dump(std::string& result) const {
+ result.append("Predictor state:\n");
+
+ const size_t hitCount = mExactHitCount + mApproximateHitCount;
+ const size_t totalAttempts = hitCount + mMissCount;
+ base::StringAppendF(&result, "Global non-skipped hit rate: %.2f%% (%zd/%zd)\n",
+ 100.0f * hitCount / totalAttempts, hitCount, totalAttempts);
+ base::StringAppendF(&result, " Exact hits: %zd\n", mExactHitCount);
+ base::StringAppendF(&result, " Approximate hits: %zd\n", mApproximateHitCount);
+ base::StringAppendF(&result, " Misses: %zd\n\n", mMissCount);
+
+ dumpPredictionsByFrequency(result);
+}
+
+void Predictor::compareLayerStacks(NonBufferHash leftHash, NonBufferHash rightHash,
+ std::string& result) const {
+ const auto& [leftPredictionEntry, rightPredictionEntry] =
+ std::make_tuple(mPredictions.find(leftHash), mPredictions.find(rightHash));
+ if (leftPredictionEntry == mPredictions.end()) {
+ base::StringAppendF(&result, "No prediction found for %zx\n", leftHash);
+ return;
+ }
+ if (rightPredictionEntry == mPredictions.end()) {
+ base::StringAppendF(&result, "No prediction found for %zx\n", rightHash);
+ return;
+ }
+
+ base::StringAppendF(&result,
+ "Comparing %-16zx %-16zx\n",
+ leftHash, rightHash);
+
+ const auto& [leftPrediction, rightPrediction] =
+ std::make_tuple(leftPredictionEntry->second, rightPredictionEntry->second);
+ const auto& [leftStack, rightStack] = std::make_tuple(leftPrediction.getExampleLayerStack(),
+ rightPrediction.getExampleLayerStack());
+ leftStack.compare(rightStack, result);
+}
+
+void Predictor::describeLayerStack(NonBufferHash hash, std::string& result) const {
+ base::StringAppendF(&result, "Describing %zx:\n\n", hash);
+
+ if (const auto predictionsEntry = mPredictions.find(hash);
+ predictionsEntry != mPredictions.cend()) {
+ const auto& [hash, prediction] = *predictionsEntry;
+
+ prediction.getExampleLayerStack().dump(result);
+
+ result.append("Prediction: ");
+ prediction.dump(result);
+ result.append("\n");
+ } else {
+ result.append("No predictions found\n");
+ }
+}
+
+void Predictor::listSimilarStacks(Plan plan, std::string& result) const {
+ base::StringAppendF(&result, "Similar stacks for plan %s:\n", to_string(plan).c_str());
+
+ if (const auto similarStacksEntry = mSimilarStacks.find(plan);
+ similarStacksEntry != mSimilarStacks.end()) {
+ const auto& [_, similarStacks] = *similarStacksEntry;
+ for (NonBufferHash hash : similarStacks) {
+ base::StringAppendF(&result, "\nPrediction hash %zx:\n", hash);
+ const Prediction& prediction = mPredictions.at(hash);
+ prediction.getExampleLayerStack().dumpLayerNames(result);
+ }
+ } else {
+ result.append("No similar stacks found\n");
+ }
+}
+
+const Prediction& Predictor::getPrediction(NonBufferHash hash) const {
+ if (const auto predictionEntry = mPredictions.find(hash);
+ predictionEntry != mPredictions.end()) {
+ const auto& [_, prediction] = *predictionEntry;
+ return prediction;
+ } else {
+ const auto candidateEntry = getCandidateEntryByHash(hash);
+ ALOGE_IF(candidateEntry == mCandidates.cend(),
+ "Hash should have been found in either predictions or candidates");
+ const auto& [_, prediction] = *candidateEntry;
+ return prediction;
+ }
+}
+
+Prediction& Predictor::getPrediction(NonBufferHash hash) {
+ return const_cast<Prediction&>(const_cast<const Predictor*>(this)->getPrediction(hash));
+}
+
+std::optional<Plan> Predictor::getExactMatch(NonBufferHash hash) const {
+ const Prediction* match = nullptr;
+ if (const auto predictionEntry = mPredictions.find(hash);
+ predictionEntry != mPredictions.end()) {
+ const auto& [hash, prediction] = *predictionEntry;
+ match = &prediction;
+ } else if (const auto candidateEntry = getCandidateEntryByHash(hash);
+ candidateEntry != mCandidates.cend()) {
+ match = &(candidateEntry->prediction);
+ }
+
+ if (match == nullptr) {
+ return std::nullopt;
+ }
+
+ if (match->getMissCount(Prediction::Type::Exact) != 0) {
+ ALOGV("[%s] Skipping exact match for %zx because of prior miss", __func__, hash);
+ return std::nullopt;
+ }
+
+ return match->getPlan();
+}
+
+std::optional<NonBufferHash> Predictor::getApproximateMatch(
+ const std::vector<const LayerState*>& layers) const {
+ const auto approximateStackMatches = [&](const ApproximateStack& approximateStack) {
+ const auto& exampleStack = mPredictions.at(approximateStack.hash).getExampleLayerStack();
+ if (const auto approximateMatchOpt = exampleStack.getApproximateMatch(layers);
+ approximateMatchOpt) {
+ return *approximateMatchOpt == approximateStack.match;
+ }
+ return false;
+ };
+
+ const auto candidateMatches = [&](const PromotionCandidate& candidate) {
+ ALOGV("[getApproximateMatch] checking against %zx", candidate.hash);
+ return candidate.prediction.getExampleLayerStack().getApproximateMatch(layers) !=
+ std::nullopt;
+ };
+
+ const Prediction* match = nullptr;
+ NonBufferHash hash;
+ if (const auto approximateStackIter =
+ std::find_if(mApproximateStacks.cbegin(), mApproximateStacks.cend(),
+ approximateStackMatches);
+ approximateStackIter != mApproximateStacks.cend()) {
+ match = &mPredictions.at(approximateStackIter->hash);
+ hash = approximateStackIter->hash;
+ } else if (const auto candidateEntry =
+ std::find_if(mCandidates.cbegin(), mCandidates.cend(), candidateMatches);
+ candidateEntry != mCandidates.cend()) {
+ match = &(candidateEntry->prediction);
+ hash = candidateEntry->hash;
+ }
+
+ if (match == nullptr) {
+ return std::nullopt;
+ }
+
+ if (match->getMissCount(Prediction::Type::Approximate) != 0) {
+ ALOGV("[%s] Skipping approximate match for %zx because of prior miss", __func__, hash);
+ return std::nullopt;
+ }
+
+ return hash;
+}
+
+void Predictor::promoteIfCandidate(NonBufferHash predictionHash) {
+ // Return if the candidate has already been promoted
+ if (mPredictions.count(predictionHash) != 0) {
+ return;
+ }
+
+ ALOGV("[%s] Promoting %zx from candidate to prediction", __func__, predictionHash);
+
+ auto candidateEntry = getCandidateEntryByHash(predictionHash);
+ ALOGE_IF(candidateEntry == mCandidates.end(), "Expected to find candidate");
+
+ mSimilarStacks[candidateEntry->prediction.getPlan()].push_back(predictionHash);
+ mPredictions.emplace(predictionHash, std::move(candidateEntry->prediction));
+ mCandidates.erase(candidateEntry);
+}
+
+void Predictor::recordPredictedResult(PredictedPlan predictedPlan,
+ const std::vector<const LayerState*>& layers, Plan result) {
+ Prediction& prediction = getPrediction(predictedPlan.hash);
+ if (prediction.getPlan() != result) {
+ ALOGV("[%s] %s prediction missed, expected %s, found %s", __func__,
+ to_string(predictedPlan.type).c_str(), to_string(prediction.getPlan()).c_str(),
+ to_string(result).c_str());
+ prediction.recordMiss(predictedPlan.type);
+ ++mMissCount;
+ return;
+ }
+
+ switch (predictedPlan.type) {
+ case Prediction::Type::Approximate:
+ ++mApproximateHitCount;
+ break;
+ case Prediction::Type::Exact:
+ ++mExactHitCount;
+ break;
+ default:
+ break;
+ }
+
+ ALOGV("[%s] %s prediction hit", __func__, to_string(predictedPlan.type).c_str());
+ ALOGV("[%s] Plan: %s", __func__, to_string(result).c_str());
+ prediction.recordHit(predictedPlan.type);
+
+ const auto stackMatchesHash = [hash = predictedPlan.hash](const ApproximateStack& stack) {
+ return stack.hash == hash;
+ };
+
+ if (predictedPlan.type == Prediction::Type::Approximate) {
+ // If this approximate match is not already in the list of approximate stacks, add it
+ if (std::find_if(mApproximateStacks.cbegin(), mApproximateStacks.cend(),
+ stackMatchesHash) == mApproximateStacks.cend()) {
+ ALOGV("[%s] Adding approximate match to list", __func__);
+ const auto approximateMatchOpt =
+ prediction.getExampleLayerStack().getApproximateMatch(layers);
+ ALOGE_IF(!approximateMatchOpt, "Expected an approximate match");
+ mApproximateStacks.emplace_back(predictedPlan.hash, *approximateMatchOpt);
+ }
+ }
+
+ promoteIfCandidate(predictedPlan.hash);
+}
+
+bool Predictor::findSimilarPrediction(const std::vector<const LayerState*>& layers, Plan result) {
+ const auto stacksEntry = mSimilarStacks.find(result);
+ if (stacksEntry == mSimilarStacks.end()) {
+ return false;
+ }
+
+ std::optional<ApproximateStack> bestMatch;
+ const auto& [plan, similarStacks] = *stacksEntry;
+ for (NonBufferHash hash : similarStacks) {
+ const Prediction& prediction = mPredictions.at(hash);
+ auto approximateMatch = prediction.getExampleLayerStack().getApproximateMatch(layers);
+ if (!approximateMatch) {
+ continue;
+ }
+
+ const int differingFieldCount = __builtin_popcount(approximateMatch->differingFields.get());
+ if (!bestMatch ||
+ differingFieldCount < __builtin_popcount(bestMatch->match.differingFields.get())) {
+ bestMatch = {hash, *approximateMatch};
+ }
+ }
+
+ if (!bestMatch) {
+ return false;
+ }
+
+ ALOGV("[%s] Adding %zx to approximate stacks", __func__, bestMatch->hash);
+
+ mApproximateStacks.emplace_back(*bestMatch);
+ return true;
+}
+
+void Predictor::dumpPredictionsByFrequency(std::string& result) const {
+ struct HashFrequency {
+ HashFrequency(NonBufferHash hash, size_t totalAttempts)
+ : hash(hash), totalAttempts(totalAttempts) {}
+
+ NonBufferHash hash;
+ size_t totalAttempts;
+ };
+
+ std::vector<HashFrequency> hashFrequencies;
+ for (const auto& [hash, prediction] : mPredictions) {
+ hashFrequencies.emplace_back(hash,
+ prediction.getHitCount(Prediction::Type::Total) +
+ prediction.getMissCount(Prediction::Type::Total));
+ }
+
+ std::sort(hashFrequencies.begin(), hashFrequencies.end(),
+ [](const HashFrequency& lhs, const HashFrequency& rhs) {
+ return lhs.totalAttempts > rhs.totalAttempts;
+ });
+
+ result.append("Predictions:\n");
+ for (const auto& [hash, totalAttempts] : hashFrequencies) {
+ base::StringAppendF(&result, " %016zx ", hash);
+ mPredictions.at(hash).dump(result);
+ result.append("\n");
+ }
+}
+
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index d5bf569..325361b 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -39,9 +39,6 @@
using ::testing::StrictMock;
struct CompositionEngineTest : public testing::Test {
- android::mock::HWComposer* mHwc = new StrictMock<android::mock::HWComposer>();
- renderengine::mock::RenderEngine* mRenderEngine =
- new StrictMock<renderengine::mock::RenderEngine>();
std::shared_ptr<TimeStats> mTimeStats;
impl::CompositionEngine mEngine;
@@ -58,15 +55,18 @@
}
TEST_F(CompositionEngineTest, canSetHWComposer) {
- mEngine.setHwComposer(std::unique_ptr<android::HWComposer>(mHwc));
+ android::mock::HWComposer* hwc = new StrictMock<android::mock::HWComposer>();
+ mEngine.setHwComposer(std::unique_ptr<android::HWComposer>(hwc));
- EXPECT_EQ(mHwc, &mEngine.getHwComposer());
+ EXPECT_EQ(hwc, &mEngine.getHwComposer());
}
TEST_F(CompositionEngineTest, canSetRenderEngine) {
- mEngine.setRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
+ renderengine::mock::RenderEngine* renderEngine =
+ new StrictMock<renderengine::mock::RenderEngine>();
+ mEngine.setRenderEngine(std::unique_ptr<renderengine::RenderEngine>(renderEngine));
- EXPECT_EQ(mRenderEngine, &mEngine.getRenderEngine());
+ EXPECT_EQ(renderEngine, &mEngine.getRenderEngine());
}
TEST_F(CompositionEngineTest, canSetTimeStats) {
@@ -130,10 +130,10 @@
struct CompositionEngineUpdateCursorAsyncTest : public CompositionEngineTest {
public:
struct Layer {
- Layer() { EXPECT_CALL(outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(layerFE)); }
+ Layer() { EXPECT_CALL(outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*layerFE)); }
StrictMock<mock::OutputLayer> outputLayer;
- StrictMock<mock::LayerFE> layerFE;
+ sp<StrictMock<mock::LayerFE>> layerFE = sp<StrictMock<mock::LayerFE>>::make();
LayerFECompositionState layerFEState;
};
@@ -175,21 +175,21 @@
{
InSequence seq;
EXPECT_CALL(mOutput2Layer1.outputLayer, isHardwareCursor()).WillRepeatedly(Return(true));
- EXPECT_CALL(mOutput2Layer1.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor));
+ EXPECT_CALL(*mOutput2Layer1.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor));
EXPECT_CALL(mOutput2Layer1.outputLayer, writeCursorPositionToHWC());
}
{
InSequence seq;
EXPECT_CALL(mOutput3Layer1.outputLayer, isHardwareCursor()).WillRepeatedly(Return(true));
- EXPECT_CALL(mOutput3Layer1.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor));
+ EXPECT_CALL(*mOutput3Layer1.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor));
EXPECT_CALL(mOutput3Layer1.outputLayer, writeCursorPositionToHWC());
}
{
InSequence seq;
EXPECT_CALL(mOutput3Layer2.outputLayer, isHardwareCursor()).WillRepeatedly(Return(true));
- EXPECT_CALL(mOutput3Layer2.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor));
+ EXPECT_CALL(*mOutput3Layer2.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor));
EXPECT_CALL(mOutput3Layer2.outputLayer, writeCursorPositionToHWC());
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 29fd4b9..8a83639 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wextra"
+
#include <cmath>
#include <compositionengine/DisplayColorProfileCreationArgs.h>
@@ -31,8 +35,8 @@
#include <compositionengine/mock/RenderSurface.h>
#include <gtest/gtest.h>
#include <renderengine/mock/RenderEngine.h>
-#include <ui/DisplayInfo.h>
#include <ui/Rect.h>
+#include <ui/StaticDisplayInfo.h>
#include "MockHWC2.h"
#include "MockHWComposer.h"
@@ -56,8 +60,9 @@
using testing::SetArgPointee;
using testing::StrictMock;
-constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42};
-constexpr DisplayId VIRTUAL_DISPLAY_ID = DisplayId{43};
+constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId{42};
+// TODO(b/160679868) Use VirtualDisplayId
+constexpr PhysicalDisplayId VIRTUAL_DISPLAY_ID = PhysicalDisplayId{43};
constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920;
constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080;
constexpr int32_t DEFAULT_LAYER_STACK = 123;
@@ -164,7 +169,7 @@
DisplayCreationArgs getDisplayCreationArgsForPhysicalHWCDisplay() {
return DisplayCreationArgsBuilder()
- .setPhysical({DEFAULT_DISPLAY_ID, DisplayConnectionType::Internal})
+ .setPhysical({DEFAULT_DISPLAY_ID, ui::DisplayConnectionType::Internal})
.setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
.setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
.setIsSecure(true)
@@ -176,6 +181,7 @@
DisplayCreationArgs getDisplayCreationArgsForNonHWCVirtualDisplay() {
return DisplayCreationArgsBuilder()
.setUseHwcVirtualDisplays(false)
+ .setGpuVirtualDisplayIdGenerator(mGpuDisplayIdGenerator)
.setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
.setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
.setIsSecure(false)
@@ -189,6 +195,7 @@
StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
StrictMock<mock::CompositionEngine> mCompositionEngine;
sp<mock::NativeWindow> mNativeWindow = new StrictMock<mock::NativeWindow>();
+ RandomDisplayIdGenerator<GpuVirtualDisplayId> mGpuDisplayIdGenerator;
};
struct PartialMockDisplayTestCommon : public DisplayTestCommon {
@@ -245,7 +252,7 @@
getDisplayCreationArgsForNonHWCVirtualDisplay());
EXPECT_FALSE(display->isSecure());
EXPECT_TRUE(display->isVirtual());
- EXPECT_EQ(std::nullopt, display->getId());
+ EXPECT_TRUE(GpuVirtualDisplayId::tryCast(display->getId()));
}
/*
@@ -258,7 +265,7 @@
mDisplay->setConfiguration(
DisplayCreationArgsBuilder()
.setUseHwcVirtualDisplays(true)
- .setPhysical({DEFAULT_DISPLAY_ID, DisplayConnectionType::Internal})
+ .setPhysical({DEFAULT_DISPLAY_ID, ui::DisplayConnectionType::Internal})
.setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH))
.setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
.setIsSecure(true)
@@ -279,7 +286,7 @@
mDisplay->setConfiguration(
DisplayCreationArgsBuilder()
.setUseHwcVirtualDisplays(true)
- .setPhysical({DEFAULT_DISPLAY_ID, DisplayConnectionType::External})
+ .setPhysical({DEFAULT_DISPLAY_ID, ui::DisplayConnectionType::External})
.setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH))
.setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
.setIsSecure(false)
@@ -332,6 +339,7 @@
mDisplay->setConfiguration(
DisplayCreationArgsBuilder()
.setUseHwcVirtualDisplays(true)
+ .setGpuVirtualDisplayIdGenerator(mGpuDisplayIdGenerator)
.setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH))
.setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
.setIsSecure(false)
@@ -340,7 +348,7 @@
.setName(getDisplayNameFromCurrentTest())
.build());
- EXPECT_EQ(std::nullopt, mDisplay->getId());
+ EXPECT_TRUE(GpuVirtualDisplayId::tryCast(mDisplay->getId()));
EXPECT_FALSE(mDisplay->isSecure());
EXPECT_TRUE(mDisplay->isVirtual());
EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
@@ -352,6 +360,7 @@
mDisplay->setConfiguration(
DisplayCreationArgsBuilder()
.setUseHwcVirtualDisplays(false)
+ .setGpuVirtualDisplayIdGenerator(mGpuDisplayIdGenerator)
.setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH))
.setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
.setIsSecure(false)
@@ -360,7 +369,7 @@
.setName(getDisplayNameFromCurrentTest())
.build());
- EXPECT_EQ(std::nullopt, mDisplay->getId());
+ EXPECT_TRUE(GpuVirtualDisplayId::tryCast(mDisplay->getId()));
EXPECT_FALSE(mDisplay->isSecure());
EXPECT_TRUE(mDisplay->isVirtual());
EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
@@ -375,16 +384,13 @@
using DisplayDisconnectTest = PartialMockDisplayTestCommon;
TEST_F(DisplayDisconnectTest, disconnectsDisplay) {
- // The first call to disconnect will disconnect the display with the HWC and
- // set mHwcId to -1.
- EXPECT_CALL(mHwComposer, disconnectDisplay(DEFAULT_DISPLAY_ID)).Times(1);
+ // The first call to disconnect will disconnect the display with the HWC.
+ EXPECT_CALL(mHwComposer, disconnectDisplay(HalDisplayId(DEFAULT_DISPLAY_ID))).Times(1);
mDisplay->disconnect();
- EXPECT_FALSE(mDisplay->getId());
// Subsequent calls will do nothing,
- EXPECT_CALL(mHwComposer, disconnectDisplay(DEFAULT_DISPLAY_ID)).Times(0);
+ EXPECT_CALL(mHwComposer, disconnectDisplay(HalDisplayId(DEFAULT_DISPLAY_ID))).Times(0);
mDisplay->disconnect();
- EXPECT_FALSE(mDisplay->getId());
}
/*
@@ -402,7 +408,8 @@
// Identity matrix sets an identity state value
const mat4 kIdentity;
- EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, kIdentity)).Times(1);
+ EXPECT_CALL(mHwComposer, setColorTransform(HalDisplayId(DEFAULT_DISPLAY_ID), kIdentity))
+ .Times(1);
refreshArgs.colorTransformMatrix = kIdentity;
mDisplay->setColorTransform(refreshArgs);
@@ -410,7 +417,8 @@
// Non-identity matrix sets a non-identity state value
const mat4 kNonIdentity = mat4() * 2;
- EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, kNonIdentity)).Times(1);
+ EXPECT_CALL(mHwComposer, setColorTransform(HalDisplayId(DEFAULT_DISPLAY_ID), kNonIdentity))
+ .Times(1);
refreshArgs.colorTransformMatrix = kNonIdentity;
mDisplay->setColorTransform(refreshArgs);
@@ -527,13 +535,14 @@
sp<mock::LayerFE> layerFE = new StrictMock<mock::LayerFE>();
StrictMock<HWC2::mock::Layer> hwcLayer;
- EXPECT_CALL(mHwComposer, createLayer(DEFAULT_DISPLAY_ID)).WillOnce(Return(&hwcLayer));
+ EXPECT_CALL(mHwComposer, createLayer(HalDisplayId(DEFAULT_DISPLAY_ID)))
+ .WillOnce(Return(&hwcLayer));
auto outputLayer = mDisplay->createOutputLayer(layerFE);
EXPECT_EQ(&hwcLayer, outputLayer->getHwcLayer());
- EXPECT_CALL(mHwComposer, destroyLayer(DEFAULT_DISPLAY_ID, &hwcLayer));
+ EXPECT_CALL(mHwComposer, destroyLayer(HalDisplayId(DEFAULT_DISPLAY_ID), &hwcLayer));
outputLayer.reset();
}
@@ -606,7 +615,7 @@
auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
std::shared_ptr<Display> nonHwcDisplay =
createPartialMockDisplay<Display>(mCompositionEngine, args);
- EXPECT_FALSE(nonHwcDisplay->getId());
+ EXPECT_TRUE(GpuVirtualDisplayId::tryCast(nonHwcDisplay->getId()));
nonHwcDisplay->chooseCompositionStrategy();
@@ -617,7 +626,8 @@
TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutOnHwcError) {
EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition()).WillOnce(Return(false));
- EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(DEFAULT_DISPLAY_ID, false, _))
+ EXPECT_CALL(mHwComposer,
+ getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), false, _))
.WillOnce(Return(INVALID_OPERATION));
mDisplay->chooseCompositionStrategy();
@@ -639,7 +649,7 @@
.InSequence(s)
.WillOnce(Return(false));
- EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(DEFAULT_DISPLAY_ID, true, _))
+ EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _))
.WillOnce(Return(NO_ERROR));
EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
@@ -669,7 +679,7 @@
.InSequence(s)
.WillOnce(Return(false));
- EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(DEFAULT_DISPLAY_ID, true, _))
+ EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _))
.WillOnce(DoAll(SetArgPointee<2>(changes), Return(NO_ERROR)));
EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(changes.changedTypes)).Times(1);
EXPECT_CALL(*mDisplay, applyDisplayRequests(changes.displayRequests)).Times(1);
@@ -699,7 +709,7 @@
TEST_F(DisplayGetSkipColorTransformTest, checksDisplayCapability) {
EXPECT_CALL(mHwComposer,
- hasDisplayCapability(DEFAULT_DISPLAY_ID,
+ hasDisplayCapability(HalDisplayId(DEFAULT_DISPLAY_ID),
hal::DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM))
.WillOnce(Return(true));
EXPECT_TRUE(mDisplay->getSkipColorTransform());
@@ -857,13 +867,16 @@
sp<Fence> layer1Fence = new Fence();
sp<Fence> layer2Fence = new Fence();
- EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(DEFAULT_DISPLAY_ID)).Times(1);
- EXPECT_CALL(mHwComposer, getPresentFence(DEFAULT_DISPLAY_ID)).WillOnce(Return(presentFence));
- EXPECT_CALL(mHwComposer, getLayerReleaseFence(DEFAULT_DISPLAY_ID, &mLayer1.hwc2Layer))
+ EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(HalDisplayId(DEFAULT_DISPLAY_ID))).Times(1);
+ EXPECT_CALL(mHwComposer, getPresentFence(HalDisplayId(DEFAULT_DISPLAY_ID)))
+ .WillOnce(Return(presentFence));
+ EXPECT_CALL(mHwComposer,
+ getLayerReleaseFence(HalDisplayId(DEFAULT_DISPLAY_ID), &mLayer1.hwc2Layer))
.WillOnce(Return(layer1Fence));
- EXPECT_CALL(mHwComposer, getLayerReleaseFence(DEFAULT_DISPLAY_ID, &mLayer2.hwc2Layer))
+ EXPECT_CALL(mHwComposer,
+ getLayerReleaseFence(HalDisplayId(DEFAULT_DISPLAY_ID), &mLayer2.hwc2Layer))
.WillOnce(Return(layer2Fence));
- EXPECT_CALL(mHwComposer, clearReleaseFences(DEFAULT_DISPLAY_ID)).Times(1);
+ EXPECT_CALL(mHwComposer, clearReleaseFences(HalDisplayId(DEFAULT_DISPLAY_ID))).Times(1);
auto result = mDisplay->presentAndGetFrameFences();
@@ -908,7 +921,7 @@
mDisplay->editState().isEnabled = true;
mDisplay->editState().usesClientComposition = false;
- mDisplay->editState().viewport = Rect(0, 0, 1, 1);
+ mDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
mDisplay->editState().dirtyRegion = Region::INVALID_REGION;
CompositionRefreshArgs refreshArgs;
@@ -929,7 +942,7 @@
nonHwcDisplay->editState().isEnabled = true;
nonHwcDisplay->editState().usesClientComposition = false;
- nonHwcDisplay->editState().viewport = Rect(0, 0, 1, 1);
+ nonHwcDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
nonHwcDisplay->editState().dirtyRegion = Region::INVALID_REGION;
CompositionRefreshArgs refreshArgs;
@@ -950,7 +963,7 @@
nonHwcDisplay->editState().isEnabled = true;
nonHwcDisplay->editState().usesClientComposition = false;
- nonHwcDisplay->editState().viewport = Rect(0, 0, 1, 1);
+ nonHwcDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
nonHwcDisplay->editState().dirtyRegion = Region(Rect(0, 0, 1, 1));
CompositionRefreshArgs refreshArgs;
@@ -971,7 +984,7 @@
nonHwcDisplay->editState().isEnabled = true;
nonHwcDisplay->editState().usesClientComposition = false;
- nonHwcDisplay->editState().viewport = Rect(0, 0, 1, 1);
+ nonHwcDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
nonHwcDisplay->editState().dirtyRegion = Region::INVALID_REGION;
CompositionRefreshArgs refreshArgs;
@@ -1005,7 +1018,7 @@
std::shared_ptr<Display> mDisplay = impl::createDisplayTemplated<
Display>(mCompositionEngine,
DisplayCreationArgsBuilder()
- .setPhysical({DEFAULT_DISPLAY_ID, DisplayConnectionType::Internal})
+ .setPhysical({DEFAULT_DISPLAY_ID, ui::DisplayConnectionType::Internal})
.setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
.setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
.setIsSecure(true)
@@ -1034,3 +1047,6 @@
} // namespace
} // namespace android::compositionengine
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
index 87911cc..9518659 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
@@ -27,12 +27,13 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
#include <ui/GraphicTypes.h>
#include "DisplayHardware/HWC2.h"
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
namespace android {
namespace HWC2 {
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 75a4fec..bac894a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -22,11 +22,12 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
#include "DisplayHardware/HWComposer.h"
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
namespace android {
namespace mock {
@@ -42,63 +43,65 @@
MOCK_CONST_METHOD3(getDisplayIdentificationData,
bool(hal::HWDisplayId, uint8_t*, DisplayIdentificationData*));
MOCK_CONST_METHOD1(hasCapability, bool(hal::Capability));
- MOCK_CONST_METHOD2(hasDisplayCapability, bool(DisplayId, hal::DisplayCapability));
+ MOCK_CONST_METHOD2(hasDisplayCapability, bool(HalDisplayId, hal::DisplayCapability));
MOCK_METHOD3(allocateVirtualDisplay,
std::optional<DisplayId>(uint32_t, uint32_t, ui::PixelFormat*));
- MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, DisplayId));
- MOCK_METHOD1(createLayer, HWC2::Layer*(DisplayId));
- MOCK_METHOD2(destroyLayer, void(DisplayId, HWC2::Layer*));
+ MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, PhysicalDisplayId));
+ MOCK_METHOD1(createLayer, HWC2::Layer*(HalDisplayId));
+ MOCK_METHOD2(destroyLayer, void(HalDisplayId, HWC2::Layer*));
MOCK_METHOD3(getDeviceCompositionChanges,
- status_t(DisplayId, bool,
+ status_t(HalDisplayId, bool,
std::optional<android::HWComposer::DeviceRequestedChanges>*));
MOCK_METHOD5(setClientTarget,
- status_t(DisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&,
+ status_t(HalDisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&,
ui::Dataspace));
- MOCK_METHOD1(presentAndGetReleaseFences, status_t(DisplayId));
- MOCK_METHOD2(setPowerMode, status_t(DisplayId, hal::PowerMode));
- MOCK_METHOD2(setActiveConfig, status_t(DisplayId, size_t));
- MOCK_METHOD2(setColorTransform, status_t(DisplayId, const mat4&));
- MOCK_METHOD1(disconnectDisplay, void(DisplayId));
+ MOCK_METHOD1(presentAndGetReleaseFences, status_t(HalDisplayId));
+ MOCK_METHOD2(setPowerMode, status_t(PhysicalDisplayId, hal::PowerMode));
+ MOCK_METHOD2(setActiveConfig, status_t(HalDisplayId, size_t));
+ MOCK_METHOD2(setColorTransform, status_t(HalDisplayId, const mat4&));
+ MOCK_METHOD1(disconnectDisplay, void(HalDisplayId));
MOCK_CONST_METHOD1(hasDeviceComposition, bool(const std::optional<DisplayId>&));
- MOCK_CONST_METHOD1(getPresentFence, sp<Fence>(DisplayId));
- MOCK_CONST_METHOD2(getLayerReleaseFence, sp<Fence>(DisplayId, HWC2::Layer*));
- MOCK_METHOD3(setOutputBuffer, status_t(DisplayId, const sp<Fence>&, const sp<GraphicBuffer>&));
- MOCK_METHOD1(clearReleaseFences, void(DisplayId));
- MOCK_METHOD2(getHdrCapabilities, status_t(DisplayId, HdrCapabilities*));
- MOCK_CONST_METHOD1(getSupportedPerFrameMetadata, int32_t(DisplayId));
- MOCK_CONST_METHOD2(getRenderIntents, std::vector<ui::RenderIntent>(DisplayId, ui::ColorMode));
- MOCK_METHOD2(getDataspaceSaturationMatrix, mat4(DisplayId, ui::Dataspace));
+ MOCK_CONST_METHOD1(getPresentFence, sp<Fence>(HalDisplayId));
+ MOCK_CONST_METHOD2(getLayerReleaseFence, sp<Fence>(HalDisplayId, HWC2::Layer*));
+ MOCK_METHOD3(setOutputBuffer,
+ status_t(HalVirtualDisplayId, const sp<Fence>&, const sp<GraphicBuffer>&));
+ MOCK_METHOD1(clearReleaseFences, void(HalDisplayId));
+ MOCK_METHOD2(getHdrCapabilities, status_t(HalDisplayId, HdrCapabilities*));
+ MOCK_CONST_METHOD1(getSupportedPerFrameMetadata, int32_t(HalDisplayId));
+ MOCK_CONST_METHOD2(getRenderIntents,
+ std::vector<ui::RenderIntent>(HalDisplayId, ui::ColorMode));
+ MOCK_METHOD2(getDataspaceSaturationMatrix, mat4(HalDisplayId, ui::Dataspace));
MOCK_METHOD4(getDisplayedContentSamplingAttributes,
- status_t(DisplayId, ui::PixelFormat*, ui::Dataspace*, uint8_t*));
- MOCK_METHOD4(setDisplayContentSamplingEnabled, status_t(DisplayId, bool, uint8_t, uint64_t));
+ status_t(HalDisplayId, ui::PixelFormat*, ui::Dataspace*, uint8_t*));
+ MOCK_METHOD4(setDisplayContentSamplingEnabled, status_t(HalDisplayId, bool, uint8_t, uint64_t));
MOCK_METHOD4(getDisplayedContentSample,
- status_t(DisplayId, uint64_t, uint64_t, DisplayedFrameStats*));
- MOCK_METHOD2(setDisplayBrightness, std::future<status_t>(DisplayId, float));
- MOCK_METHOD2(getDisplayBrightnessSupport, status_t(DisplayId, bool*));
+ status_t(HalDisplayId, uint64_t, uint64_t, DisplayedFrameStats*));
+ MOCK_METHOD2(setDisplayBrightness, std::future<status_t>(PhysicalDisplayId, float));
+ MOCK_METHOD2(getDisplayBrightnessSupport, status_t(PhysicalDisplayId, bool*));
MOCK_METHOD2(onHotplug,
std::optional<DisplayIdentificationInfo>(hal::HWDisplayId, hal::Connection));
+ MOCK_CONST_METHOD0(updatesDeviceProductInfoOnHotplugReconnect, bool());
MOCK_METHOD2(onVsync, bool(hal::HWDisplayId, int64_t));
- MOCK_METHOD2(setVsyncEnabled, void(DisplayId, hal::Vsync));
- MOCK_CONST_METHOD1(getRefreshTimestamp, nsecs_t(DisplayId));
- MOCK_CONST_METHOD1(isConnected, bool(DisplayId));
- MOCK_CONST_METHOD1(getConfigs,
- std::vector<std::shared_ptr<const HWC2::Display::Config>>(DisplayId));
- MOCK_CONST_METHOD1(getActiveConfig, std::shared_ptr<const HWC2::Display::Config>(DisplayId));
- MOCK_CONST_METHOD1(getActiveConfigIndex, int(DisplayId));
- MOCK_CONST_METHOD1(getColorModes, std::vector<ui::ColorMode>(DisplayId));
- MOCK_METHOD3(setActiveColorMode, status_t(DisplayId, ui::ColorMode, ui::RenderIntent));
+ MOCK_METHOD2(setVsyncEnabled, void(PhysicalDisplayId, hal::Vsync));
+ MOCK_CONST_METHOD1(isConnected, bool(PhysicalDisplayId));
+ MOCK_CONST_METHOD1(getModes, std::vector<HWComposer::HWCDisplayMode>(PhysicalDisplayId));
+ MOCK_CONST_METHOD1(getActiveMode, std::optional<hal::HWConfigId>(PhysicalDisplayId));
+ MOCK_CONST_METHOD1(getColorModes, std::vector<ui::ColorMode>(PhysicalDisplayId));
+ MOCK_METHOD3(setActiveColorMode, status_t(PhysicalDisplayId, ui::ColorMode, ui::RenderIntent));
MOCK_CONST_METHOD0(isUsingVrComposer, bool());
- MOCK_CONST_METHOD1(getDisplayConnectionType, DisplayConnectionType(DisplayId));
- MOCK_CONST_METHOD1(isVsyncPeriodSwitchSupported, bool(DisplayId));
- MOCK_CONST_METHOD1(getDisplayVsyncPeriod, nsecs_t(DisplayId));
- MOCK_METHOD4(setActiveConfigWithConstraints,
- status_t(DisplayId, size_t, const hal::VsyncPeriodChangeConstraints&,
+ MOCK_CONST_METHOD1(getDisplayConnectionType, ui::DisplayConnectionType(PhysicalDisplayId));
+ MOCK_CONST_METHOD1(isVsyncPeriodSwitchSupported, bool(PhysicalDisplayId));
+ MOCK_CONST_METHOD2(getDisplayVsyncPeriod, status_t(PhysicalDisplayId, nsecs_t*));
+ MOCK_METHOD4(setActiveModeWithConstraints,
+ status_t(PhysicalDisplayId, hal::HWConfigId,
+ const hal::VsyncPeriodChangeConstraints&,
hal::VsyncPeriodChangeTimeline*));
- MOCK_METHOD2(setAutoLowLatencyMode, status_t(DisplayId, bool));
- MOCK_METHOD2(getSupportedContentTypes, status_t(DisplayId, std::vector<hal::ContentType>*));
- MOCK_METHOD2(setContentType, status_t(DisplayId, hal::ContentType));
+ MOCK_METHOD2(setAutoLowLatencyMode, status_t(PhysicalDisplayId, bool));
+ MOCK_METHOD2(getSupportedContentTypes,
+ status_t(PhysicalDisplayId, std::vector<hal::ContentType>*));
+ MOCK_METHOD2(setContentType, status_t(PhysicalDisplayId, hal::ContentType));
MOCK_CONST_METHOD0(getSupportedLayerGenericMetadata,
const std::unordered_map<std::string, bool>&());
@@ -107,8 +110,8 @@
MOCK_CONST_METHOD1(getHwcDisplayId, std::optional<hal::HWDisplayId>(int32_t));
MOCK_CONST_METHOD0(getInternalHwcDisplayId, std::optional<hal::HWDisplayId>());
MOCK_CONST_METHOD0(getExternalHwcDisplayId, std::optional<hal::HWDisplayId>());
- MOCK_CONST_METHOD1(toPhysicalDisplayId, std::optional<DisplayId>(hal::HWDisplayId));
- MOCK_CONST_METHOD1(fromPhysicalDisplayId, std::optional<hal::HWDisplayId>(DisplayId));
+ MOCK_CONST_METHOD1(toPhysicalDisplayId, std::optional<PhysicalDisplayId>(hal::HWDisplayId));
+ MOCK_CONST_METHOD1(fromPhysicalDisplayId, std::optional<hal::HWDisplayId>(PhysicalDisplayId));
};
} // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index c32ef11..fb8ffce 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -21,7 +21,10 @@
#include <compositionengine/mock/LayerFE.h>
#include <compositionengine/mock/Output.h>
#include <gtest/gtest.h>
+#include <log/log.h>
+#include <renderengine/mock/RenderEngine.h>
+#include <ui/PixelFormat.h>
#include "MockHWC2.h"
#include "MockHWComposer.h"
#include "RegionMatcher.h"
@@ -55,6 +58,22 @@
return expected.r == arg.r && expected.g == arg.g && expected.b == arg.b && expected.a == arg.a;
}
+ui::Rotation toRotation(uint32_t rotationFlag) {
+ switch (rotationFlag) {
+ case ui::Transform::RotationFlags::ROT_0:
+ return ui::ROTATION_0;
+ case ui::Transform::RotationFlags::ROT_90:
+ return ui::ROTATION_90;
+ case ui::Transform::RotationFlags::ROT_180:
+ return ui::ROTATION_180;
+ case ui::Transform::RotationFlags::ROT_270:
+ return ui::ROTATION_270;
+ default:
+ LOG_FATAL("Unexpected rotation flag %d", rotationFlag);
+ return ui::Rotation(-1);
+ }
+}
+
struct OutputLayerTest : public testing::Test {
struct OutputLayer final : public impl::OutputLayer {
OutputLayer(const compositionengine::Output& output, sp<compositionengine::LayerFE> layerFE)
@@ -138,7 +157,7 @@
mLayerFEState.geomBufferSize = Rect{0, 0, 1920, 1080};
mLayerFEState.geomBufferTransform = TR_IDENT;
- mOutputState.viewport = Rect{0, 0, 1920, 1080};
+ mOutputState.layerStackSpace.content = Rect{0, 0, 1920, 1080};
}
FloatRect calculateOutputSourceCrop() {
@@ -209,7 +228,7 @@
mLayerFEState.geomBufferUsesDisplayInverseTransform = entry.bufferInvDisplay;
mLayerFEState.geomBufferTransform = entry.buffer;
- mOutputState.orientation = entry.display;
+ mOutputState.displaySpace.orientation = toRotation(entry.display);
EXPECT_THAT(calculateOutputSourceCrop(), entry.expected) << "entry " << i;
}
@@ -223,7 +242,7 @@
}
TEST_F(OutputLayerSourceCropTest, viewportAffectsCrop) {
- mOutputState.viewport = Rect{0, 0, 960, 540};
+ mOutputState.layerStackSpace.content = Rect{0, 0, 960, 540};
const FloatRect expected{0.f, 0.f, 960.f, 540.f};
EXPECT_THAT(calculateOutputSourceCrop(), expected);
@@ -245,7 +264,7 @@
mLayerFEState.geomCrop = Rect{0, 0, 1920, 1080};
mLayerFEState.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f};
- mOutputState.viewport = Rect{0, 0, 1920, 1080};
+ mOutputState.layerStackSpace.content = Rect{0, 0, 1920, 1080};
mOutputState.transform = ui::Transform{TR_IDENT};
}
@@ -293,7 +312,7 @@
}
TEST_F(OutputLayerDisplayFrameTest, viewportAffectsFrame) {
- mOutputState.viewport = Rect{0, 0, 960, 540};
+ mOutputState.layerStackSpace.content = Rect{0, 0, 960, 540};
const Rect expected{0, 0, 960, 540};
EXPECT_THAT(calculateOutputDisplayFrame(), expected);
}
@@ -358,7 +377,7 @@
mLayerFEState.geomLayerTransform.set(entry.layer, 1920, 1080);
mLayerFEState.geomBufferTransform = entry.buffer;
- mOutputState.orientation = entry.display;
+ mOutputState.displaySpace.orientation = toRotation(entry.display);
mOutputState.transform = ui::Transform{entry.display};
const auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(entry.display);
@@ -470,7 +489,7 @@
mLayerFEState.geomLayerTransform.set(entry.layer, 1920, 1080);
mLayerFEState.geomBufferTransform = entry.buffer;
- mOutputState.orientation = entry.display;
+ mOutputState.displaySpace.orientation = toRotation(entry.display);
mOutputState.transform = ui::Transform{entry.display};
const auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(entry.internal);
@@ -671,12 +690,16 @@
struct OutputLayerWriteStateToHWCTest : public OutputLayerTest {
static constexpr hal::Error kError = hal::Error::UNSUPPORTED;
static constexpr FloatRect kSourceCrop{11.f, 12.f, 13.f, 14.f};
- static constexpr uint32_t kZOrder = 21u;
static constexpr Hwc2::Transform kBufferTransform = static_cast<Hwc2::Transform>(31);
+ static constexpr Hwc2::Transform kOverrideBufferTransform = static_cast<Hwc2::Transform>(0);
static constexpr Hwc2::IComposerClient::BlendMode kBlendMode =
static_cast<Hwc2::IComposerClient::BlendMode>(41);
+ static constexpr Hwc2::IComposerClient::BlendMode kOverrideBlendMode =
+ Hwc2::IComposerClient::BlendMode::PREMULTIPLIED;
static constexpr float kAlpha = 51.f;
+ static constexpr float kOverrideAlpha = 1.f;
static constexpr ui::Dataspace kDataspace = static_cast<ui::Dataspace>(71);
+ static constexpr ui::Dataspace kOverrideDataspace = static_cast<ui::Dataspace>(72);
static constexpr int kSupportedPerFrameMetadata = 101;
static constexpr int kExpectedHwcSlot = 0;
static constexpr bool kLayerGenericMetadata1Mandatory = true;
@@ -684,13 +707,19 @@
static const half4 kColor;
static const Rect kDisplayFrame;
+ static const Rect kOverrideDisplayFrame;
+ static const FloatRect kOverrideSourceCrop;
static const Region kOutputSpaceVisibleRegion;
+ static const Region kOverrideVisibleRegion;
static const mat4 kColorTransform;
static const Region kSurfaceDamage;
+ static const Region kOverrideSurfaceDamage;
static const HdrMetadata kHdrMetadata;
static native_handle_t* kSidebandStreamHandle;
static const sp<GraphicBuffer> kBuffer;
+ static const sp<GraphicBuffer> kOverrideBuffer;
static const sp<Fence> kFence;
+ static const sp<Fence> kOverrideFence;
static const std::string kLayerGenericMetadata1Key;
static const std::vector<uint8_t> kLayerGenericMetadata1Value;
static const std::string kLayerGenericMetadata2Key;
@@ -702,7 +731,6 @@
outputLayerState.displayFrame = kDisplayFrame;
outputLayerState.sourceCrop = kSourceCrop;
- outputLayerState.z = kZOrder;
outputLayerState.bufferTransform = static_cast<Hwc2::Transform>(kBufferTransform);
outputLayerState.outputSpaceVisibleRegion = kOutputSpaceVisibleRegion;
outputLayerState.dataspace = kDataspace;
@@ -734,26 +762,46 @@
kLayerGenericMetadata2Value};
}
- void expectGeometryCommonCalls() {
- EXPECT_CALL(*mHwcLayer, setDisplayFrame(kDisplayFrame)).WillOnce(Return(kError));
- EXPECT_CALL(*mHwcLayer, setSourceCrop(kSourceCrop)).WillOnce(Return(kError));
- EXPECT_CALL(*mHwcLayer, setZOrder(kZOrder)).WillOnce(Return(kError));
- EXPECT_CALL(*mHwcLayer, setTransform(kBufferTransform)).WillOnce(Return(kError));
+ void includeOverrideInfo() {
+ auto& overrideInfo = mOutputLayer.editState().overrideInfo;
- EXPECT_CALL(*mHwcLayer, setBlendMode(kBlendMode)).WillOnce(Return(kError));
- EXPECT_CALL(*mHwcLayer, setPlaneAlpha(kAlpha)).WillOnce(Return(kError));
+ overrideInfo.buffer = std::make_shared<
+ renderengine::ExternalTexture>(kOverrideBuffer, mRenderEngine,
+ renderengine::ExternalTexture::Usage::READABLE |
+ renderengine::ExternalTexture::Usage::
+ WRITEABLE);
+ overrideInfo.acquireFence = kOverrideFence;
+ overrideInfo.displayFrame = kOverrideDisplayFrame;
+ overrideInfo.dataspace = kOverrideDataspace;
+ overrideInfo.damageRegion = kOverrideSurfaceDamage;
+ overrideInfo.visibleRegion = kOverrideVisibleRegion;
}
- void expectPerFrameCommonCalls(SimulateUnsupported unsupported = SimulateUnsupported::None) {
- EXPECT_CALL(*mHwcLayer, setVisibleRegion(RegionEq(kOutputSpaceVisibleRegion)))
- .WillOnce(Return(kError));
- EXPECT_CALL(*mHwcLayer, setDataspace(kDataspace)).WillOnce(Return(kError));
+ void expectGeometryCommonCalls(Rect displayFrame = kDisplayFrame,
+ FloatRect sourceCrop = kSourceCrop,
+ Hwc2::Transform bufferTransform = kBufferTransform,
+ Hwc2::IComposerClient::BlendMode blendMode = kBlendMode,
+ float alpha = kAlpha) {
+ EXPECT_CALL(*mHwcLayer, setDisplayFrame(displayFrame)).WillOnce(Return(kError));
+ EXPECT_CALL(*mHwcLayer, setSourceCrop(sourceCrop)).WillOnce(Return(kError));
+ EXPECT_CALL(*mHwcLayer, setZOrder(_)).WillOnce(Return(kError));
+ EXPECT_CALL(*mHwcLayer, setTransform(bufferTransform)).WillOnce(Return(kError));
+
+ EXPECT_CALL(*mHwcLayer, setBlendMode(blendMode)).WillOnce(Return(kError));
+ EXPECT_CALL(*mHwcLayer, setPlaneAlpha(alpha)).WillOnce(Return(kError));
+ }
+
+ void expectPerFrameCommonCalls(SimulateUnsupported unsupported = SimulateUnsupported::None,
+ ui::Dataspace dataspace = kDataspace,
+ const Region& visibleRegion = kOutputSpaceVisibleRegion,
+ const Region& surfaceDamage = kSurfaceDamage) {
+ EXPECT_CALL(*mHwcLayer, setVisibleRegion(RegionEq(visibleRegion))).WillOnce(Return(kError));
+ EXPECT_CALL(*mHwcLayer, setDataspace(dataspace)).WillOnce(Return(kError));
EXPECT_CALL(*mHwcLayer, setColorTransform(kColorTransform))
.WillOnce(Return(unsupported == SimulateUnsupported::ColorTransform
? hal::Error::UNSUPPORTED
: hal::Error::NONE));
- EXPECT_CALL(*mHwcLayer, setSurfaceDamage(RegionEq(kSurfaceDamage)))
- .WillOnce(Return(kError));
+ EXPECT_CALL(*mHwcLayer, setSurfaceDamage(RegionEq(surfaceDamage))).WillOnce(Return(kError));
}
void expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition compositionType) {
@@ -776,9 +824,10 @@
EXPECT_CALL(*mHwcLayer, setSidebandStream(kSidebandStreamHandle));
}
- void expectSetHdrMetadataAndBufferCalls() {
+ void expectSetHdrMetadataAndBufferCalls(sp<GraphicBuffer> buffer = kBuffer,
+ sp<Fence> fence = kFence) {
EXPECT_CALL(*mHwcLayer, setPerFrameMetadata(kSupportedPerFrameMetadata, kHdrMetadata));
- EXPECT_CALL(*mHwcLayer, setBuffer(kExpectedHwcSlot, kBuffer, kFence));
+ EXPECT_CALL(*mHwcLayer, setBuffer(kExpectedHwcSlot, buffer, fence));
}
void expectGenericLayerMetadataCalls() {
@@ -795,23 +844,33 @@
std::shared_ptr<HWC2::mock::Layer> mHwcLayer{std::make_shared<StrictMock<HWC2::mock::Layer>>()};
StrictMock<mock::DisplayColorProfile> mDisplayColorProfile;
+ renderengine::mock::RenderEngine mRenderEngine;
};
const half4 OutputLayerWriteStateToHWCTest::kColor{81.f / 255.f, 82.f / 255.f, 83.f / 255.f,
84.f / 255.f};
const Rect OutputLayerWriteStateToHWCTest::kDisplayFrame{1001, 1002, 1003, 10044};
+const Rect OutputLayerWriteStateToHWCTest::kOverrideDisplayFrame{1002, 1003, 1004, 20044};
+const FloatRect OutputLayerWriteStateToHWCTest::kOverrideSourceCrop{0.f, 0.f, 4.f, 5.f};
const Region OutputLayerWriteStateToHWCTest::kOutputSpaceVisibleRegion{
Rect{1005, 1006, 1007, 1008}};
+const Region OutputLayerWriteStateToHWCTest::kOverrideVisibleRegion{Rect{1006, 1007, 1008, 1009}};
const mat4 OutputLayerWriteStateToHWCTest::kColorTransform{
1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016,
1017, 1018, 1019, 1020, 1021, 1022, 1023, 1024,
};
const Region OutputLayerWriteStateToHWCTest::kSurfaceDamage{Rect{1025, 1026, 1027, 1028}};
+const Region OutputLayerWriteStateToHWCTest::kOverrideSurfaceDamage{Rect{1026, 1027, 1028, 1029}};
const HdrMetadata OutputLayerWriteStateToHWCTest::kHdrMetadata{{/* LightFlattenable */}, 1029};
native_handle_t* OutputLayerWriteStateToHWCTest::kSidebandStreamHandle =
reinterpret_cast<native_handle_t*>(1031);
const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kBuffer;
+const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kOverrideBuffer =
+ new GraphicBuffer(4, 5, PIXEL_FORMAT_RGBA_8888,
+ AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
+ AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN);
const sp<Fence> OutputLayerWriteStateToHWCTest::kFence;
+const sp<Fence> OutputLayerWriteStateToHWCTest::kOverrideFence = new Fence();
const std::string OutputLayerWriteStateToHWCTest::kLayerGenericMetadata1Key =
"com.example.metadata.1";
const std::vector<uint8_t> OutputLayerWriteStateToHWCTest::kLayerGenericMetadata1Value{{1, 2, 3}};
@@ -823,19 +882,22 @@
TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoFECompositionState) {
EXPECT_CALL(*mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
- mOutputLayer.writeStateToHWC(true);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCState) {
mOutputLayer.editState().hwc.reset();
- mOutputLayer.writeStateToHWC(true);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCLayer) {
mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc(nullptr);
- mOutputLayer.writeStateToHWC(true);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetAllState) {
@@ -843,8 +905,10 @@
expectPerFrameCommonCalls();
expectNoSetCompositionTypeCall();
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
- mOutputLayer.writeStateToHWC(true);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
TEST_F(OutputLayerTest, displayInstallOrientationBufferTransformSetTo90) {
@@ -853,7 +917,7 @@
// This test simulates a scenario where displayInstallOrientation is set to
// ROT_90. This only has an effect on the transform; orientation stays 0 (see
// DisplayDevice::setProjection).
- mOutputState.orientation = TR_IDENT;
+ mOutputState.displaySpace.orientation = ui::ROTATION_0;
mOutputState.transform = ui::Transform{TR_ROT_90};
// Buffers are pre-rotated based on the transform hint (ROT_90); their
// geomBufferTransform is set to the inverse transform.
@@ -866,6 +930,7 @@
mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
expectPerFrameCommonCalls();
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
// Setting the composition type should happen before setting the color. We
// check this in this test only by setting up an testing::InSeqeuence
@@ -874,7 +939,8 @@
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::SOLID_COLOR);
expectSetColorCall();
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSideband) {
@@ -884,7 +950,10 @@
expectSetSidebandHandleCall();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::SIDEBAND);
- mOutputLayer.writeStateToHWC(false);
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForCursor) {
@@ -894,7 +963,10 @@
expectSetHdrMetadataAndBufferCalls();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CURSOR);
- mOutputLayer.writeStateToHWC(false);
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForDevice) {
@@ -904,7 +976,10 @@
expectSetHdrMetadataAndBufferCalls();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
- mOutputLayer.writeStateToHWC(false);
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsNotSetIfUnchanged) {
@@ -917,7 +992,10 @@
expectSetColorCall();
expectNoSetCompositionTypeCall();
- mOutputLayer.writeStateToHWC(false);
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfColorTransformNotSupported) {
@@ -927,7 +1005,8 @@
expectSetColorCall();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfClientCompositionForced) {
@@ -939,7 +1018,8 @@
expectSetColorCall();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, allStateIncludesMetadataIfPresent) {
@@ -952,7 +1032,10 @@
expectGenericLayerMetadataCalls();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
- mOutputLayer.writeStateToHWC(true);
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, perFrameStateDoesNotIncludeMetadataIfPresent) {
@@ -963,7 +1046,27 @@
expectSetHdrMetadataAndBufferCalls();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
- mOutputLayer.writeStateToHWC(false);
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoIfPresent) {
+ mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ includeOverrideInfo();
+
+ expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
+ kOverrideBlendMode, kOverrideAlpha);
+ expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
+ kOverrideSurfaceDamage);
+ expectSetHdrMetadataAndBufferCalls(kOverrideBuffer, kOverrideFence);
+ expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
/*
@@ -983,7 +1086,7 @@
mLayerFEState.cursorFrame = kDefaultCursorFrame;
- mOutputState.viewport = kDefaultDisplayViewport;
+ mOutputState.layerStackSpace.content = kDefaultDisplayViewport;
mOutputState.transform = ui::Transform{kDefaultTransform};
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 8f76afa..7b71957 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#include <cmath>
-
#include <android-base/stringprintf.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/impl/Output.h>
@@ -31,9 +29,13 @@
#include <ui/Rect.h>
#include <ui/Region.h>
+#include <cmath>
+#include <cstdint>
+
#include "CallOrderStateMachineHelper.h"
#include "MockHWC2.h"
#include "RegionMatcher.h"
+#include "renderengine/ExternalTexture.h"
namespace android::compositionengine {
namespace {
@@ -96,6 +98,8 @@
EXPECT_CALL(*outputLayer, editState()).WillRepeatedly(ReturnRef(outputLayerState));
EXPECT_CALL(*layerFE, getCompositionState()).WillRepeatedly(Return(&layerFEState));
+ EXPECT_CALL(*layerFE, getSequence()).WillRepeatedly(Return(0));
+ EXPECT_CALL(*layerFE, getDebugName()).WillRepeatedly(Return("InjectedLayer"));
}
mock::OutputLayer* outputLayer = {new StrictMock<mock::OutputLayer>};
@@ -111,6 +115,8 @@
EXPECT_CALL(outputLayer, editState()).WillRepeatedly(ReturnRef(outputLayerState));
EXPECT_CALL(*layerFE, getCompositionState()).WillRepeatedly(Return(&layerFEState));
+ EXPECT_CALL(*layerFE, getSequence()).WillRepeatedly(Return(0));
+ EXPECT_CALL(*layerFE, getDebugName()).WillRepeatedly(Return("NonInjectedLayer"));
}
mock::OutputLayer outputLayer;
@@ -136,7 +142,7 @@
std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
mOutput->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
- mOutput->editState().bounds = kDefaultDisplaySize;
+ mOutput->editState().displaySpace.bounds = kDefaultDisplaySize;
}
void injectOutputLayer(InjectedLayer& layer) {
@@ -235,42 +241,120 @@
* Output::setProjection()
*/
-TEST_F(OutputTest, setProjectionTriviallyWorks) {
- const ui::Transform transform{ui::Transform::ROT_180};
- const int32_t orientation = 123;
- const Rect frame{1, 2, 3, 4};
- const Rect viewport{5, 6, 7, 8};
- const Rect sourceClip{9, 10, 11, 12};
- const Rect destinationClip{13, 14, 15, 16};
- const bool needsFiltering = true;
+TEST_F(OutputTest, setProjectionWorks) {
+ const Rect displayRect{0, 0, 1000, 2000};
+ mOutput->editState().displaySpace.bounds = displayRect;
+ mOutput->editState().framebufferSpace.bounds = displayRect;
- mOutput->setProjection(transform, orientation, frame, viewport, sourceClip, destinationClip,
- needsFiltering);
+ const ui::Rotation orientation = ui::ROTATION_90;
+ const Rect frame{50, 60, 100, 100};
+ const Rect viewport{10, 20, 30, 40};
- EXPECT_THAT(mOutput->getState().transform, transform);
- EXPECT_EQ(orientation, mOutput->getState().orientation);
- EXPECT_EQ(frame, mOutput->getState().frame);
- EXPECT_EQ(viewport, mOutput->getState().viewport);
- EXPECT_EQ(sourceClip, mOutput->getState().sourceClip);
- EXPECT_EQ(destinationClip, mOutput->getState().destinationClip);
- EXPECT_EQ(needsFiltering, mOutput->getState().needsFiltering);
+ mOutput->setProjection(orientation, viewport, frame);
+
+ EXPECT_EQ(orientation, mOutput->getState().displaySpace.orientation);
+ EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.content);
+ EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.content);
+
+ const auto state = mOutput->getState();
+ EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation);
+ EXPECT_EQ(viewport, state.layerStackSpace.content);
+ EXPECT_EQ(viewport, state.layerStackSpace.bounds);
+
+ EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation);
+ EXPECT_EQ(frame, state.orientedDisplaySpace.content);
+ EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.bounds);
+
+ EXPECT_EQ(displayRect, state.displaySpace.bounds);
+ EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.content);
+ EXPECT_EQ(orientation, state.displaySpace.orientation);
+
+ EXPECT_EQ(displayRect, state.framebufferSpace.bounds);
+ EXPECT_EQ(Rect(900, 50, 940, 100), state.framebufferSpace.content);
+ EXPECT_EQ(orientation, state.framebufferSpace.orientation);
+
+ EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content));
+
+ EXPECT_EQ(ui::Transform::ROT_90, mOutput->getTransformHint());
+}
+
+TEST_F(OutputTest, setProjectionWithSmallFramebufferWorks) {
+ const Rect displayRect{0, 0, 1000, 2000};
+ const Rect framebufferRect{0, 0, 500, 1000};
+ mOutput->editState().displaySpace.bounds = displayRect;
+ mOutput->editState().framebufferSpace.bounds = framebufferRect;
+
+ const ui::Rotation orientation = ui::ROTATION_90;
+ const Rect frame{50, 60, 100, 100};
+ const Rect viewport{10, 20, 30, 40};
+
+ mOutput->setProjection(orientation, viewport, frame);
+
+ EXPECT_EQ(orientation, mOutput->getState().displaySpace.orientation);
+ EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.content);
+ EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.content);
+
+ const auto state = mOutput->getState();
+ EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation);
+ EXPECT_EQ(viewport, state.layerStackSpace.content);
+ EXPECT_EQ(viewport, state.layerStackSpace.bounds);
+
+ EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation);
+ EXPECT_EQ(frame, state.orientedDisplaySpace.content);
+ EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.bounds);
+
+ EXPECT_EQ(displayRect, state.displaySpace.bounds);
+ EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.content);
+ EXPECT_EQ(orientation, state.displaySpace.orientation);
+
+ EXPECT_EQ(framebufferRect, state.framebufferSpace.bounds);
+ EXPECT_EQ(Rect(450, 25, 470, 50), state.framebufferSpace.content);
+ EXPECT_EQ(orientation, state.framebufferSpace.orientation);
+
+ EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content));
}
/*
- * Output::setBounds()
+ * Output::setDisplaySize()
*/
-TEST_F(OutputTest, setBoundsSetsSizeAndDirtiesEntireOutput) {
- const ui::Size displaySize{200, 400};
+TEST_F(OutputTest, setDisplaySpaceSizeUpdatesOutputStateAndDirtiesEntireOutput) {
+ mOutput->editState().layerStackSpace.content = Rect(0, 0, 2000, 1000);
+ mOutput->editState().layerStackSpace.bounds = Rect(0, 0, 2000, 1000);
+ mOutput->editState().orientedDisplaySpace.content = Rect(0, 0, 1800, 900);
+ mOutput->editState().orientedDisplaySpace.bounds = Rect(0, 0, 2000, 1000);
+ mOutput->editState().framebufferSpace.content = Rect(0, 0, 900, 1800);
+ mOutput->editState().framebufferSpace.bounds = Rect(0, 0, 1000, 2000);
+ mOutput->editState().framebufferSpace.orientation = ui::ROTATION_90;
+ mOutput->editState().displaySpace.content = Rect(0, 0, 900, 1800);
+ mOutput->editState().displaySpace.bounds = Rect(0, 0, 1000, 2000);
+ mOutput->editState().displaySpace.orientation = ui::ROTATION_90;
- EXPECT_CALL(*mRenderSurface, setDisplaySize(displaySize)).Times(1);
- EXPECT_CALL(*mRenderSurface, getSize()).WillOnce(ReturnRef(displaySize));
+ const ui::Size newDisplaySize{500, 1000};
- mOutput->setBounds(displaySize);
+ EXPECT_CALL(*mRenderSurface, setDisplaySize(newDisplaySize)).Times(1);
- EXPECT_EQ(Rect(displaySize), mOutput->getState().bounds);
+ mOutput->setDisplaySize(newDisplaySize);
- EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(Rect(displaySize))));
+ const auto state = mOutput->getState();
+
+ const Rect displayRect(newDisplaySize);
+ EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation);
+ EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.content);
+ EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.bounds);
+
+ EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation);
+ EXPECT_EQ(Rect(0, 0, 1000, 500), state.orientedDisplaySpace.bounds);
+
+ EXPECT_EQ(displayRect, state.displaySpace.bounds);
+ EXPECT_EQ(ui::ROTATION_90, state.displaySpace.orientation);
+
+ EXPECT_EQ(displayRect, state.framebufferSpace.bounds);
+ EXPECT_EQ(ui::ROTATION_90, state.framebufferSpace.orientation);
+
+ EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content));
+
+ EXPECT_THAT(state.dirtyRegion, RegionEq(Region(displayRect)));
}
/*
@@ -431,7 +515,7 @@
mOutput->setRenderSurface(std::unique_ptr<RenderSurface>(renderSurface));
- EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().bounds);
+ EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().framebufferSpace.bounds);
}
/*
@@ -440,7 +524,7 @@
TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingTrue) {
const Rect viewport{100, 200};
- mOutput->editState().viewport = viewport;
+ mOutput->editState().layerStackSpace.content = viewport;
mOutput->editState().dirtyRegion.set(50, 300);
{
@@ -452,7 +536,7 @@
TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingFalse) {
const Rect viewport{100, 200};
- mOutput->editState().viewport = viewport;
+ mOutput->editState().layerStackSpace.content = viewport;
mOutput->editState().dirtyRegion.set(50, 300);
{
@@ -673,7 +757,9 @@
mOutput->editState().isEnabled = true;
CompositionRefreshArgs args;
- mOutput->updateAndWriteCompositionState(args);
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+ mOutput->writeCompositionState(args);
}
TEST_F(OutputUpdateAndWriteCompositionStateTest, doesNothingIfOutputNotEnabled) {
@@ -688,7 +774,9 @@
injectOutputLayer(layer3);
CompositionRefreshArgs args;
- mOutput->updateAndWriteCompositionState(args);
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+ mOutput->writeCompositionState(args);
}
TEST_F(OutputUpdateAndWriteCompositionStateTest, updatesLayerContentForAllLayers) {
@@ -696,12 +784,19 @@
InjectedLayer layer2;
InjectedLayer layer3;
+ uint32_t z = 0;
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
- EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer1.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
- EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer2.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
- EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer3.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
injectOutputLayer(layer1);
injectOutputLayer(layer2);
@@ -713,7 +808,9 @@
args.updatingGeometryThisFrame = false;
args.devOptForceClientComposition = false;
args.internalDisplayRotationFlags = ui::Transform::ROT_180;
- mOutput->updateAndWriteCompositionState(args);
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+ mOutput->writeCompositionState(args);
}
TEST_F(OutputUpdateAndWriteCompositionStateTest, updatesLayerGeometryAndContentForAllLayers) {
@@ -721,12 +818,19 @@
InjectedLayer layer2;
InjectedLayer layer3;
+ uint32_t z = 0;
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
- EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(true));
+ EXPECT_CALL(*layer1.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
- EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(true));
+ EXPECT_CALL(*layer2.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
- EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(true));
+ EXPECT_CALL(*layer3.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
injectOutputLayer(layer1);
injectOutputLayer(layer2);
@@ -737,7 +841,9 @@
CompositionRefreshArgs args;
args.updatingGeometryThisFrame = true;
args.devOptForceClientComposition = false;
- mOutput->updateAndWriteCompositionState(args);
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+ mOutput->writeCompositionState(args);
}
TEST_F(OutputUpdateAndWriteCompositionStateTest, forcesClientCompositionForAllLayers) {
@@ -745,12 +851,19 @@
InjectedLayer layer2;
InjectedLayer layer3;
+ uint32_t z = 0;
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer1.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer2.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer3.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
injectOutputLayer(layer1);
injectOutputLayer(layer2);
@@ -761,7 +874,9 @@
CompositionRefreshArgs args;
args.updatingGeometryThisFrame = false;
args.devOptForceClientComposition = true;
- mOutput->updateAndWriteCompositionState(args);
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+ mOutput->writeCompositionState(args);
}
/*
@@ -799,6 +914,9 @@
mOutput.editState().usesDeviceComposition = true;
EXPECT_CALL(mOutput, chooseCompositionStrategy()).Times(1);
+ if (mOutput.plannerEnabled()) {
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+ }
EXPECT_CALL(*mRenderSurface, prepareFrame(false, true));
mOutput.prepareFrame();
@@ -860,7 +978,7 @@
OutputRebuildLayerStacksTest() {
mOutput.mState.isEnabled = true;
mOutput.mState.transform = kIdentityTransform;
- mOutput.mState.bounds = kOutputBounds;
+ mOutput.mState.displaySpace.bounds = kOutputBounds;
mRefreshArgs.updatingOutputGeometryThisFrame = true;
@@ -1039,11 +1157,6 @@
EXPECT_CALL(mOutput, finalizePendingOutputLayers());
mOutput.collectVisibleLayers(mRefreshArgs, mCoverageState);
-
- // Ensure all output layers have been assigned a simple/flattened z-order.
- EXPECT_EQ(0u, mLayer1.outputLayerState.z);
- EXPECT_EQ(1u, mLayer2.outputLayerState.z);
- EXPECT_EQ(2u, mLayer3.outputLayerState.z);
}
/*
@@ -1067,8 +1180,8 @@
EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
.WillRepeatedly(Return(&mLayer.outputLayer));
- mOutput.mState.bounds = Rect(0, 0, 200, 300);
- mOutput.mState.viewport = Rect(0, 0, 200, 300);
+ mOutput.mState.displaySpace.bounds = Rect(0, 0, 200, 300);
+ mOutput.mState.layerStackSpace.content = Rect(0, 0, 200, 300);
mOutput.mState.transform = ui::Transform(TR_IDENT, 200, 300);
mLayer.layerFEState.isVisible = true;
@@ -1148,7 +1261,7 @@
}
TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesNotSoEarlyOutifDrawRegionEmpty) {
- mOutput.mState.bounds = Rect(0, 0, 0, 0);
+ mOutput.mState.displaySpace.bounds = Rect(0, 0, 0, 0);
ensureOutputLayerIfVisible();
}
@@ -1345,7 +1458,7 @@
mLayer.layerFEState.contentDirty = true;
mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
- mOutput.mState.viewport = Rect(0, 0, 300, 200);
+ mOutput.mState.layerStackSpace.content = Rect(0, 0, 300, 200);
mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300);
EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
@@ -1371,7 +1484,7 @@
mLayer.layerFEState.contentDirty = true;
mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
- mOutput.mState.viewport = Rect(0, 0, 300, 200);
+ mOutput.mState.layerStackSpace.content = Rect(0, 0, 300, 200);
mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300);
EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
@@ -1543,14 +1656,17 @@
// Sets up the helper functions called by the function under test to use
// mock implementations.
MOCK_METHOD1(updateColorProfile, void(const compositionengine::CompositionRefreshArgs&));
- MOCK_METHOD1(updateAndWriteCompositionState,
+ MOCK_METHOD1(updateCompositionState,
void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD0(planComposition, void());
+ MOCK_METHOD1(writeCompositionState, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD0(beginFrame, void());
MOCK_METHOD0(prepareFrame, void());
MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD1(finishFrame, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD0(postFramebuffer, void());
+ MOCK_METHOD0(renderCachedSets, void());
};
StrictMock<OutputPartialMock> mOutput;
@@ -1561,13 +1677,16 @@
InSequence seq;
EXPECT_CALL(mOutput, updateColorProfile(Ref(args)));
- EXPECT_CALL(mOutput, updateAndWriteCompositionState(Ref(args)));
+ EXPECT_CALL(mOutput, updateCompositionState(Ref(args)));
+ EXPECT_CALL(mOutput, planComposition());
+ EXPECT_CALL(mOutput, writeCompositionState(Ref(args)));
EXPECT_CALL(mOutput, setColorTransform(Ref(args)));
EXPECT_CALL(mOutput, beginFrame());
EXPECT_CALL(mOutput, prepareFrame());
EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
EXPECT_CALL(mOutput, finishFrame(Ref(args)));
EXPECT_CALL(mOutput, postFramebuffer());
+ EXPECT_CALL(mOutput, renderCachedSets());
mOutput.present(args);
}
@@ -1587,12 +1706,12 @@
struct Layer {
Layer() {
- EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(mLayerFE));
- EXPECT_CALL(mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState));
+ EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*mLayerFE));
+ EXPECT_CALL(*mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState));
}
StrictMock<mock::OutputLayer> mOutputLayer;
- StrictMock<mock::LayerFE> mLayerFE;
+ sp<StrictMock<mock::LayerFE>> mLayerFE = sp<StrictMock<mock::LayerFE>>::make();
LayerFECompositionState mLayerFEState;
};
@@ -2602,12 +2721,12 @@
struct Layer {
Layer() {
- EXPECT_CALL(outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(layerFE));
+ EXPECT_CALL(outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*layerFE));
EXPECT_CALL(outputLayer, getHwcLayer()).WillRepeatedly(Return(&hwc2Layer));
}
StrictMock<mock::OutputLayer> outputLayer;
- StrictMock<mock::LayerFE> layerFE;
+ sp<StrictMock<mock::LayerFE>> layerFE = sp<StrictMock<mock::LayerFE>>::make();
StrictMock<HWC2::mock::Layer> hwc2Layer;
};
@@ -2683,11 +2802,11 @@
// are passed. This happens to work with the current implementation, but
// would not survive certain calls like Fence::merge() which would return a
// new instance.
- EXPECT_CALL(mLayer1.layerFE,
+ EXPECT_CALL(*mLayer1.layerFE,
onLayerDisplayed(Property(&sp<Fence>::get, Eq(layer1Fence.get()))));
- EXPECT_CALL(mLayer2.layerFE,
+ EXPECT_CALL(*mLayer2.layerFE,
onLayerDisplayed(Property(&sp<Fence>::get, Eq(layer2Fence.get()))));
- EXPECT_CALL(mLayer3.layerFE,
+ EXPECT_CALL(*mLayer3.layerFE,
onLayerDisplayed(Property(&sp<Fence>::get, Eq(layer3Fence.get()))));
mOutput.postFramebuffer();
@@ -2714,9 +2833,9 @@
// Fence::merge is called, and since none of the fences are actually valid,
// Fence::NO_FENCE is returned and passed to each onLayerDisplayed() call.
// This is the best we can do without creating a real kernel fence object.
- EXPECT_CALL(mLayer1.layerFE, onLayerDisplayed(Fence::NO_FENCE));
- EXPECT_CALL(mLayer2.layerFE, onLayerDisplayed(Fence::NO_FENCE));
- EXPECT_CALL(mLayer3.layerFE, onLayerDisplayed(Fence::NO_FENCE));
+ EXPECT_CALL(*mLayer1.layerFE, onLayerDisplayed(Fence::NO_FENCE));
+ EXPECT_CALL(*mLayer2.layerFE, onLayerDisplayed(Fence::NO_FENCE));
+ EXPECT_CALL(*mLayer3.layerFE, onLayerDisplayed(Fence::NO_FENCE));
mOutput.postFramebuffer();
}
@@ -2785,12 +2904,12 @@
mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
mOutput.cacheClientCompositionRequests(MAX_CLIENT_COMPOSITION_CACHE_SIZE);
- mOutput.mState.frame = kDefaultOutputFrame;
- mOutput.mState.viewport = kDefaultOutputViewport;
- mOutput.mState.sourceClip = kDefaultOutputSourceClip;
- mOutput.mState.destinationClip = kDefaultOutputDestinationClip;
- mOutput.mState.transform = ui::Transform{kDefaultOutputOrientation};
- mOutput.mState.orientation = kDefaultOutputOrientation;
+ mOutput.mState.orientedDisplaySpace.content = kDefaultOutputFrame;
+ mOutput.mState.layerStackSpace.content = kDefaultOutputViewport;
+ mOutput.mState.framebufferSpace.content = kDefaultOutputDestinationClip;
+ mOutput.mState.displaySpace.content = kDefaultOutputDestinationClip;
+ mOutput.mState.displaySpace.orientation = kDefaultOutputOrientation;
+ mOutput.mState.transform = ui::Transform{kDefaultOutputOrientationFlags};
mOutput.mState.dataspace = kDefaultOutputDataspace;
mOutput.mState.colorTransformMatrix = kDefaultColorTransformMat;
mOutput.mState.isSecure = false;
@@ -2825,7 +2944,9 @@
// Call this member function to start using the mini-DSL defined above.
[[nodiscard]] auto verify() { return ExecuteState::make(this); }
- static constexpr uint32_t kDefaultOutputOrientation = TR_IDENT;
+ static constexpr ui::Rotation kDefaultOutputOrientation = ui::ROTATION_0;
+ static constexpr uint32_t kDefaultOutputOrientationFlags =
+ ui::Transform::toRotationFlags(kDefaultOutputOrientation);
static constexpr ui::Dataspace kDefaultOutputDataspace = ui::Dataspace::UNKNOWN;
static constexpr ui::Dataspace kExpensiveOutputDataspace = ui::Dataspace::DISPLAY_P3;
static constexpr float kDefaultMaxLuminance = 0.9f;
@@ -2834,7 +2955,6 @@
static const Rect kDefaultOutputFrame;
static const Rect kDefaultOutputViewport;
- static const Rect kDefaultOutputSourceClip;
static const Rect kDefaultOutputDestinationClip;
static const mat4 kDefaultColorTransformMat;
@@ -2849,14 +2969,16 @@
mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
StrictMock<OutputPartialMock> mOutput;
- sp<GraphicBuffer> mOutputBuffer = new GraphicBuffer();
+ std::shared_ptr<renderengine::ExternalTexture> mOutputBuffer = std::make_shared<
+ renderengine::ExternalTexture>(new GraphicBuffer(), mRenderEngine,
+ renderengine::ExternalTexture::Usage::READABLE |
+ renderengine::ExternalTexture::Usage::WRITEABLE);
std::optional<base::unique_fd> mReadyFence;
};
const Rect OutputComposeSurfacesTest::kDefaultOutputFrame{1001, 1002, 1003, 1004};
const Rect OutputComposeSurfacesTest::kDefaultOutputViewport{1005, 1006, 1007, 1008};
-const Rect OutputComposeSurfacesTest::kDefaultOutputSourceClip{1009, 1010, 1011, 1012};
const Rect OutputComposeSurfacesTest::kDefaultOutputDestinationClip{1013, 1014, 1015, 1016};
const mat4 OutputComposeSurfacesTest::kDefaultColorTransformMat{mat4() * 0.5f};
const compositionengine::CompositionRefreshArgs OutputComposeSurfacesTest::kDefaultRefreshArgs;
@@ -2925,7 +3047,7 @@
.WillRepeatedly(Return());
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
- EXPECT_CALL(mRenderEngine, drawLayers(_, IsEmpty(), _, true, _, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, IsEmpty(), _, false, _, _))
.WillRepeatedly(Return(NO_ERROR));
verify().execute().expectAFenceWasReturned();
@@ -2952,6 +3074,36 @@
}));
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _))
+ .WillRepeatedly(Return(NO_ERROR));
+
+ verify().execute().expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest,
+ buildsAndRendersRequestListAndCachesFramebufferForInternalLayers) {
+ LayerFE::LayerSettings r1;
+ LayerFE::LayerSettings r2;
+
+ r1.geometry.boundaries = FloatRect{1, 2, 3, 4};
+ r2.geometry.boundaries = FloatRect{5, 6, 7, 8};
+ const constexpr uint32_t kInternalLayerStack = 1234;
+ mOutput.setLayerStackFilter(kInternalLayerStack, true);
+
+ EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+ .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1}));
+ EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+ .WillRepeatedly(
+ Invoke([&](const Region&,
+ std::vector<LayerFE::LayerSettings>& clientCompositionLayers) {
+ clientCompositionLayers.emplace_back(r2);
+ }));
+
+ EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _, _))
.WillRepeatedly(Return(NO_ERROR));
@@ -2976,7 +3128,7 @@
.WillRepeatedly(Return());
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
- EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _))
.Times(2)
.WillOnce(Return(NO_ERROR));
@@ -3005,7 +3157,7 @@
.WillRepeatedly(Return());
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
- EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _))
.WillOnce(Return(NO_ERROR));
EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false));
@@ -3033,11 +3185,14 @@
EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
.WillRepeatedly(Return());
- sp<GraphicBuffer> otherOutputBuffer = new GraphicBuffer();
+ const auto otherOutputBuffer = std::make_shared<
+ renderengine::ExternalTexture>(new GraphicBuffer(), mRenderEngine,
+ renderengine::ExternalTexture::Usage::READABLE |
+ renderengine::ExternalTexture::Usage::WRITEABLE);
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_))
.WillOnce(Return(mOutputBuffer))
.WillOnce(Return(otherOutputBuffer));
- EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _))
.WillRepeatedly(Return(NO_ERROR));
verify().execute().expectAFenceWasReturned();
@@ -3067,9 +3222,9 @@
.WillRepeatedly(Return());
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
- EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _))
.WillOnce(Return(NO_ERROR));
- EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r3)), _, true, _, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r3)), _, false, _, _))
.WillOnce(Return(NO_ERROR));
verify().execute().expectAFenceWasReturned();
@@ -3119,7 +3274,7 @@
struct ExpectDisplaySettingsState
: public CallOrderStateMachineHelper<TestType, ExpectDisplaySettingsState> {
auto thenExpectDisplaySettingsUsed(renderengine::DisplaySettings settings) {
- EXPECT_CALL(getInstance()->mRenderEngine, drawLayers(settings, _, _, true, _, _))
+ EXPECT_CALL(getInstance()->mRenderEngine, drawLayers(settings, _, _, false, _, _))
.WillOnce(Return(NO_ERROR));
return nextState<ExecuteState>();
}
@@ -3133,9 +3288,9 @@
verify().ifMixedCompositionIs(true)
.andIfUsesHdr(true)
.andIfSkipColorTransform(false)
- .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+ .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
- Region::INVALID_REGION, kDefaultOutputOrientation})
+ Region::INVALID_REGION, kDefaultOutputOrientationFlags})
.execute()
.expectAFenceWasReturned();
}
@@ -3144,9 +3299,9 @@
verify().ifMixedCompositionIs(true)
.andIfUsesHdr(false)
.andIfSkipColorTransform(false)
- .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+ .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
- Region::INVALID_REGION, kDefaultOutputOrientation})
+ Region::INVALID_REGION, kDefaultOutputOrientationFlags})
.execute()
.expectAFenceWasReturned();
}
@@ -3155,10 +3310,10 @@
verify().ifMixedCompositionIs(false)
.andIfUsesHdr(true)
.andIfSkipColorTransform(false)
- .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+ .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
kDefaultMaxLuminance, kDefaultOutputDataspace,
kDefaultColorTransformMat, Region::INVALID_REGION,
- kDefaultOutputOrientation})
+ kDefaultOutputOrientationFlags})
.execute()
.expectAFenceWasReturned();
}
@@ -3167,10 +3322,10 @@
verify().ifMixedCompositionIs(false)
.andIfUsesHdr(false)
.andIfSkipColorTransform(false)
- .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+ .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
kDefaultMaxLuminance, kDefaultOutputDataspace,
kDefaultColorTransformMat, Region::INVALID_REGION,
- kDefaultOutputOrientation})
+ kDefaultOutputOrientationFlags})
.execute()
.expectAFenceWasReturned();
}
@@ -3180,9 +3335,9 @@
verify().ifMixedCompositionIs(false)
.andIfUsesHdr(true)
.andIfSkipColorTransform(true)
- .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+ .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
- Region::INVALID_REGION, kDefaultOutputOrientation})
+ Region::INVALID_REGION, kDefaultOutputOrientationFlags})
.execute()
.expectAFenceWasReturned();
}
@@ -3190,12 +3345,12 @@
struct OutputComposeSurfacesTest_HandlesProtectedContent : public OutputComposeSurfacesTest {
struct Layer {
Layer() {
- EXPECT_CALL(mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState));
- EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(mLayerFE));
+ EXPECT_CALL(*mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState));
+ EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*mLayerFE));
}
StrictMock<mock::OutputLayer> mOutputLayer;
- StrictMock<mock::LayerFE> mLayerFE;
+ sp<StrictMock<mock::LayerFE>> mLayerFE = sp<StrictMock<mock::LayerFE>>::make();
LayerFECompositionState mLayerFEState;
};
@@ -3218,7 +3373,7 @@
EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
.WillRepeatedly(Return());
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _))
.WillRepeatedly(Return(NO_ERROR));
}
@@ -3271,7 +3426,7 @@
EXPECT_CALL(*mRenderSurface, setProtected(true));
// Must happen after setting the protected content state.
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)).WillOnce(Return(NO_ERROR));
mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
}
@@ -3341,7 +3496,7 @@
InSequence seq;
EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true));
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)).WillOnce(Return(NO_ERROR));
mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
}
@@ -3353,10 +3508,12 @@
mOutput.editState().isEnabled = true;
EXPECT_CALL(mLayer.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(mLayer.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(mLayer.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>{}));
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)).WillOnce(Return(NO_ERROR));
EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
.WillRepeatedly(Return(&mLayer.outputLayer));
@@ -3368,7 +3525,9 @@
TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur, IfBlursAreExpensive) {
mRefreshArgs.blursAreExpensive = true;
- mOutput.updateAndWriteCompositionState(mRefreshArgs);
+ mOutput.updateCompositionState(mRefreshArgs);
+ mOutput.planComposition();
+ mOutput.writeCompositionState(mRefreshArgs);
EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true));
mOutput.composeSurfaces(kDebugRegion, mRefreshArgs);
@@ -3376,7 +3535,9 @@
TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur, IfBlursAreNotExpensive) {
mRefreshArgs.blursAreExpensive = false;
- mOutput.updateAndWriteCompositionState(mRefreshArgs);
+ mOutput.updateCompositionState(mRefreshArgs);
+ mOutput.planComposition();
+ mOutput.writeCompositionState(mRefreshArgs);
EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true)).Times(0);
mOutput.composeSurfaces(kDebugRegion, mRefreshArgs);
@@ -3401,12 +3562,12 @@
Layer() {
EXPECT_CALL(mOutputLayer, getState()).WillRepeatedly(ReturnRef(mOutputLayerState));
EXPECT_CALL(mOutputLayer, editState()).WillRepeatedly(ReturnRef(mOutputLayerState));
- EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(mLayerFE));
- EXPECT_CALL(mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState));
+ EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*mLayerFE));
+ EXPECT_CALL(*mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState));
}
StrictMock<mock::OutputLayer> mOutputLayer;
- StrictMock<mock::LayerFE> mLayerFE;
+ sp<StrictMock<mock::LayerFE>> mLayerFE = sp<StrictMock<mock::LayerFE>>::make();
LayerFECompositionState mLayerFEState;
impl::OutputLayerCompositionState mOutputLayerState;
LayerFE::LayerSettings mLayerSettings;
@@ -3428,12 +3589,12 @@
struct GenerateClientCompositionRequestsTest_ThreeLayers
: public GenerateClientCompositionRequestsTest {
GenerateClientCompositionRequestsTest_ThreeLayers() {
- mOutput.mState.frame = kDisplayFrame;
- mOutput.mState.viewport = kDisplayViewport;
- mOutput.mState.sourceClip = kDisplaySourceClip;
- mOutput.mState.destinationClip = kDisplayDestinationClip;
- mOutput.mState.transform = ui::Transform{kDisplayOrientation};
- mOutput.mState.orientation = kDisplayOrientation;
+ mOutput.mState.orientedDisplaySpace.content = kDisplayFrame;
+ mOutput.mState.layerStackSpace.content = kDisplayViewport;
+ mOutput.mState.displaySpace.content = kDisplayDestinationClip;
+ mOutput.mState.transform =
+ ui::Transform{ui::Transform::toRotationFlags(kDisplayOrientation)};
+ mOutput.mState.displaySpace.orientation = kDisplayOrientation;
mOutput.mState.needsFiltering = false;
mOutput.mState.isSecure = false;
@@ -3457,12 +3618,11 @@
EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(mLayers.size()));
}
- static constexpr uint32_t kDisplayOrientation = TR_IDENT;
+ static constexpr ui::Rotation kDisplayOrientation = ui::ROTATION_0;
static constexpr ui::Dataspace kDisplayDataspace = ui::Dataspace::UNKNOWN;
static const Rect kDisplayFrame;
static const Rect kDisplayViewport;
- static const Rect kDisplaySourceClip;
static const Rect kDisplayDestinationClip;
std::array<Layer, 3> mLayers;
@@ -3470,7 +3630,6 @@
const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayFrame(0, 0, 100, 200);
const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayViewport(0, 0, 101, 201);
-const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplaySourceClip(0, 0, 102, 202);
const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayDestinationClip(0, 0, 103,
203);
@@ -3502,11 +3661,11 @@
LayerFE::LayerSettings mShadowSettings;
mShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f};
- EXPECT_CALL(mLayers[0].mLayerFE, prepareClientCompositionList(_))
+ EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(_))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
- EXPECT_CALL(mLayers[1].mLayerFE, prepareClientCompositionList(_))
+ EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(_))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[1].mLayerSettings})));
- EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(_))
+ EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(_))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>(
{mShadowSettings, mLayers[2].mLayerSettings})));
@@ -3540,7 +3699,7 @@
mLayers[1].mLayerFEState.isOpaque = true;
mLayers[2].mLayerFEState.isOpaque = true;
- EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(_))
+ EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(_))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings})));
Region accumClearRegion(Rect(10, 11, 12, 13));
@@ -3566,7 +3725,7 @@
mLayers[1].mLayerFEState.isOpaque = false;
mLayers[2].mLayerFEState.isOpaque = false;
- EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(_))
+ EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(_))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings})));
Region accumClearRegion(Rect(10, 11, 12, 13));
@@ -3601,7 +3760,6 @@
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3610,10 +3768,10 @@
kDisplayDataspace,
false /* realContentIsVisible */,
true /* clearContent */,
+ false /* disabledBlurs */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3622,6 +3780,7 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
+ false /* disabledBlurs */,
};
LayerFE::LayerSettings mBlackoutSettings = mLayers[1].mLayerSettings;
@@ -3630,9 +3789,9 @@
mBlackoutSettings.alpha = 0.f;
mBlackoutSettings.disableBlending = true;
- EXPECT_CALL(mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
+ EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>({mBlackoutSettings})));
- EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
+ EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings})));
auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
@@ -3657,7 +3816,6 @@
compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
Region(Rect(10, 10, 20, 20)),
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3666,10 +3824,10 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
+ false /* disabledBlurs */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(Rect(0, 0, 30, 30)),
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3678,10 +3836,10 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
+ false /* disabledBlurs */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(Rect(0, 0, 40, 201)),
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3690,13 +3848,14 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
+ false /* disabledBlurs */,
};
- EXPECT_CALL(mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
+ EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
- EXPECT_CALL(mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
+ EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
- EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
+ EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
static_cast<void>(
@@ -3713,7 +3872,6 @@
compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
true, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3722,10 +3880,10 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
+ false /* disabledBlurs */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3734,10 +3892,10 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
+ false /* disabledBlurs */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3746,13 +3904,14 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
+ false /* disabledBlurs */,
};
- EXPECT_CALL(mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
+ EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
- EXPECT_CALL(mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
+ EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
- EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
+ EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
static_cast<void>(
@@ -3769,7 +3928,6 @@
compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
true, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3778,11 +3936,11 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
+ false /* disabledBlurs */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
true, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3791,10 +3949,10 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
+ false /* disabledBlurs */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
true, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3803,13 +3961,14 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
+ false /* disabledBlurs */,
};
- EXPECT_CALL(mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
+ EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
- EXPECT_CALL(mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
+ EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
- EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
+ EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
static_cast<void>(
@@ -3825,7 +3984,6 @@
compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
false, /* needs filtering */
true, /* secure */
false, /* supports protected content */
@@ -3834,10 +3992,10 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
+ false /* disabledBlurs */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
false, /* needs filtering */
true, /* secure */
false, /* supports protected content */
@@ -3846,10 +4004,10 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
+ false /* disabledBlurs */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
false, /* needs filtering */
true, /* secure */
false, /* supports protected content */
@@ -3858,13 +4016,14 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
+ false /* disabledBlurs */,
};
- EXPECT_CALL(mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
+ EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
- EXPECT_CALL(mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
+ EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
- EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
+ EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
static_cast<void>(
@@ -3878,7 +4037,6 @@
compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
true, /* supports protected content */
@@ -3887,10 +4045,10 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
+ false /* disabledBlurs */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
true, /* supports protected content */
@@ -3899,10 +4057,10 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
+ false /* disabledBlurs */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
true, /* supports protected content */
@@ -3911,13 +4069,14 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
+ false /* disabledBlurs */,
};
- EXPECT_CALL(mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
+ EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
- EXPECT_CALL(mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
+ EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
- EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
+ EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
static_cast<void>(mOutput.generateClientCompositionRequests(true /* supportsProtectedContent */,
@@ -3930,13 +4089,20 @@
InjectedLayer layer2;
InjectedLayer layer3;
+ uint32_t z = 0;
// Layer requesting blur, or below, should request client composition.
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer1.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer2.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
- EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer3.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
layer2.layerFEState.backgroundBlurRadius = 10;
@@ -3949,7 +4115,46 @@
CompositionRefreshArgs args;
args.updatingGeometryThisFrame = false;
args.devOptForceClientComposition = false;
- mOutput->updateAndWriteCompositionState(args);
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+ mOutput->writeCompositionState(args);
+}
+
+TEST_F(OutputUpdateAndWriteCompositionStateTest, handlesBlurRegionRequests) {
+ InjectedLayer layer1;
+ InjectedLayer layer2;
+ InjectedLayer layer3;
+
+ uint32_t z = 0;
+ // Layer requesting blur, or below, should request client composition.
+ EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
+ EXPECT_CALL(*layer1.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
+ EXPECT_CALL(*layer2.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
+ EXPECT_CALL(*layer3.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+
+ BlurRegion region;
+ layer2.layerFEState.blurRegions.push_back(region);
+
+ injectOutputLayer(layer1);
+ injectOutputLayer(layer2);
+ injectOutputLayer(layer3);
+
+ mOutput->editState().isEnabled = true;
+
+ CompositionRefreshArgs args;
+ args.updatingGeometryThisFrame = false;
+ args.devOptForceClientComposition = false;
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+ mOutput->writeCompositionState(args);
}
TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenRequests) {
@@ -3959,17 +4164,15 @@
const Rect kPortraitFrame(0, 0, 1000, 2000);
const Rect kPortraitViewport(0, 0, 2000, 1000);
- const Rect kPortraitSourceClip(0, 0, 1000, 2000);
const Rect kPortraitDestinationClip(0, 0, 1000, 2000);
- const uint32_t kPortraitOrientation = TR_ROT_90;
+ const ui::Rotation kPortraitOrientation = ui::ROTATION_90;
constexpr ui::Dataspace kOutputDataspace = ui::Dataspace::DISPLAY_P3;
- mOutput.mState.frame = kPortraitFrame;
- mOutput.mState.viewport = kPortraitViewport;
- mOutput.mState.sourceClip = kPortraitSourceClip;
- mOutput.mState.destinationClip = kPortraitDestinationClip;
- mOutput.mState.transform = ui::Transform{kPortraitOrientation};
- mOutput.mState.orientation = kPortraitOrientation;
+ mOutput.mState.orientedDisplaySpace.content = kPortraitFrame;
+ mOutput.mState.layerStackSpace.content = kPortraitViewport;
+ mOutput.mState.displaySpace.content = kPortraitDestinationClip;
+ mOutput.mState.transform = ui::Transform{ui::Transform::toRotationFlags(kPortraitOrientation)};
+ mOutput.mState.displaySpace.orientation = kPortraitOrientation;
mOutput.mState.needsFiltering = false;
mOutput.mState.isSecure = true;
@@ -3996,7 +4199,6 @@
compositionengine::LayerFE::ClientCompositionTargetSettings leftLayerSettings{
Region(Rect(0, 0, 1000, 1000)),
- false, /* identity transform */
false, /* needs filtering */
true, /* secure */
true, /* supports protected content */
@@ -4005,16 +4207,16 @@
kOutputDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
+ false /* disabledBlurs */,
};
EXPECT_CALL(leftLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
EXPECT_CALL(leftLayer.mOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
- EXPECT_CALL(leftLayer.mLayerFE, prepareClientCompositionList(Eq(ByRef(leftLayerSettings))))
+ EXPECT_CALL(*leftLayer.mLayerFE, prepareClientCompositionList(Eq(ByRef(leftLayerSettings))))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>({leftLayer.mLayerSettings})));
compositionengine::LayerFE::ClientCompositionTargetSettings rightLayerSettings{
Region(Rect(1000, 0, 2000, 1000)),
- false, /* identity transform */
false, /* needs filtering */
true, /* secure */
true, /* supports protected content */
@@ -4023,11 +4225,12 @@
kOutputDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
+ false /* disabledBlurs */,
};
EXPECT_CALL(rightLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
EXPECT_CALL(rightLayer.mOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
- EXPECT_CALL(rightLayer.mLayerFE, prepareClientCompositionList(Eq(ByRef(rightLayerSettings))))
+ EXPECT_CALL(*rightLayer.mLayerFE, prepareClientCompositionList(Eq(ByRef(rightLayerSettings))))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>({rightLayer.mLayerSettings})));
constexpr bool supportsProtectedContent = true;
@@ -4048,7 +4251,6 @@
Region accumClearRegion(Rect(10, 11, 12, 13));
compositionengine::LayerFE::ClientCompositionTargetSettings layer2Settings{
Region(Rect(60, 40, 70, 80)).merge(Rect(40, 80, 70, 90)), /* visible region */
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -4057,6 +4259,7 @@
kDisplayDataspace,
false /* realContentIsVisible */,
false /* clearContent */,
+ false /* disabledBlurs */,
};
LayerFE::LayerSettings mShadowSettings;
@@ -4067,7 +4270,7 @@
EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
- EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2Settings))))
+ EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2Settings))))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>({mShadowSettings})));
auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
@@ -4094,7 +4297,6 @@
Region accumClearRegion(Rect(10, 11, 12, 13));
compositionengine::LayerFE::ClientCompositionTargetSettings layer2Settings{
Region(Rect(50, 40, 70, 80)).merge(Rect(40, 80, 70, 90)), /* visible region */
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -4103,11 +4305,12 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
+ false /* disabledBlurs */,
};
EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
- EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2Settings))))
+ EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2Settings))))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>(
{mShadowSettings, mLayers[2].mLayerSettings})));
diff --git a/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp
new file mode 100644
index 0000000..704f5a8
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2020 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 <compositionengine/ProjectionSpace.h>
+#include <gtest/gtest.h>
+
+namespace android::compositionengine {
+namespace {
+
+// Returns a rectangular strip along the side of the given rect pointed by
+// rotation. E.g. if rotation is ROTATION_0, the srip will be along the top
+// side, if it is ROTATION_90 the stip will be along the right wall.
+// One of the dimensions of the strip will be 0 and the other one will match
+// the length of the corresponding side.
+// The strip will be contained inside the given rect.
+Rect getSideStrip(const Rect& rect, ui::Rotation rotation) {
+ int width, height;
+ if (rotation == ui::ROTATION_90 || rotation == ui::ROTATION_270) {
+ width = 0;
+ height = rect.height();
+ } else {
+ width = rect.width();
+ height = 0;
+ }
+
+ if (rotation == ui::ROTATION_0 || rotation == ui::ROTATION_270) {
+ return Rect(rect.left, rect.top, rect.left + width, rect.top + height);
+ }
+
+ if (rotation == ui::ROTATION_90) {
+ return Rect(rect.right, rect.top, rect.right + width, rect.top + height);
+ }
+
+ if (rotation == ui::ROTATION_180) {
+ return Rect(rect.left, rect.bottom, rect.left + width, rect.bottom + height);
+ }
+
+ return Rect::INVALID_RECT;
+}
+} // namespace
+
+TEST(ProjectionSpaceTest, getTransformToSelfIsIdentity) {
+ ProjectionSpace space;
+ space.content = Rect(100, 200);
+ space.bounds = Rect(100, 200);
+
+ const ui::Transform identity;
+ for (int rotation = 0; rotation <= 3; rotation++) {
+ space.orientation = ui::Rotation(rotation);
+ EXPECT_EQ(space.getTransform(space), identity);
+ }
+}
+
+TEST(ProjectionSpaceTest, getTransformWhenTranslationIsNeeded) {
+ ProjectionSpace source;
+ source.content = Rect(10, 10, 20, 20);
+ source.bounds = Rect(100, 200);
+
+ ProjectionSpace dest;
+ dest.content = Rect(10, 20, 30, 20);
+ dest.bounds = source.bounds;
+
+ const auto transform = source.getTransform(dest);
+ EXPECT_EQ(transform.transform(source.content), dest.content);
+}
+
+TEST(ProjectionSpaceTest, getTransformWhenScaleIsNeeded) {
+ ProjectionSpace source;
+ source.content = Rect(0, 0, 20, 20);
+ source.bounds = Rect(100, 200);
+
+ ProjectionSpace dest;
+ dest.content = Rect(0, 0, 40, 30);
+ dest.bounds = source.bounds;
+
+ const auto transform = source.getTransform(dest);
+ EXPECT_EQ(transform.transform(source.content), dest.content);
+}
+
+TEST(ProjectionSpaceTest, getSideStripTest) {
+ const Rect rect(10, 20, 40, 100);
+ EXPECT_EQ(getSideStrip(rect, ui::ROTATION_0), Rect(10, 20, 40, 20));
+ EXPECT_EQ(getSideStrip(rect, ui::ROTATION_90), Rect(40, 20, 40, 100));
+ EXPECT_EQ(getSideStrip(rect, ui::ROTATION_180), Rect(10, 100, 40, 100));
+ EXPECT_EQ(getSideStrip(rect, ui::ROTATION_270), Rect(10, 20, 10, 100));
+}
+
+void testTransform(const ProjectionSpace& source, const ProjectionSpace& dest) {
+ const auto transform = source.getTransform(dest);
+ EXPECT_EQ(transform.transform(source.content), dest.content)
+ << "Source content doesn't map to dest content when projecting " << to_string(source)
+ << " onto " << to_string(dest);
+
+ // We take a strip at the top (according to the orientation) of each
+ // content rect and verify that transform maps between them. This way we
+ // verify that the transform is rotating properly.
+ // In the following example the strip is marked with asterisks:
+ //
+ // ******* +-------*
+ // | | | *
+ // | | | *
+ // +-----+ +-------*
+ // source(ROTATION_0) dest (ROTATION_90)
+ const auto sourceStrip = getSideStrip(source.content, source.orientation);
+ const auto destStrip = getSideStrip(dest.content, dest.orientation);
+ ASSERT_NE(sourceStrip, Rect::INVALID_RECT);
+ ASSERT_NE(destStrip, Rect::INVALID_RECT);
+ const auto mappedStrip = transform.transform(sourceStrip);
+ EXPECT_EQ(mappedStrip, destStrip)
+ << to_string(sourceStrip) << " maps to " << to_string(mappedStrip) << " instead of "
+ << to_string(destStrip) << " when projecting " << to_string(source) << " onto "
+ << to_string(dest);
+}
+
+TEST(ProjectionSpaceTest, getTransformWithOrienations) {
+ ProjectionSpace source;
+ source.bounds = Rect(12, 13, 678, 789);
+ source.content = Rect(40, 50, 234, 343);
+ ProjectionSpace dest;
+ dest.bounds = Rect(17, 18, 879, 564);
+ dest.content = Rect(43, 52, 432, 213);
+
+ for (int sourceRot = 0; sourceRot <= 3; sourceRot++) {
+ source.orientation = ui::Rotation(sourceRot);
+ for (int destRot = 0; destRot <= 3; destRot++) {
+ dest.orientation = ui::Rotation(destRot);
+ testTransform(source, dest);
+ }
+ }
+}
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
index fd47e45..9aeb290 100644
--- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -14,6 +14,12 @@
* limitations under the License.
*/
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#include "renderengine/ExternalTexture.h"
+#include "ui/GraphicBuffer.h"
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wextra"
+
#include <cstdarg>
#include <cstdint>
@@ -33,7 +39,7 @@
constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920;
constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080;
-constexpr std::optional<DisplayId> DEFAULT_DISPLAY_ID = std::make_optional(DisplayId{123u});
+constexpr DisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId(123u);
const std::string DEFAULT_DISPLAY_NAME = "Mock Display";
using testing::_;
@@ -48,7 +54,7 @@
class RenderSurfaceTest : public testing::Test {
public:
RenderSurfaceTest() {
- EXPECT_CALL(mDisplay, getId()).WillRepeatedly(ReturnRef(DEFAULT_DISPLAY_ID));
+ EXPECT_CALL(mDisplay, getId()).WillRepeatedly(Return(DEFAULT_DISPLAY_ID));
EXPECT_CALL(mDisplay, getName()).WillRepeatedly(ReturnRef(DEFAULT_DISPLAY_NAME));
EXPECT_CALL(mCompositionEngine, getRenderEngine).WillRepeatedly(ReturnRef(mRenderEngine));
EXPECT_CALL(*mNativeWindow, disconnect(NATIVE_WINDOW_API_EGL))
@@ -82,7 +88,8 @@
EXPECT_CALL(*mNativeWindow, connect(NATIVE_WINDOW_API_EGL)).WillOnce(Return(NO_ERROR));
EXPECT_CALL(*mNativeWindow, setBuffersFormat(HAL_PIXEL_FORMAT_RGBA_8888))
.WillOnce(Return(NO_ERROR));
- EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE))
+ .WillOnce(Return(NO_ERROR));
mSurface.initialize();
}
@@ -114,9 +121,10 @@
*/
TEST_F(RenderSurfaceTest, setDisplaySizeAppliesChange) {
- EXPECT_CALL(*mDisplaySurface, resizeBuffers(640, 480)).Times(1);
+ const ui::Size size(640, 480);
+ EXPECT_CALL(*mDisplaySurface, resizeBuffers(size)).Times(1);
- mSurface.setDisplaySize(ui::Size(640, 480));
+ mSurface.setDisplaySize(size);
}
/*
@@ -136,7 +144,9 @@
TEST_F(RenderSurfaceTest, setProtectedTrueEnablesProtection) {
EXPECT_FALSE(mSurface.isProtected());
- EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_PROTECTED))
+ EXPECT_CALL(*mNativeWindow,
+ setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE |
+ GRALLOC_USAGE_PROTECTED))
.WillOnce(Return(NO_ERROR));
mSurface.setProtected(true);
@@ -145,7 +155,8 @@
TEST_F(RenderSurfaceTest, setProtectedFalseDisablesProtection) {
EXPECT_FALSE(mSurface.isProtected());
- EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE))
+ .WillOnce(Return(NO_ERROR));
mSurface.setProtected(false);
EXPECT_FALSE(mSurface.isProtected());
@@ -153,9 +164,12 @@
TEST_F(RenderSurfaceTest, setProtectedEnableAndDisable) {
EXPECT_FALSE(mSurface.isProtected());
- EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_PROTECTED))
+ EXPECT_CALL(*mNativeWindow,
+ setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE |
+ GRALLOC_USAGE_PROTECTED))
.WillOnce(Return(NO_ERROR));
- EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE))
+ .WillOnce(Return(NO_ERROR));
mSurface.setProtected(true);
EXPECT_TRUE(mSurface.isProtected());
@@ -165,7 +179,9 @@
TEST_F(RenderSurfaceTest, setProtectedEnableWithError) {
EXPECT_FALSE(mSurface.isProtected());
- EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_PROTECTED))
+ EXPECT_CALL(*mNativeWindow,
+ setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE |
+ GRALLOC_USAGE_PROTECTED))
.WillOnce(Return(INVALID_OPERATION));
mSurface.setProtected(true);
EXPECT_FALSE(mSurface.isProtected());
@@ -225,9 +241,9 @@
DoAll(SetArgPointee<0>(buffer.get()), SetArgPointee<1>(-1), Return(NO_ERROR)));
base::unique_fd fence;
- EXPECT_EQ(buffer.get(), mSurface.dequeueBuffer(&fence).get());
+ EXPECT_EQ(buffer.get(), mSurface.dequeueBuffer(&fence)->getBuffer().get());
- EXPECT_EQ(buffer.get(), mSurface.mutableGraphicBufferForTest().get());
+ EXPECT_EQ(buffer.get(), mSurface.mutableTextureForTest()->getBuffer().get());
}
/*
@@ -235,8 +251,11 @@
*/
TEST_F(RenderSurfaceTest, queueBufferHandlesNoClientComposition) {
- sp<GraphicBuffer> buffer = new GraphicBuffer();
- mSurface.mutableGraphicBufferForTest() = buffer;
+ const auto buffer = std::make_shared<
+ renderengine::ExternalTexture>(new GraphicBuffer(), mRenderEngine,
+ renderengine::ExternalTexture::Usage::READABLE |
+ renderengine::ExternalTexture::Usage::WRITEABLE);
+ mSurface.mutableTextureForTest() = buffer;
impl::OutputCompositionState state;
state.usesClientComposition = false;
@@ -247,43 +266,45 @@
mSurface.queueBuffer(base::unique_fd());
- EXPECT_EQ(buffer.get(), mSurface.mutableGraphicBufferForTest().get());
+ EXPECT_EQ(buffer.get(), mSurface.mutableTextureForTest().get());
}
TEST_F(RenderSurfaceTest, queueBufferHandlesClientComposition) {
- sp<GraphicBuffer> buffer = new GraphicBuffer();
- mSurface.mutableGraphicBufferForTest() = buffer;
+ const auto buffer = std::make_shared<renderengine::ExternalTexture>(new GraphicBuffer(),
+ mRenderEngine, false);
+ mSurface.mutableTextureForTest() = buffer;
impl::OutputCompositionState state;
state.usesClientComposition = true;
state.flipClientTarget = false;
EXPECT_CALL(mDisplay, getState()).WillOnce(ReturnRef(state));
- EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
+ EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getBuffer()->getNativeBuffer(), -1))
.WillOnce(Return(NO_ERROR));
EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
mSurface.queueBuffer(base::unique_fd());
- EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
+ EXPECT_EQ(nullptr, mSurface.mutableTextureForTest().get());
}
TEST_F(RenderSurfaceTest, queueBufferHandlesFlipClientTargetRequest) {
- sp<GraphicBuffer> buffer = new GraphicBuffer();
- mSurface.mutableGraphicBufferForTest() = buffer;
+ const auto buffer = std::make_shared<renderengine::ExternalTexture>(new GraphicBuffer(),
+ mRenderEngine, false);
+ mSurface.mutableTextureForTest() = buffer;
impl::OutputCompositionState state;
state.usesClientComposition = false;
state.flipClientTarget = true;
EXPECT_CALL(mDisplay, getState()).WillOnce(ReturnRef(state));
- EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
+ EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getBuffer()->getNativeBuffer(), -1))
.WillOnce(Return(NO_ERROR));
EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
mSurface.queueBuffer(base::unique_fd());
- EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
+ EXPECT_EQ(nullptr, mSurface.mutableTextureForTest().get());
}
TEST_F(RenderSurfaceTest, queueBufferHandlesFlipClientTargetRequestWithNoBufferYetDequeued) {
@@ -303,27 +324,28 @@
mSurface.queueBuffer(base::unique_fd());
- EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
+ EXPECT_EQ(nullptr, mSurface.mutableTextureForTest().get());
}
TEST_F(RenderSurfaceTest, queueBufferHandlesNativeWindowQueueBufferFailureOnVirtualDisplay) {
- sp<GraphicBuffer> buffer = new GraphicBuffer();
- mSurface.mutableGraphicBufferForTest() = buffer;
+ const auto buffer = std::make_shared<renderengine::ExternalTexture>(new GraphicBuffer(),
+ mRenderEngine, false);
+ mSurface.mutableTextureForTest() = buffer;
impl::OutputCompositionState state;
state.usesClientComposition = true;
EXPECT_CALL(mDisplay, getState()).WillOnce(ReturnRef(state));
- EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
+ EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getBuffer()->getNativeBuffer(), -1))
.WillOnce(Return(INVALID_OPERATION));
EXPECT_CALL(mDisplay, isVirtual()).WillOnce(Return(true));
- EXPECT_CALL(*mNativeWindow, cancelBuffer(buffer->getNativeBuffer(), -1))
+ EXPECT_CALL(*mNativeWindow, cancelBuffer(buffer->getBuffer()->getNativeBuffer(), -1))
.WillOnce(Return(NO_ERROR));
EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
mSurface.queueBuffer(base::unique_fd());
- EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
+ EXPECT_EQ(nullptr, mSurface.mutableTextureForTest().get());
}
/*
@@ -350,3 +372,6 @@
} // namespace
} // namespace android::compositionengine
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
new file mode 100644
index 0000000..8884711
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -0,0 +1,384 @@
+/*
+ * Copyright 2021 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 <compositionengine/impl/OutputCompositionState.h>
+#include <compositionengine/impl/planner/CachedSet.h>
+#include <compositionengine/impl/planner/LayerState.h>
+#include <compositionengine/mock/LayerFE.h>
+#include <compositionengine/mock/OutputLayer.h>
+#include <gtest/gtest.h>
+#include <renderengine/mock/RenderEngine.h>
+
+namespace android::compositionengine {
+using namespace std::chrono_literals;
+
+using testing::_;
+using testing::DoAll;
+using testing::Invoke;
+using testing::Return;
+using testing::ReturnRef;
+using testing::SetArgPointee;
+
+using impl::planner::CachedSet;
+using impl::planner::LayerState;
+using impl::planner::LayerStateField;
+
+namespace {
+
+class CachedSetTest : public testing::Test {
+public:
+ CachedSetTest() = default;
+ void SetUp() override;
+ void TearDown() override;
+
+protected:
+ const std::chrono::steady_clock::time_point kStartTime = std::chrono::steady_clock::now();
+
+ struct TestLayer {
+ mock::OutputLayer outputLayer;
+ impl::OutputLayerCompositionState outputLayerCompositionState;
+ // LayerFE inherits from RefBase and must be held by an sp<>
+ sp<mock::LayerFE> layerFE;
+ LayerFECompositionState layerFECompositionState;
+
+ std::unique_ptr<LayerState> layerState;
+ std::unique_ptr<CachedSet::Layer> cachedSetLayer;
+ };
+
+ static constexpr size_t kNumLayers = 5;
+ std::vector<std::unique_ptr<TestLayer>> mTestLayers;
+ impl::OutputCompositionState mOutputState;
+
+ android::renderengine::mock::RenderEngine mRenderEngine;
+};
+
+void CachedSetTest::SetUp() {
+ for (size_t i = 0; i < kNumLayers; i++) {
+ auto testLayer = std::make_unique<TestLayer>();
+ auto pos = static_cast<int32_t>(i);
+ testLayer->outputLayerCompositionState.displayFrame = Rect(pos, pos, pos + 1, pos + 1);
+ testLayer->outputLayerCompositionState.visibleRegion =
+ Region(Rect(pos + 1, pos + 1, pos + 2, pos + 2));
+
+ testLayer->layerFE = sp<mock::LayerFE>::make();
+
+ EXPECT_CALL(*testLayer->layerFE, getSequence)
+ .WillRepeatedly(Return(static_cast<int32_t>(i)));
+ EXPECT_CALL(*testLayer->layerFE, getDebugName).WillRepeatedly(Return("testLayer"));
+ EXPECT_CALL(*testLayer->layerFE, getCompositionState)
+ .WillRepeatedly(Return(&testLayer->layerFECompositionState));
+ EXPECT_CALL(testLayer->outputLayer, getLayerFE)
+ .WillRepeatedly(ReturnRef(*testLayer->layerFE));
+ EXPECT_CALL(testLayer->outputLayer, getState)
+ .WillRepeatedly(ReturnRef(testLayer->outputLayerCompositionState));
+
+ testLayer->layerState = std::make_unique<LayerState>(&testLayer->outputLayer);
+ testLayer->layerState->incrementFramesSinceBufferUpdate();
+ testLayer->cachedSetLayer =
+ std::make_unique<CachedSet::Layer>(testLayer->layerState.get(), kStartTime);
+
+ mTestLayers.emplace_back(std::move(testLayer));
+
+ // set up minimium params needed for rendering
+ mOutputState.dataspace = ui::Dataspace::SRGB;
+ mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(10, 5));
+ mOutputState.framebufferSpace.orientation = ui::ROTATION_90;
+ mOutputState.layerStackSpace = ProjectionSpace(ui::Size(20, 10), Rect(5, 10));
+ }
+}
+
+void CachedSetTest::TearDown() {
+ mTestLayers.clear();
+}
+
+void expectEqual(const CachedSet& cachedSet, const CachedSet::Layer& layer) {
+ EXPECT_EQ(layer.getHash(), cachedSet.getFingerprint());
+ EXPECT_EQ(layer.getLastUpdate(), cachedSet.getLastUpdate());
+ EXPECT_EQ(layer.getDisplayFrame(), cachedSet.getBounds());
+ EXPECT_TRUE(layer.getVisibleRegion().hasSameRects(cachedSet.getVisibleRegion()));
+ EXPECT_EQ(1u, cachedSet.getLayerCount());
+ EXPECT_EQ(layer.getState(), cachedSet.getFirstLayer().getState());
+ EXPECT_EQ(0u, cachedSet.getAge());
+ EXPECT_EQ(layer.getHash(), cachedSet.getNonBufferHash());
+}
+
+void expectEqual(const CachedSet& cachedSet, const LayerState& layerState,
+ std::chrono::steady_clock::time_point lastUpdate) {
+ CachedSet::Layer layer(&layerState, lastUpdate);
+ expectEqual(cachedSet, layer);
+}
+
+void expectNoBuffer(const CachedSet& cachedSet) {
+ EXPECT_EQ(nullptr, cachedSet.getBuffer());
+ EXPECT_EQ(nullptr, cachedSet.getDrawFence());
+ EXPECT_FALSE(cachedSet.hasReadyBuffer());
+}
+
+void expectReadyBuffer(const CachedSet& cachedSet) {
+ EXPECT_NE(nullptr, cachedSet.getBuffer());
+ EXPECT_NE(nullptr, cachedSet.getDrawFence());
+ EXPECT_TRUE(cachedSet.hasReadyBuffer());
+}
+
+TEST_F(CachedSetTest, createFromLayer) {
+ CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
+ CachedSet cachedSet(layer);
+ expectEqual(cachedSet, layer);
+ expectNoBuffer(cachedSet);
+}
+
+TEST_F(CachedSetTest, createFromLayerState) {
+ LayerState& layerState = *mTestLayers[0]->layerState.get();
+ CachedSet cachedSet(&layerState, kStartTime);
+ expectEqual(cachedSet, layerState, kStartTime);
+ expectNoBuffer(cachedSet);
+}
+
+TEST_F(CachedSetTest, addLayer) {
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+
+ CachedSet cachedSet(layer1);
+ cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+
+ EXPECT_EQ(layer1.getHash(), cachedSet.getFingerprint());
+ EXPECT_EQ(kStartTime, cachedSet.getLastUpdate());
+ EXPECT_EQ(Rect(0, 0, 2, 2), cachedSet.getBounds());
+ Region expectedRegion;
+ expectedRegion.orSelf(Rect(1, 1, 2, 2));
+ expectedRegion.orSelf(Rect(2, 2, 3, 3));
+ EXPECT_TRUE(cachedSet.getVisibleRegion().hasSameRects(expectedRegion));
+ EXPECT_EQ(2u, cachedSet.getLayerCount());
+ EXPECT_EQ(0u, cachedSet.getAge());
+ expectNoBuffer(cachedSet);
+ // TODO(b/181192080): check that getNonBufferHash returns the correct hash value
+ // EXPECT_EQ(android::hashCombine(layer1.getHash(), layer2.getHash()),
+ // cachedSet.getNonBufferHash());
+}
+
+TEST_F(CachedSetTest, decompose) {
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+
+ CachedSet cachedSet(layer1);
+ cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+ cachedSet.addLayer(layer3.getState(), kStartTime + 20ms);
+
+ std::vector<CachedSet> decomposed = cachedSet.decompose();
+ EXPECT_EQ(3u, decomposed.size());
+ expectEqual(decomposed[0], *layer1.getState(), kStartTime);
+ expectNoBuffer(decomposed[0]);
+
+ expectEqual(decomposed[1], *layer2.getState(), kStartTime + 10ms);
+ expectNoBuffer(decomposed[1]);
+
+ expectEqual(decomposed[2], *layer3.getState(), kStartTime + 20ms);
+ expectNoBuffer(decomposed[2]);
+}
+
+TEST_F(CachedSetTest, setLastUpdate) {
+ LayerState& layerState = *mTestLayers[0]->layerState.get();
+ CachedSet cachedSet(&layerState, kStartTime);
+ cachedSet.setLastUpdate(kStartTime + 10ms);
+ expectEqual(cachedSet, layerState, kStartTime + 10ms);
+}
+
+TEST_F(CachedSetTest, incrementAge) {
+ CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
+ CachedSet cachedSet(layer);
+ EXPECT_EQ(0u, cachedSet.getAge());
+ cachedSet.incrementAge();
+ EXPECT_EQ(1u, cachedSet.getAge());
+ cachedSet.incrementAge();
+ EXPECT_EQ(2u, cachedSet.getAge());
+}
+
+TEST_F(CachedSetTest, hasBufferUpdate_NoUpdate) {
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+
+ CachedSet cachedSet(layer1);
+ cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+ cachedSet.addLayer(layer3.getState(), kStartTime + 20ms);
+
+ EXPECT_FALSE(cachedSet.hasBufferUpdate());
+}
+
+TEST_F(CachedSetTest, hasBufferUpdate_BufferUpdate) {
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+
+ CachedSet cachedSet(layer1);
+ cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+ cachedSet.addLayer(layer3.getState(), kStartTime + 20ms);
+
+ mTestLayers[1]->layerState->resetFramesSinceBufferUpdate();
+
+ EXPECT_TRUE(cachedSet.hasBufferUpdate());
+}
+
+TEST_F(CachedSetTest, append) {
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+
+ CachedSet cachedSet1(layer1);
+ CachedSet cachedSet2(layer2);
+ cachedSet1.addLayer(layer3.getState(), kStartTime + 10ms);
+ cachedSet1.append(cachedSet2);
+
+ EXPECT_EQ(layer1.getHash(), cachedSet1.getFingerprint());
+ EXPECT_EQ(kStartTime, cachedSet1.getLastUpdate());
+ EXPECT_EQ(Rect(0, 0, 3, 3), cachedSet1.getBounds());
+ Region expectedRegion;
+ expectedRegion.orSelf(Rect(1, 1, 2, 2));
+ expectedRegion.orSelf(Rect(2, 2, 3, 3));
+ expectedRegion.orSelf(Rect(3, 3, 4, 4));
+ EXPECT_TRUE(cachedSet1.getVisibleRegion().hasSameRects(expectedRegion));
+ EXPECT_EQ(3u, cachedSet1.getLayerCount());
+ EXPECT_EQ(0u, cachedSet1.getAge());
+ expectNoBuffer(cachedSet1);
+ // TODO(b/181192080): check that getNonBufferHash returns the correct hash value
+ // EXPECT_EQ(android::hashCombine(layer1.getHash(), layer2.getHash()),
+ // cachedSet1.getNonBufferHash());
+}
+
+TEST_F(CachedSetTest, updateAge_NoUpdate) {
+ CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
+
+ CachedSet cachedSet(layer);
+ cachedSet.incrementAge();
+ EXPECT_EQ(kStartTime, cachedSet.getLastUpdate());
+ EXPECT_EQ(1u, cachedSet.getAge());
+
+ cachedSet.updateAge(kStartTime + 10ms);
+ EXPECT_EQ(kStartTime, cachedSet.getLastUpdate());
+ EXPECT_EQ(1u, cachedSet.getAge());
+}
+
+TEST_F(CachedSetTest, updateAge_BufferUpdate) {
+ CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
+ mTestLayers[0]->layerState->resetFramesSinceBufferUpdate();
+
+ CachedSet cachedSet(layer);
+ cachedSet.incrementAge();
+ EXPECT_EQ(kStartTime, cachedSet.getLastUpdate());
+ EXPECT_EQ(1u, cachedSet.getAge());
+
+ cachedSet.updateAge(kStartTime + 10ms);
+ EXPECT_EQ(kStartTime + 10ms, cachedSet.getLastUpdate());
+ EXPECT_EQ(0u, cachedSet.getAge());
+}
+
+TEST_F(CachedSetTest, render) {
+ // Skip the 0th layer to ensure that the bounding box of the layers is offset from (0, 0)
+ CachedSet::Layer& layer1 = *mTestLayers[1]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE1 = mTestLayers[1]->layerFE;
+ CachedSet::Layer& layer2 = *mTestLayers[2]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE2 = mTestLayers[2]->layerFE;
+
+ CachedSet cachedSet(layer1);
+ cachedSet.append(CachedSet(layer2));
+
+ std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
+ clientCompList1.push_back({});
+ clientCompList1[0].alpha = 0.5f;
+
+ std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
+ clientCompList2.push_back({});
+ clientCompList2[0].alpha = 0.75f;
+
+ const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
+ const std::vector<const renderengine::LayerSettings*>& layers,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&, base::unique_fd*) -> size_t {
+ EXPECT_EQ(Rect(-1, -1, 9, 4), displaySettings.physicalDisplay);
+ EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip);
+ EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
+ displaySettings.orientation);
+ EXPECT_EQ(0.5f, layers[0]->alpha);
+ EXPECT_EQ(0.75f, layers[1]->alpha);
+ EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace);
+
+ return NO_ERROR;
+ };
+
+ EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
+ EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+ cachedSet.render(mRenderEngine, mOutputState);
+ expectReadyBuffer(cachedSet);
+
+ EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
+
+ // Now check that appending a new cached set properly cleans up RenderEngine resources.
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+ cachedSet.append(CachedSet(layer3));
+}
+
+TEST_F(CachedSetTest, rendersWithOffsetFramebufferContent) {
+ // Skip the 0th layer to ensure that the bounding box of the layers is offset from (0, 0)
+ CachedSet::Layer& layer1 = *mTestLayers[1]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE1 = mTestLayers[1]->layerFE;
+ CachedSet::Layer& layer2 = *mTestLayers[2]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE2 = mTestLayers[2]->layerFE;
+
+ CachedSet cachedSet(layer1);
+ cachedSet.append(CachedSet(layer2));
+
+ std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
+ clientCompList1.push_back({});
+ clientCompList1[0].alpha = 0.5f;
+
+ std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
+ clientCompList2.push_back({});
+ clientCompList2[0].alpha = 0.75f;
+
+ mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(2, 3, 10, 5));
+
+ const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
+ const std::vector<const renderengine::LayerSettings*>& layers,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&, base::unique_fd*) -> size_t {
+ EXPECT_EQ(Rect(1, 2, 9, 4), displaySettings.physicalDisplay);
+ EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip);
+ EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
+ displaySettings.orientation);
+ EXPECT_EQ(0.5f, layers[0]->alpha);
+ EXPECT_EQ(0.75f, layers[1]->alpha);
+ EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace);
+
+ return NO_ERROR;
+ };
+
+ EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
+ EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+ cachedSet.render(mRenderEngine, mOutputState);
+ expectReadyBuffer(cachedSet);
+
+ EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
+
+ // Now check that appending a new cached set properly cleans up RenderEngine resources.
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+ cachedSet.append(CachedSet(layer3));
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
new file mode 100644
index 0000000..373b895
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -0,0 +1,532 @@
+/*
+ * Copyright 2021 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 <compositionengine/impl/OutputCompositionState.h>
+#include <compositionengine/impl/planner/CachedSet.h>
+#include <compositionengine/impl/planner/Flattener.h>
+#include <compositionengine/impl/planner/LayerState.h>
+#include <compositionengine/impl/planner/Predictor.h>
+#include <compositionengine/mock/LayerFE.h>
+#include <compositionengine/mock/OutputLayer.h>
+#include <gtest/gtest.h>
+#include <renderengine/mock/RenderEngine.h>
+
+namespace android::compositionengine {
+using namespace std::chrono_literals;
+using impl::planner::Flattener;
+using impl::planner::LayerState;
+using impl::planner::NonBufferHash;
+using impl::planner::Predictor;
+
+using testing::_;
+using testing::ByMove;
+using testing::ByRef;
+using testing::DoAll;
+using testing::Invoke;
+using testing::Return;
+using testing::ReturnRef;
+using testing::Sequence;
+using testing::SetArgPointee;
+
+namespace {
+
+class FlattenerTest : public testing::Test {
+public:
+ FlattenerTest() : mFlattener(std::make_unique<Flattener>(mPredictor)) {}
+ void SetUp() override;
+
+protected:
+ void initializeOverrideBuffer(const std::vector<const LayerState*>& layers);
+ void initializeFlattener(const std::vector<const LayerState*>& layers);
+ void expectAllLayersFlattened(const std::vector<const LayerState*>& layers);
+
+ // TODO(b/181192467): Once Flattener starts to do something useful with Predictor,
+ // mPredictor should be mocked and checked for expectations.
+ Predictor mPredictor;
+
+ // mRenderEngine may be held as a pointer to mFlattener, so mFlattener must be destroyed first.
+ renderengine::mock::RenderEngine mRenderEngine;
+ std::unique_ptr<Flattener> mFlattener;
+
+ const std::chrono::steady_clock::time_point kStartTime = std::chrono::steady_clock::now();
+ std::chrono::steady_clock::time_point mTime = kStartTime;
+
+ struct TestLayer {
+ std::string name;
+ mock::OutputLayer outputLayer;
+ impl::OutputLayerCompositionState outputLayerCompositionState;
+ // LayerFE inherits from RefBase and must be held by an sp<>
+ sp<mock::LayerFE> layerFE;
+ LayerFECompositionState layerFECompositionState;
+
+ std::unique_ptr<LayerState> layerState;
+ };
+
+ static constexpr size_t kNumLayers = 5;
+ std::vector<std::unique_ptr<TestLayer>> mTestLayers;
+ impl::OutputCompositionState mOutputState;
+};
+
+void FlattenerTest::SetUp() {
+ for (size_t i = 0; i < kNumLayers; i++) {
+ auto testLayer = std::make_unique<TestLayer>();
+ auto pos = static_cast<int32_t>(i);
+ std::stringstream ss;
+ ss << "testLayer" << i;
+ testLayer->name = ss.str();
+
+ testLayer->outputLayerCompositionState.displayFrame = Rect(pos, pos, pos + 1, pos + 1);
+ testLayer->outputLayerCompositionState.visibleRegion =
+ Region(Rect(pos + 1, pos + 1, pos + 2, pos + 2));
+
+ testLayer->layerFECompositionState.buffer =
+ new GraphicBuffer(100, 100, HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
+ GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE,
+ "output");
+
+ testLayer->layerFE = sp<mock::LayerFE>::make();
+
+ EXPECT_CALL(*testLayer->layerFE, getSequence)
+ .WillRepeatedly(Return(static_cast<int32_t>(i)));
+ EXPECT_CALL(*testLayer->layerFE, getDebugName)
+ .WillRepeatedly(Return(testLayer->name.c_str()));
+ EXPECT_CALL(*testLayer->layerFE, getCompositionState)
+ .WillRepeatedly(Return(&testLayer->layerFECompositionState));
+
+ std::vector<LayerFE::LayerSettings> clientCompositionList = {
+ LayerFE::LayerSettings{},
+ };
+
+ EXPECT_CALL(*testLayer->layerFE, prepareClientCompositionList)
+ .WillRepeatedly(Return(clientCompositionList));
+ EXPECT_CALL(testLayer->outputLayer, getLayerFE)
+ .WillRepeatedly(ReturnRef(*testLayer->layerFE));
+ EXPECT_CALL(testLayer->outputLayer, getState)
+ .WillRepeatedly(ReturnRef(testLayer->outputLayerCompositionState));
+ EXPECT_CALL(testLayer->outputLayer, editState)
+ .WillRepeatedly(ReturnRef(testLayer->outputLayerCompositionState));
+
+ testLayer->layerState = std::make_unique<LayerState>(&testLayer->outputLayer);
+ testLayer->layerState->incrementFramesSinceBufferUpdate();
+
+ mTestLayers.emplace_back(std::move(testLayer));
+
+ // set up minimium params needed for rendering
+ mOutputState.dataspace = ui::Dataspace::SRGB;
+ mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(10, 5));
+ mOutputState.framebufferSpace.orientation = ui::ROTATION_90;
+ }
+}
+
+void FlattenerTest::initializeOverrideBuffer(const std::vector<const LayerState*>& layers) {
+ for (const auto layer : layers) {
+ layer->getOutputLayer()->editState().overrideInfo = {};
+ }
+}
+
+void FlattenerTest::initializeFlattener(const std::vector<const LayerState*>& layers) {
+ // layer stack is unknown, reset current geomentry
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ // same geometry, update the internal layer stack
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+}
+
+void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState*>& layers) {
+ // layers would be flattened but the buffer would not be overridden
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ for (const auto layer : layers) {
+ EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
+ }
+
+ // the new flattened layer is replaced
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ const auto buffer = layers[0]->getOutputLayer()->getState().overrideInfo.buffer;
+ EXPECT_NE(nullptr, buffer);
+ for (const auto layer : layers) {
+ EXPECT_EQ(buffer, layer->getOutputLayer()->getState().overrideInfo.buffer);
+ }
+}
+
+TEST_F(FlattenerTest, flattenLayers_NewLayerStack) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ auto& layerState2 = mTestLayers[1]->layerState;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ };
+ initializeFlattener(layers);
+}
+
+TEST_F(FlattenerTest, flattenLayers_ActiveLayersAreNotFlattened) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ auto& layerState2 = mTestLayers[1]->layerState;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // layers cannot be flattened yet, since they are still active
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+}
+
+TEST_F(FlattenerTest, flattenLayers_basicFlatten) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ auto& layerState2 = mTestLayers[1]->layerState;
+ auto& layerState3 = mTestLayers[2]->layerState;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ layerState3.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // make all layers inactive
+ mTime += 200ms;
+ expectAllLayersFlattened(layers);
+}
+
+TEST_F(FlattenerTest, flattenLayers_FlattenedLayersStayFlattenWhenNoUpdate) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+ auto& layerState2 = mTestLayers[1]->layerState;
+ const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+
+ auto& layerState3 = mTestLayers[2]->layerState;
+ const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ layerState3.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // make all layers inactive
+ mTime += 200ms;
+ expectAllLayersFlattened(layers);
+
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ EXPECT_NE(nullptr, overrideBuffer1);
+ EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+ EXPECT_EQ(overrideBuffer2, overrideBuffer3);
+}
+
+TEST_F(FlattenerTest, flattenLayers_FlattenedLayersSetsProjectionSpace) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ const auto& overrideDisplaySpace =
+ layerState1->getOutputLayer()->getState().overrideInfo.displaySpace;
+
+ auto& layerState2 = mTestLayers[1]->layerState;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // make all layers inactive
+ mTime += 200ms;
+ expectAllLayersFlattened(layers);
+
+ EXPECT_EQ(overrideDisplaySpace, mOutputState.framebufferSpace);
+}
+
+TEST_F(FlattenerTest, flattenLayers_FlattenedLayersSetsDamageRegions) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ const auto& overrideDamageRegion =
+ layerState1->getOutputLayer()->getState().overrideInfo.damageRegion;
+
+ auto& layerState2 = mTestLayers[1]->layerState;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // make all layers inactive
+ mTime += 200ms;
+ expectAllLayersFlattened(layers);
+ EXPECT_TRUE(overrideDamageRegion.isRect() &&
+ overrideDamageRegion.bounds() == Rect::INVALID_RECT);
+
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ EXPECT_TRUE(overrideDamageRegion.isRect() && overrideDamageRegion.bounds() == Rect::EMPTY_RECT);
+}
+
+TEST_F(FlattenerTest, flattenLayers_FlattenedLayersSetsVisibleRegion) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ const auto& overrideVisibleRegion =
+ layerState1->getOutputLayer()->getState().overrideInfo.visibleRegion;
+
+ auto& layerState2 = mTestLayers[1]->layerState;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // make all layers inactive
+ mTime += 200ms;
+ expectAllLayersFlattened(layers);
+ Region expectedRegion;
+ expectedRegion.orSelf(Rect(1, 1, 2, 2));
+ expectedRegion.orSelf(Rect(2, 2, 3, 3));
+ EXPECT_TRUE(overrideVisibleRegion.hasSameRects(expectedRegion));
+}
+
+TEST_F(FlattenerTest, flattenLayers_addLayerToFlattenedCauseReset) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+ auto& layerState2 = mTestLayers[1]->layerState;
+ const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+
+ auto& layerState3 = mTestLayers[2]->layerState;
+ const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+
+ std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ };
+
+ initializeFlattener(layers);
+ // make all layers inactive
+ mTime += 200ms;
+
+ initializeOverrideBuffer(layers);
+ expectAllLayersFlattened(layers);
+
+ // add a new layer to the stack, this will cause all the flatenner to reset
+ layers.push_back(layerState3.get());
+
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ EXPECT_EQ(nullptr, overrideBuffer1);
+ EXPECT_EQ(nullptr, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+}
+
+TEST_F(FlattenerTest, flattenLayers_BufferUpdateToFlatten) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+ auto& layerState2 = mTestLayers[1]->layerState;
+ const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+
+ auto& layerState3 = mTestLayers[2]->layerState;
+ const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ layerState3.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // make all layers inactive
+ mTime += 200ms;
+ expectAllLayersFlattened(layers);
+
+ // Layer 1 posted a buffer update, layers would be decomposed, and a new drawFrame would be
+ // caleed for Layer2 and Layer3
+ layerState1->resetFramesSinceBufferUpdate();
+
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ EXPECT_EQ(nullptr, overrideBuffer1);
+ EXPECT_EQ(nullptr, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ EXPECT_EQ(nullptr, overrideBuffer1);
+ EXPECT_NE(nullptr, overrideBuffer2);
+ EXPECT_EQ(overrideBuffer2, overrideBuffer3);
+
+ layerState1->incrementFramesSinceBufferUpdate();
+ mTime += 200ms;
+
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ EXPECT_EQ(nullptr, overrideBuffer1);
+ EXPECT_NE(nullptr, overrideBuffer2);
+ EXPECT_EQ(overrideBuffer2, overrideBuffer3);
+
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ EXPECT_NE(nullptr, overrideBuffer1);
+ EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+ EXPECT_EQ(overrideBuffer2, overrideBuffer3);
+}
+
+TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+ auto& layerState2 = mTestLayers[1]->layerState;
+ const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+
+ auto& layerState3 = mTestLayers[2]->layerState;
+ const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+
+ auto& layerState4 = mTestLayers[3]->layerState;
+ const auto& overrideBuffer4 = layerState4->getOutputLayer()->getState().overrideInfo.buffer;
+
+ auto& layerState5 = mTestLayers[4]->layerState;
+ const auto& overrideBuffer5 = layerState5->getOutputLayer()->getState().overrideInfo.buffer;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(), layerState2.get(), layerState3.get(),
+ layerState4.get(), layerState5.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // make all layers inactive
+ mTime += 200ms;
+ expectAllLayersFlattened(layers);
+
+ // Layer 3 posted a buffer update, layers would be decomposed, and a new drawFrame would be
+ // called for Layer1 and Layer2
+ layerState3->resetFramesSinceBufferUpdate();
+
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ EXPECT_EQ(nullptr, overrideBuffer1);
+ EXPECT_EQ(nullptr, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+ EXPECT_EQ(nullptr, overrideBuffer4);
+ EXPECT_EQ(nullptr, overrideBuffer5);
+
+ // Layers 1 and 2 will be flattened a new drawFrame would be called for Layer4 and Layer5
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mOutputState.framebufferSpace.orientation = ui::ROTATION_90;
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ EXPECT_NE(nullptr, overrideBuffer1);
+ EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+ EXPECT_EQ(nullptr, overrideBuffer4);
+ EXPECT_EQ(nullptr, overrideBuffer5);
+
+ // Layers 4 and 5 will be flattened
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mOutputState.framebufferSpace.orientation = ui::ROTATION_180;
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ EXPECT_NE(nullptr, overrideBuffer1);
+ EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+ EXPECT_NE(nullptr, overrideBuffer4);
+ EXPECT_EQ(overrideBuffer4, overrideBuffer5);
+
+ layerState3->incrementFramesSinceBufferUpdate();
+ mTime += 200ms;
+
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ EXPECT_NE(nullptr, overrideBuffer1);
+ EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+ EXPECT_NE(nullptr, overrideBuffer4);
+ EXPECT_EQ(overrideBuffer4, overrideBuffer5);
+
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mOutputState.framebufferSpace.orientation = ui::ROTATION_270;
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ EXPECT_NE(nullptr, overrideBuffer1);
+ EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+ EXPECT_EQ(overrideBuffer2, overrideBuffer3);
+ EXPECT_EQ(overrideBuffer3, overrideBuffer4);
+ EXPECT_EQ(overrideBuffer4, overrideBuffer5);
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
new file mode 100644
index 0000000..948c850
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
@@ -0,0 +1,916 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LayerStateTest"
+
+#include <compositionengine/impl/OutputLayer.h>
+#include <compositionengine/impl/planner/LayerState.h>
+#include <compositionengine/mock/LayerFE.h>
+#include <compositionengine/mock/OutputLayer.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+#include "android/hardware_buffer.h"
+#include "compositionengine/LayerFECompositionState.h"
+
+namespace android::compositionengine::impl::planner {
+namespace {
+
+using testing::Return;
+using testing::ReturnRef;
+
+const std::string sDebugName = std::string("Test LayerFE");
+const std::string sDebugNameTwo = std::string("Test LayerFE2");
+const constexpr int32_t sSequenceId = 12345;
+const constexpr int32_t sSequenceIdTwo = 123456;
+const Rect sRectOne = Rect(10, 20, 30, 40);
+const Rect sRectTwo = Rect(40, 30, 20, 10);
+const FloatRect sFloatRectOne = FloatRect(100.f, 200.f, 300.f, 400.f);
+const FloatRect sFloatRectTwo = FloatRect(400.f, 300.f, 200.f, 100.f);
+const constexpr float sAlphaOne = 0.25f;
+const constexpr float sAlphaTwo = 0.5f;
+const Region sRegionOne = Region(sRectOne);
+const Region sRegionTwo = Region(sRectTwo);
+const mat4 sMat4One = mat4::scale(vec4(2.f, 3.f, 1.f, 1.f));
+native_handle_t* const sFakeSidebandStreamOne = reinterpret_cast<native_handle_t*>(10);
+native_handle_t* const sFakeSidebandStreamTwo = reinterpret_cast<native_handle_t*>(11);
+const half4 sHalf4One = half4(0.2f, 0.3f, 0.4f, 0.5f);
+const half4 sHalf4Two = half4(0.5f, 0.4f, 0.3f, 0.2f);
+const std::string sMetadataKeyOne = std::string("Meta!");
+const std::string sMetadataKeyTwo = std::string("Data!");
+const GenericLayerMetadataEntry sMetadataValueOne = GenericLayerMetadataEntry{
+ .value = std::vector<uint8_t>({1, 2}),
+};
+const GenericLayerMetadataEntry sMetadataValueTwo = GenericLayerMetadataEntry{
+ .value = std::vector<uint8_t>({1, 3}),
+};
+
+struct LayerStateTest : public testing::Test {
+ LayerStateTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+
+ ~LayerStateTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+
+ void setupMocksForLayer(mock::OutputLayer& layer, mock::LayerFE& layerFE,
+ const OutputLayerCompositionState& outputLayerState,
+ const LayerFECompositionState& layerFEState,
+ int32_t sequenceId = sSequenceId,
+ const std::string& debugName = sDebugName) {
+ EXPECT_CALL(layer, getLayerFE()).WillRepeatedly(ReturnRef(layerFE));
+ EXPECT_CALL(layer, getState()).WillRepeatedly(ReturnRef(outputLayerState));
+ EXPECT_CALL(layerFE, getSequence()).WillRepeatedly(Return(sequenceId));
+ EXPECT_CALL(layerFE, getDebugName()).WillRepeatedly(Return(debugName.c_str()));
+ EXPECT_CALL(layerFE, getCompositionState()).WillRepeatedly(Return(&layerFEState));
+ }
+
+ void verifyUniqueDifferingFields(const LayerState& lhs, const LayerState& rhs) {
+ EXPECT_EQ(lhs.getHash(), rhs.getHash());
+
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None), lhs.getDifferingFields(rhs));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None), rhs.getDifferingFields(lhs));
+ }
+
+ void verifyNonUniqueDifferingFields(const LayerState& lhs, const LayerState& rhs,
+ Flags<LayerStateField> fields) {
+ EXPECT_NE(lhs.getHash(), rhs.getHash());
+
+ EXPECT_EQ(fields, lhs.getDifferingFields(rhs));
+ EXPECT_EQ(fields, rhs.getDifferingFields(lhs));
+ }
+
+ mock::LayerFE mLayerFE;
+ mock::OutputLayer mOutputLayer;
+ std::unique_ptr<LayerState> mLayerState;
+};
+
+TEST_F(LayerStateTest, getOutputLayer) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ EXPECT_EQ(&mOutputLayer, mLayerState->getOutputLayer());
+}
+
+TEST_F(LayerStateTest, getId) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ EXPECT_EQ(sSequenceId, mLayerState->getId());
+}
+
+TEST_F(LayerStateTest, updateId) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionState, sSequenceIdTwo);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(sSequenceIdTwo, mLayerState->getId());
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Id), updates);
+}
+
+TEST_F(LayerStateTest, compareId) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionState, sSequenceIdTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ EXPECT_NE(mLayerState->getId(), otherLayerState->getId());
+
+ // Id is a unique field, so it's not computed in the hash for a layer state.
+ verifyUniqueDifferingFields(*mLayerState, *otherLayerState);
+ EXPECT_FALSE(mLayerState->compare(*otherLayerState));
+ EXPECT_FALSE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, getName) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ EXPECT_EQ(sDebugName, mLayerState->getName());
+}
+
+TEST_F(LayerStateTest, updateName) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionState, sSequenceId, sDebugNameTwo);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(sDebugNameTwo, mLayerState->getName());
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Name), updates);
+}
+
+TEST_F(LayerStateTest, compareName) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionState, sSequenceId, sDebugNameTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ EXPECT_NE(mLayerState->getName(), otherLayerState->getName());
+
+ // Name is a unique field, so it's not computed in the hash for a layer state.
+ verifyUniqueDifferingFields(*mLayerState, *otherLayerState);
+ EXPECT_FALSE(mLayerState->compare(*otherLayerState));
+ EXPECT_FALSE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, getDisplayFrame) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.displayFrame = sRectOne;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ EXPECT_EQ(sRectOne, mLayerState->getDisplayFrame());
+}
+
+TEST_F(LayerStateTest, updateDisplayFrame) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.displayFrame = sRectOne;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ outputLayerCompositionStateTwo.displayFrame = sRectTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+ layerFECompositionState);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(sRectTwo, mLayerState->getDisplayFrame());
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::DisplayFrame), updates);
+}
+
+TEST_F(LayerStateTest, compareDisplayFrame) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.displayFrame = sRectOne;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ outputLayerCompositionStateTwo.displayFrame = sRectTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+ layerFECompositionState);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ EXPECT_NE(mLayerState->getDisplayFrame(), otherLayerState->getDisplayFrame());
+
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::DisplayFrame);
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, getCompositionType) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.compositionType =
+ hardware::graphics::composer::hal::Composition::DEVICE;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ EXPECT_EQ(hardware::graphics::composer::hal::Composition::DEVICE,
+ mLayerState->getCompositionType());
+}
+
+TEST_F(LayerStateTest, getCompositionType_forcedClient) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.forceClientComposition = true;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.compositionType =
+ hardware::graphics::composer::hal::Composition::DEVICE;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ EXPECT_EQ(hardware::graphics::composer::hal::Composition::CLIENT,
+ mLayerState->getCompositionType());
+}
+
+TEST_F(LayerStateTest, updateCompositionType) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.compositionType =
+ hardware::graphics::composer::hal::Composition::DEVICE;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.compositionType =
+ hardware::graphics::composer::hal::Composition::SOLID_COLOR;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(hardware::graphics::composer::hal::Composition::SOLID_COLOR,
+ mLayerState->getCompositionType());
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::CompositionType), updates);
+}
+
+TEST_F(LayerStateTest, compareCompositionType) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.compositionType =
+ hardware::graphics::composer::hal::Composition::DEVICE;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.compositionType =
+ hardware::graphics::composer::hal::Composition::SOLID_COLOR;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ EXPECT_NE(mLayerState->getCompositionType(), otherLayerState->getCompositionType());
+
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState,
+ LayerStateField::CompositionType);
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateBuffer) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.buffer = new GraphicBuffer();
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.buffer = new GraphicBuffer();
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer), updates);
+}
+
+TEST_F(LayerStateTest, compareBuffer) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.buffer = new GraphicBuffer();
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.buffer = new GraphicBuffer();
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ // A buffer is not a unique field, but the assertions are the same.
+ verifyUniqueDifferingFields(*mLayerState, *otherLayerState);
+
+ // Buffers are explicitly excluded from comparison
+ EXPECT_FALSE(mLayerState->compare(*otherLayerState));
+ EXPECT_FALSE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateSourceCrop) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.sourceCrop = sFloatRectOne;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ outputLayerCompositionStateTwo.sourceCrop = sFloatRectTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+ layerFECompositionState);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SourceCrop), updates);
+}
+
+TEST_F(LayerStateTest, compareSourceCrop) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.sourceCrop = sFloatRectOne;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ outputLayerCompositionStateTwo.sourceCrop = sFloatRectTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+ layerFECompositionState);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::SourceCrop);
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateBufferTransform) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.bufferTransform = Hwc2::Transform::FLIP_H;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ outputLayerCompositionStateTwo.bufferTransform = Hwc2::Transform::FLIP_V;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+ layerFECompositionState);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BufferTransform), updates);
+}
+
+TEST_F(LayerStateTest, compareBufferTransform) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.bufferTransform = Hwc2::Transform::FLIP_H;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ outputLayerCompositionStateTwo.bufferTransform = Hwc2::Transform::FLIP_V;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+ layerFECompositionState);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState,
+ LayerStateField::BufferTransform);
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateBlendMode) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.blendMode = hal::BlendMode::COVERAGE;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.blendMode = hal::BlendMode::PREMULTIPLIED;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BlendMode), updates);
+}
+
+TEST_F(LayerStateTest, compareBlendMode) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.blendMode = hal::BlendMode::COVERAGE;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.blendMode = hal::BlendMode::PREMULTIPLIED;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::BlendMode);
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateAlpha) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.alpha = sAlphaOne;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.alpha = sAlphaTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Alpha), updates);
+}
+
+TEST_F(LayerStateTest, compareAlpha) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.alpha = sAlphaOne;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.alpha = sAlphaTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::Alpha);
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateLayerMetadata) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.metadata[sMetadataKeyOne] = sMetadataValueOne;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.metadata[sMetadataKeyTwo] = sMetadataValueTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::LayerMetadata), updates);
+}
+
+TEST_F(LayerStateTest, compareLayerMetadata) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.metadata[sMetadataKeyOne] = sMetadataValueOne;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.metadata[sMetadataKeyTwo] = sMetadataValueTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::LayerMetadata);
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, getVisibleRegion) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.visibleRegion = sRegionOne;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ EXPECT_TRUE(mLayerState->getVisibleRegion().hasSameRects(sRegionOne));
+}
+
+TEST_F(LayerStateTest, updateVisibleRegion) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.visibleRegion = sRegionOne;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ outputLayerCompositionStateTwo.visibleRegion = sRegionTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+ layerFECompositionState);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::VisibleRegion), updates);
+}
+
+TEST_F(LayerStateTest, compareVisibleRegion) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.visibleRegion = sRegionOne;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ outputLayerCompositionStateTwo.visibleRegion = sRegionTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+ layerFECompositionState);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::VisibleRegion);
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateDataspace) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.dataspace = ui::Dataspace::SRGB;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ outputLayerCompositionStateTwo.dataspace = ui::Dataspace::DISPLAY_P3;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+ layerFECompositionState);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Dataspace), updates);
+}
+
+TEST_F(LayerStateTest, compareDataspace) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.dataspace = ui::Dataspace::SRGB;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ outputLayerCompositionStateTwo.dataspace = ui::Dataspace::DISPLAY_P3;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+ layerFECompositionState);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::Dataspace);
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updatePixelFormat) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.buffer =
+ new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBA_8888,
+ AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
+ AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+ "buffer1");
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.buffer =
+ new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBX_8888,
+ AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
+ AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+ "buffer2");
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer) |
+ Flags<LayerStateField>(LayerStateField::PixelFormat),
+ updates);
+}
+
+TEST_F(LayerStateTest, comparePixelFormat) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.buffer =
+ new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBA_8888,
+ AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
+ AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+ "buffer1");
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.buffer =
+ new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBX_8888,
+ AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
+ AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+ "buffer2");
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState,
+ Flags<LayerStateField>(LayerStateField::PixelFormat));
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateColorTransform) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.colorTransformIsIdentity = true;
+ layerFECompositionState.colorTransform = mat4();
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.colorTransformIsIdentity = false;
+ layerFECompositionStateTwo.colorTransform = sMat4One;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ColorTransform), updates);
+}
+
+TEST_F(LayerStateTest, compareColorTransform) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.colorTransformIsIdentity = true;
+ layerFECompositionState.colorTransform = mat4();
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.colorTransformIsIdentity = false;
+ layerFECompositionStateTwo.colorTransform = sMat4One;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::ColorTransform);
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateSidebandStream) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.sidebandStream = NativeHandle::create(sFakeSidebandStreamOne, false);
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.sidebandStream = NativeHandle::create(sFakeSidebandStreamTwo, false);
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SidebandStream), updates);
+}
+
+TEST_F(LayerStateTest, compareSidebandStream) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.sidebandStream = NativeHandle::create(sFakeSidebandStreamOne, false);
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.sidebandStream = NativeHandle::create(sFakeSidebandStreamTwo, false);
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::SidebandStream);
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateSolidColor) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.color = sHalf4One;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.color = sHalf4Two;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SolidColor), updates);
+}
+
+TEST_F(LayerStateTest, compareSolidColor) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.color = sHalf4One;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.color = sHalf4Two;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::SolidColor);
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, dumpDoesNotCrash) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ std::string dump;
+ mLayerState->dump(dump);
+ EXPECT_TRUE(dump.size() > 0);
+}
+
+TEST_F(LayerStateTest, framesSinceBufferUpdate) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ EXPECT_EQ(0, mLayerState->getFramesSinceBufferUpdate());
+ mLayerState->incrementFramesSinceBufferUpdate();
+ EXPECT_EQ(1, mLayerState->getFramesSinceBufferUpdate());
+ mLayerState->resetFramesSinceBufferUpdate();
+ EXPECT_EQ(0, mLayerState->getFramesSinceBufferUpdate());
+}
+
+TEST_F(LayerStateTest, getNonBufferHash_doesNotCommute) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.displayFrame = sRectOne;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ outputLayerCompositionStateTwo.displayFrame = sRectTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+ layerFECompositionState);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ EXPECT_NE(getNonBufferHash({mLayerState.get(), otherLayerState.get()}),
+ getNonBufferHash({otherLayerState.get(), mLayerState.get()}));
+}
+
+TEST_F(LayerStateTest, getNonBufferHash_isIdempotent) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.displayFrame = sRectOne;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ outputLayerCompositionStateTwo.displayFrame = sRectTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+ layerFECompositionState);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ EXPECT_EQ(getNonBufferHash({mLayerState.get(), otherLayerState.get()}),
+ getNonBufferHash({mLayerState.get(), otherLayerState.get()}));
+}
+
+TEST_F(LayerStateTest, getNonBufferHash_filtersOutBuffers) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.buffer = new GraphicBuffer();
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.buffer = new GraphicBuffer();
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ EXPECT_EQ(getNonBufferHash({mLayerState.get()}), getNonBufferHash({otherLayerState.get()}));
+}
+
+} // namespace
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
new file mode 100644
index 0000000..1492707
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
@@ -0,0 +1,550 @@
+/*
+ * Copyright 2021 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 "DisplayHardware/Hal.h"
+#undef LOG_TAG
+#define LOG_TAG "PredictorTest"
+
+#include <compositionengine/impl/planner/Predictor.h>
+#include <compositionengine/mock/LayerFE.h>
+#include <compositionengine/mock/OutputLayer.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+namespace android::compositionengine::impl::planner {
+namespace {
+
+const FloatRect sFloatRectOne = FloatRect(100.f, 200.f, 300.f, 400.f);
+const FloatRect sFloatRectTwo = FloatRect(400.f, 300.f, 200.f, 100.f);
+const Rect sRectOne = Rect(1, 2, 3, 4);
+const Rect sRectTwo = Rect(4, 3, 2, 1);
+const constexpr float sAlphaOne = 0.25f;
+const constexpr float sAlphaTwo = 0.5f;
+const Region sRegionOne = Region(sRectOne);
+const Region sRegionTwo = Region(sRectTwo);
+const mat4 sMat4One = mat4::scale(vec4(2.f, 3.f, 1.f, 1.f));
+
+using testing::Return;
+using testing::ReturnRef;
+
+const std::string sDebugName = std::string("Test LayerFE");
+const constexpr int32_t sSequenceId = 12345;
+
+void setupMocksForLayer(mock::OutputLayer& layer, mock::LayerFE& layerFE,
+ const OutputLayerCompositionState& outputLayerState,
+ const LayerFECompositionState& layerFEState) {
+ EXPECT_CALL(layer, getLayerFE()).WillRepeatedly(ReturnRef(layerFE));
+ EXPECT_CALL(layer, getState()).WillRepeatedly(ReturnRef(outputLayerState));
+ EXPECT_CALL(layerFE, getSequence()).WillRepeatedly(Return(sSequenceId));
+ EXPECT_CALL(layerFE, getDebugName()).WillRepeatedly(Return(sDebugName.c_str()));
+ EXPECT_CALL(layerFE, getCompositionState()).WillRepeatedly(Return(&layerFEState));
+}
+
+struct LayerStackTest : public testing::Test {
+ LayerStackTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+
+ ~LayerStackTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+};
+
+TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchSizeDifferences) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne;
+ LayerFECompositionState layerFECompositionStateOne;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ LayerFECompositionState layerFECompositionStateTwo;
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ mock::OutputLayer outputLayerThree;
+ mock::LayerFE layerFEThree;
+ OutputLayerCompositionState outputLayerCompositionStateThree;
+ LayerFECompositionState layerFECompositionStateThree;
+ setupMocksForLayer(outputLayerThree, layerFEThree, outputLayerCompositionStateThree,
+ layerFECompositionStateThree);
+ LayerState layerStateThree(&outputLayerThree);
+
+ LayerStack stack({&layerStateOne});
+
+ EXPECT_FALSE(stack.getApproximateMatch({}));
+ EXPECT_FALSE(stack.getApproximateMatch({&layerStateOne, &layerStateThree}));
+}
+
+TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchDifferentCompositionTypes) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne;
+ LayerFECompositionState layerFECompositionStateOne;
+ layerFECompositionStateOne.compositionType = hal::Composition::DEVICE;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.compositionType = hal::Composition::SOLID_COLOR;
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ LayerStack stack({&layerStateOne});
+
+ EXPECT_FALSE(stack.getApproximateMatch({&layerStateTwo}));
+}
+
+TEST_F(LayerStackTest, getApproximateMatch_matchesSingleDifferenceInSingleLayer) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne{
+ .sourceCrop = sFloatRectOne,
+ };
+ LayerFECompositionState layerFECompositionStateOne;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo{
+ .sourceCrop = sFloatRectTwo,
+ };
+ LayerFECompositionState layerFECompositionStateTwo;
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ LayerStack stack({&layerStateOne});
+
+ const auto match = stack.getApproximateMatch({&layerStateTwo});
+ EXPECT_TRUE(match);
+ LayerStack::ApproximateMatch expectedMatch;
+ expectedMatch.differingIndex = 0;
+ expectedMatch.differingFields = LayerStateField::SourceCrop;
+ EXPECT_EQ(expectedMatch, *match);
+}
+
+TEST_F(LayerStackTest, getApproximateMatch_matchesSingleDifferenceInMultiLayerStack) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne{
+ .sourceCrop = sFloatRectOne,
+ };
+ LayerFECompositionState layerFECompositionStateOne;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo{
+ .sourceCrop = sFloatRectTwo,
+ };
+ LayerFECompositionState layerFECompositionStateTwo;
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ LayerStack stack({&layerStateOne, &layerStateOne});
+
+ const auto match = stack.getApproximateMatch({&layerStateOne, &layerStateTwo});
+ EXPECT_TRUE(match);
+ LayerStack::ApproximateMatch expectedMatch;
+ expectedMatch.differingIndex = 1;
+ expectedMatch.differingFields = LayerStateField::SourceCrop;
+ EXPECT_EQ(expectedMatch, *match);
+}
+
+TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchManyDifferences) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne{
+ .visibleRegion = sRegionOne,
+ .displayFrame = sRectOne,
+ .sourceCrop = sFloatRectOne,
+ .dataspace = ui::Dataspace::SRGB,
+ };
+ LayerFECompositionState layerFECompositionStateOne;
+ layerFECompositionStateOne.alpha = sAlphaOne;
+ layerFECompositionStateOne.colorTransformIsIdentity = true;
+ layerFECompositionStateOne.blendMode = hal::BlendMode::NONE;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo{
+ .visibleRegion = sRegionTwo,
+ .displayFrame = sRectTwo,
+ .sourceCrop = sFloatRectTwo,
+ .dataspace = ui::Dataspace::DISPLAY_P3,
+ };
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.alpha = sAlphaTwo;
+ layerFECompositionStateTwo.colorTransformIsIdentity = false;
+ layerFECompositionStateTwo.colorTransform = sMat4One;
+ layerFECompositionStateTwo.blendMode = hal::BlendMode::PREMULTIPLIED;
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ LayerStack stack({&layerStateOne});
+
+ EXPECT_FALSE(stack.getApproximateMatch({&layerStateTwo}));
+}
+
+TEST_F(LayerStackTest, getApproximateMatch_exactMatchesSameBuffer) {
+ sp<GraphicBuffer> buffer = new GraphicBuffer();
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne;
+ LayerFECompositionState layerFECompositionStateOne;
+ layerFECompositionStateOne.buffer = buffer;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.buffer = buffer;
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ LayerStack stack({&layerStateOne});
+
+ const auto match = stack.getApproximateMatch({&layerStateTwo});
+ EXPECT_TRUE(match);
+ LayerStack::ApproximateMatch expectedMatch;
+ expectedMatch.differingIndex = 0;
+ expectedMatch.differingFields = LayerStateField::None;
+ EXPECT_EQ(expectedMatch, *match);
+}
+
+TEST_F(LayerStackTest, getApproximateMatch_alwaysMatchesClientComposition) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne{
+ .visibleRegion = sRegionOne,
+ .forceClientComposition = true,
+ .displayFrame = sRectOne,
+ .sourceCrop = sFloatRectOne,
+ .dataspace = ui::Dataspace::SRGB,
+ };
+ LayerFECompositionState layerFECompositionStateOne;
+ layerFECompositionStateOne.buffer = new GraphicBuffer();
+ layerFECompositionStateOne.alpha = sAlphaOne;
+ layerFECompositionStateOne.colorTransformIsIdentity = true;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo{
+ .visibleRegion = sRegionTwo,
+ .forceClientComposition = true,
+ .displayFrame = sRectTwo,
+ .sourceCrop = sFloatRectTwo,
+ .dataspace = ui::Dataspace::DISPLAY_P3,
+ };
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.buffer = new GraphicBuffer();
+ layerFECompositionStateTwo.alpha = sAlphaTwo;
+ layerFECompositionStateTwo.colorTransformIsIdentity = false;
+ layerFECompositionStateTwo.colorTransform = sMat4One;
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ LayerStack stack({&layerStateOne});
+
+ const auto match = stack.getApproximateMatch({&layerStateTwo});
+ EXPECT_TRUE(match);
+ LayerStack::ApproximateMatch expectedMatch;
+ expectedMatch.differingIndex = 0;
+ expectedMatch.differingFields = LayerStateField::None;
+ EXPECT_EQ(expectedMatch, *match);
+}
+
+TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchMultipleApproximations) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne{
+ .sourceCrop = sFloatRectOne,
+ };
+ LayerFECompositionState layerFECompositionStateOne;
+ layerFECompositionStateOne.buffer = new GraphicBuffer();
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo{
+ .sourceCrop = sFloatRectTwo,
+ };
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.buffer = new GraphicBuffer();
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ EXPECT_TRUE(LayerStack({&layerStateOne}).getApproximateMatch({&layerStateTwo}));
+
+ LayerStack stack({&layerStateOne, &layerStateOne});
+ EXPECT_FALSE(stack.getApproximateMatch({&layerStateTwo, &layerStateTwo}));
+}
+
+struct PredictionTest : public testing::Test {
+ PredictionTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+
+ ~PredictionTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+};
+
+TEST_F(LayerStackTest, reorderingChangesNonBufferHash) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne{
+ .sourceCrop = sFloatRectOne,
+ };
+ LayerFECompositionState layerFECompositionStateOne;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo{
+ .sourceCrop = sFloatRectTwo,
+ };
+ LayerFECompositionState layerFECompositionStateTwo;
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ NonBufferHash hash = getNonBufferHash({&layerStateOne, &layerStateTwo});
+ NonBufferHash hashReverse = getNonBufferHash({&layerStateTwo, &layerStateOne});
+ EXPECT_NE(hash, hashReverse);
+}
+
+TEST_F(PredictionTest, constructPrediction) {
+ Plan plan;
+ plan.addLayerType(hal::Composition::DEVICE);
+
+ Prediction prediction({}, plan);
+
+ EXPECT_EQ(plan, prediction.getPlan());
+
+ // check that dump doesn't crash
+ std::string result;
+ prediction.dump(result);
+}
+
+TEST_F(PredictionTest, recordHits) {
+ Prediction prediction({}, {});
+
+ const constexpr uint32_t kExactMatches = 2;
+ for (uint32_t i = 0; i < kExactMatches; i++) {
+ prediction.recordHit(Prediction::Type::Exact);
+ }
+
+ const constexpr uint32_t kApproximateMatches = 3;
+ for (uint32_t i = 0; i < kApproximateMatches; i++) {
+ prediction.recordHit(Prediction::Type::Approximate);
+ }
+
+ EXPECT_EQ(kExactMatches, prediction.getHitCount(Prediction::Type::Exact));
+ EXPECT_EQ(kApproximateMatches, prediction.getHitCount(Prediction::Type::Approximate));
+ EXPECT_EQ(kExactMatches + kApproximateMatches, prediction.getHitCount(Prediction::Type::Total));
+}
+
+TEST_F(PredictionTest, recordMisses) {
+ Prediction prediction({}, {});
+
+ const constexpr uint32_t kExactMatches = 2;
+ for (uint32_t i = 0; i < kExactMatches; i++) {
+ prediction.recordMiss(Prediction::Type::Exact);
+ }
+
+ const constexpr uint32_t kApproximateMatches = 3;
+ for (uint32_t i = 0; i < kApproximateMatches; i++) {
+ prediction.recordMiss(Prediction::Type::Approximate);
+ }
+
+ EXPECT_EQ(kExactMatches, prediction.getMissCount(Prediction::Type::Exact));
+ EXPECT_EQ(kApproximateMatches, prediction.getMissCount(Prediction::Type::Approximate));
+ EXPECT_EQ(kExactMatches + kApproximateMatches,
+ prediction.getMissCount(Prediction::Type::Total));
+}
+
+struct PredictorTest : public testing::Test {
+ PredictorTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+
+ ~PredictorTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+};
+
+TEST_F(PredictorTest, getPredictedPlan_emptyLayersWithoutExactMatch_returnsNullopt) {
+ Predictor predictor;
+ EXPECT_FALSE(predictor.getPredictedPlan({}, 0));
+}
+
+TEST_F(PredictorTest, getPredictedPlan_recordCandidateAndRetrieveExactMatch) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne;
+ LayerFECompositionState layerFECompositionStateOne;
+ layerFECompositionStateOne.compositionType = hal::Composition::DEVICE;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ Plan plan;
+ plan.addLayerType(hal::Composition::DEVICE);
+
+ Predictor predictor;
+
+ NonBufferHash hash = getNonBufferHash({&layerStateOne});
+
+ predictor.recordResult(std::nullopt, hash, {&layerStateOne}, false, plan);
+
+ auto predictedPlan = predictor.getPredictedPlan({}, hash);
+ EXPECT_TRUE(predictedPlan);
+ Predictor::PredictedPlan expectedPlan{hash, plan, Prediction::Type::Exact};
+ EXPECT_EQ(expectedPlan, predictedPlan);
+}
+
+TEST_F(PredictorTest, getPredictedPlan_recordCandidateAndRetrieveApproximateMatch) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne{
+ .sourceCrop = sFloatRectOne,
+ };
+ LayerFECompositionState layerFECompositionStateOne;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo{
+ .sourceCrop = sFloatRectTwo,
+ };
+ LayerFECompositionState layerFECompositionStateTwo;
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ Plan plan;
+ plan.addLayerType(hal::Composition::DEVICE);
+
+ Predictor predictor;
+
+ NonBufferHash hashOne = getNonBufferHash({&layerStateOne});
+ NonBufferHash hashTwo = getNonBufferHash({&layerStateTwo});
+
+ predictor.recordResult(std::nullopt, hashOne, {&layerStateOne}, false, plan);
+
+ auto predictedPlan = predictor.getPredictedPlan({&layerStateTwo}, hashTwo);
+ EXPECT_TRUE(predictedPlan);
+ Predictor::PredictedPlan expectedPlan{hashOne, plan, Prediction::Type::Approximate};
+ EXPECT_EQ(expectedPlan, predictedPlan);
+}
+
+TEST_F(PredictorTest, recordMissedPlan_skipsApproximateMatch) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne{
+ .sourceCrop = sFloatRectOne,
+ };
+ LayerFECompositionState layerFECompositionStateOne;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo{
+ .sourceCrop = sFloatRectTwo,
+ };
+ LayerFECompositionState layerFECompositionStateTwo;
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ Plan plan;
+ plan.addLayerType(hal::Composition::DEVICE);
+
+ Predictor predictor;
+
+ NonBufferHash hashOne = getNonBufferHash({&layerStateOne});
+ NonBufferHash hashTwo = getNonBufferHash({&layerStateTwo});
+
+ predictor.recordResult(std::nullopt, hashOne, {&layerStateOne}, false, plan);
+
+ auto predictedPlan = predictor.getPredictedPlan({&layerStateTwo}, hashTwo);
+ ASSERT_TRUE(predictedPlan);
+ EXPECT_EQ(Prediction::Type::Approximate, predictedPlan->type);
+
+ Plan planTwo;
+ planTwo.addLayerType(hal::Composition::CLIENT);
+ predictor.recordResult(predictedPlan, hashTwo, {&layerStateTwo}, false, planTwo);
+ // Now trying to retrieve the predicted plan again returns a nullopt instead.
+ // TODO(b/158790260): Even though this is enforced in this test, we might want to reassess this.
+ // One of the implications around this implementation is that if we miss a prediction then we
+ // can never actually correct our mistake if we see the same layer stack again, which doesn't
+ // seem robust.
+ auto predictedPlanTwo = predictor.getPredictedPlan({&layerStateTwo}, hashTwo);
+ EXPECT_FALSE(predictedPlanTwo);
+}
+
+} // namespace
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 730f297..8692ee6 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -29,6 +29,7 @@
#include <compositionengine/DisplayColorProfileCreationArgs.h>
#include <compositionengine/DisplayCreationArgs.h>
#include <compositionengine/DisplaySurface.h>
+#include <compositionengine/ProjectionSpace.h>
#include <compositionengine/RenderSurface.h>
#include <compositionengine/RenderSurfaceCreationArgs.h>
#include <compositionengine/impl/OutputCompositionState.h>
@@ -50,25 +51,32 @@
ui::Transform::RotationFlags DisplayDevice::sPrimaryDisplayRotationFlags = ui::Transform::ROT_0;
DisplayDeviceCreationArgs::DisplayDeviceCreationArgs(
- const sp<SurfaceFlinger>& flinger, const wp<IBinder>& displayToken,
+ const sp<SurfaceFlinger>& flinger, HWComposer& hwComposer, const wp<IBinder>& displayToken,
std::shared_ptr<compositionengine::Display> compositionDisplay)
- : flinger(flinger), displayToken(displayToken), compositionDisplay(compositionDisplay) {}
+ : flinger(flinger),
+ hwComposer(hwComposer),
+ displayToken(displayToken),
+ compositionDisplay(compositionDisplay) {}
DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs& args)
: mFlinger(args.flinger),
+ mHwComposer(args.hwComposer),
mDisplayToken(args.displayToken),
mSequenceId(args.sequenceId),
mConnectionType(args.connectionType),
mCompositionDisplay{args.compositionDisplay},
mPhysicalOrientation(args.physicalOrientation),
+ mSupportedModes(std::move(args.supportedModes)),
mIsPrimary(args.isPrimary) {
mCompositionDisplay->editState().isSecure = args.isSecure;
mCompositionDisplay->createRenderSurface(
- compositionengine::RenderSurfaceCreationArgs{ANativeWindow_getWidth(
- args.nativeWindow.get()),
- ANativeWindow_getHeight(
- args.nativeWindow.get()),
- args.nativeWindow, args.displaySurface});
+ compositionengine::
+ RenderSurfaceCreationArgs{ANativeWindow_getWidth(args.nativeWindow.get()),
+ ANativeWindow_getHeight(args.nativeWindow.get()),
+ args.nativeWindow, args.displaySurface,
+ static_cast<size_t>(
+ SurfaceFlinger::
+ maxFrameBufferAcquiredBuffers)});
if (!mFlinger->mDisableClientCompositionCache &&
SurfaceFlinger::maxFrameBufferAcquiredBuffers > 0) {
@@ -101,11 +109,11 @@
}
int DisplayDevice::getWidth() const {
- return mCompositionDisplay->getState().bounds.getWidth();
+ return mCompositionDisplay->getState().displaySpace.bounds.getWidth();
}
int DisplayDevice::getHeight() const {
- return mCompositionDisplay->getState().bounds.getHeight();
+ return mCompositionDisplay->getState().displaySpace.bounds.getHeight();
}
void DisplayDevice::setDisplayName(const std::string& displayName) {
@@ -116,6 +124,10 @@
}
}
+void DisplayDevice::setDeviceProductInfo(std::optional<DeviceProductInfo> info) {
+ mDeviceProductInfo = std::move(info);
+}
+
uint32_t DisplayDevice::getPageFlipCount() const {
return mCompositionDisplay->getRenderSurface()->getPageFlipCount();
}
@@ -134,12 +146,65 @@
return mPowerMode != hal::PowerMode::OFF;
}
-void DisplayDevice::setActiveConfig(HwcConfigIndexType mode) {
- mActiveConfig = mode;
+void DisplayDevice::setActiveMode(DisplayModeId id) {
+ const auto mode = getMode(id);
+ LOG_FATAL_IF(!mode, "Cannot set active mode which is not supported.");
+ mActiveMode = mode;
}
-HwcConfigIndexType DisplayDevice::getActiveConfig() const {
- return mActiveConfig;
+status_t DisplayDevice::initiateModeChange(DisplayModeId modeId,
+ const hal::VsyncPeriodChangeConstraints& constraints,
+ hal::VsyncPeriodChangeTimeline* outTimeline) const {
+ const auto mode = getMode(modeId);
+ if (!mode) {
+ ALOGE("Trying to initiate a mode change to invalid mode %s on display %s",
+ std::to_string(modeId.value()).c_str(), to_string(getId()).c_str());
+ return BAD_VALUE;
+ }
+ return mHwComposer.setActiveModeWithConstraints(getPhysicalId(), mode->getHwcId(), constraints,
+ outTimeline);
+}
+
+const DisplayModePtr& DisplayDevice::getActiveMode() const {
+ return mActiveMode;
+}
+
+const DisplayModes& DisplayDevice::getSupportedModes() const {
+ return mSupportedModes;
+}
+
+DisplayModePtr DisplayDevice::getMode(DisplayModeId modeId) const {
+ const auto it = std::find_if(mSupportedModes.begin(), mSupportedModes.end(),
+ [&](DisplayModePtr mode) { return mode->getId() == modeId; });
+ if (it != mSupportedModes.end()) {
+ return *it;
+ }
+ return nullptr;
+}
+
+nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const {
+ const auto physicalId = getPhysicalId();
+ if (!mHwComposer.isConnected(physicalId)) {
+ return 0;
+ }
+
+ nsecs_t vsyncPeriod;
+ const auto status = mHwComposer.getDisplayVsyncPeriod(physicalId, &vsyncPeriod);
+ if (status == NO_ERROR) {
+ return vsyncPeriod;
+ }
+
+ return getActiveMode()->getFps().getPeriodNsecs();
+}
+
+nsecs_t DisplayDevice::getRefreshTimestamp() const {
+ const nsecs_t now = systemTime(CLOCK_MONOTONIC);
+ const auto vsyncPeriodNanos = getVsyncPeriodFromHWC();
+ return now - ((now - mLastHwVsync) % vsyncPeriodNanos);
+}
+
+void DisplayDevice::onVsync(nsecs_t timestamp) {
+ mLastHwVsync = timestamp;
}
ui::Dataspace DisplayDevice::getCompositionDataSpace() const {
@@ -151,96 +216,38 @@
}
void DisplayDevice::setDisplaySize(int width, int height) {
- mCompositionDisplay->setBounds(ui::Size(width, height));
+ LOG_FATAL_IF(!isVirtual(), "Changing the display size is supported only for virtual displays.");
+ mCompositionDisplay->setDisplaySize(ui::Size(width, height));
}
-void DisplayDevice::setProjection(ui::Rotation orientation, Rect viewport, Rect frame) {
+void DisplayDevice::setProjection(ui::Rotation orientation, Rect layerStackSpaceRect,
+ Rect orientedDisplaySpaceRect) {
mOrientation = orientation;
- const Rect& displayBounds = getCompositionDisplay()->getState().bounds;
- const int displayWidth = displayBounds.width();
- const int displayHeight = displayBounds.height();
-
- ui::Transform rotation;
- if (const auto flags = ui::Transform::toRotationFlags(orientation);
- flags != ui::Transform::ROT_INVALID) {
- rotation.set(flags, displayWidth, displayHeight);
- }
-
- if (!frame.isValid()) {
- // the destination frame can be invalid if it has never been set,
- // in that case we assume the whole display frame.
- frame = Rect(displayWidth, displayHeight);
- }
-
- if (viewport.isEmpty()) {
- // viewport can be invalid if it has never been set, in that case
- // we assume the whole display size.
- // it's also invalid to have an empty viewport, so we handle that
- // case in the same way.
- viewport = Rect(displayWidth, displayHeight);
- if (rotation.getOrientation() & ui::Transform::ROT_90) {
- // viewport is always specified in the logical orientation
- // of the display (ie: post-rotation).
- std::swap(viewport.right, viewport.bottom);
- }
- }
-
- ui::Transform logicalTranslation, physicalTranslation, scale;
- const float sourceWidth = viewport.width();
- const float sourceHeight = viewport.height();
- const float destWidth = frame.width();
- const float destHeight = frame.height();
- if (sourceWidth != destWidth || sourceHeight != destHeight) {
- const float scaleX = destWidth / sourceWidth;
- const float scaleY = destHeight / sourceHeight;
- scale.set(scaleX, 0, 0, scaleY);
- }
-
- const float sourceX = viewport.left;
- const float sourceY = viewport.top;
- const float destX = frame.left;
- const float destY = frame.top;
- logicalTranslation.set(-sourceX, -sourceY);
- physicalTranslation.set(destX, destY);
-
- // need to take care of primary display rotation for globalTransform
- // for case if the panel is not installed aligned with device orientation
- if (isPrimary()) {
- if (const auto flags = ui::Transform::toRotationFlags(orientation + mPhysicalOrientation);
- flags != ui::Transform::ROT_INVALID) {
- rotation.set(flags, displayWidth, displayHeight);
- }
- }
-
- // The viewport and frame are both in the logical orientation.
- // Apply the logical translation, scale to physical size, apply the
- // physical translation and finally rotate to the physical orientation.
- ui::Transform globalTransform = rotation * physicalTranslation * scale * logicalTranslation;
-
- const uint8_t type = globalTransform.getType();
- const bool needsFiltering =
- (!globalTransform.preserveRects() || (type >= ui::Transform::SCALE));
-
- const Rect& sourceClip = viewport;
- Rect destinationClip = globalTransform.transform(viewport);
- if (destinationClip.isEmpty()) {
- destinationClip = displayBounds;
- }
- // Make sure the destination clip is contained in the display bounds
- destinationClip.intersect(displayBounds, &destinationClip);
-
- uint32_t transformOrientation;
-
if (isPrimary()) {
sPrimaryDisplayRotationFlags = ui::Transform::toRotationFlags(orientation);
- transformOrientation = ui::Transform::toRotationFlags(orientation + mPhysicalOrientation);
- } else {
- transformOrientation = ui::Transform::toRotationFlags(orientation);
}
- getCompositionDisplay()->setProjection(globalTransform, transformOrientation, frame, viewport,
- sourceClip, destinationClip, needsFiltering);
+ if (!orientedDisplaySpaceRect.isValid()) {
+ // The destination frame can be invalid if it has never been set,
+ // in that case we assume the whole display size.
+ orientedDisplaySpaceRect = getCompositionDisplay()->getState().displaySpace.bounds;
+ }
+
+ if (layerStackSpaceRect.isEmpty()) {
+ // The layerStackSpaceRect can be invalid if it has never been set, in that case
+ // we assume the whole framebuffer size.
+ layerStackSpaceRect = getCompositionDisplay()->getState().framebufferSpace.bounds;
+ if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
+ std::swap(layerStackSpaceRect.right, layerStackSpaceRect.bottom);
+ }
+ }
+
+ // We need to take care of display rotation for globalTransform for case if the panel is not
+ // installed aligned with device orientation.
+ const auto transformOrientation = orientation + mPhysicalOrientation;
+ getCompositionDisplay()->setProjection(transformOrientation, layerStackSpaceRect,
+ orientedDisplaySpaceRect);
}
ui::Transform::RotationFlags DisplayDevice::getPrimaryDisplayRotationFlags() {
@@ -248,27 +255,36 @@
}
std::string DisplayDevice::getDebugName() const {
- std::string displayId;
- if (const auto id = getId()) {
- displayId = to_string(*id) + ", ";
- }
-
const char* type = "virtual";
if (mConnectionType) {
- type = *mConnectionType == DisplayConnectionType::Internal ? "internal" : "external";
+ type = *mConnectionType == ui::DisplayConnectionType::Internal ? "internal" : "external";
}
- return base::StringPrintf("DisplayDevice{%s%s%s, \"%s\"}", displayId.c_str(), type,
+ return base::StringPrintf("DisplayDevice{%s, %s%s, \"%s\"}", to_string(getId()).c_str(), type,
isPrimary() ? ", primary" : "", mDisplayName.c_str());
}
void DisplayDevice::dump(std::string& result) const {
StringAppendF(&result, "+ %s\n", getDebugName().c_str());
-
- result.append(" ");
- StringAppendF(&result, "powerMode=%s (%d), ", to_string(mPowerMode).c_str(),
+ StringAppendF(&result, " powerMode=%s (%d)\n", to_string(mPowerMode).c_str(),
static_cast<int32_t>(mPowerMode));
- StringAppendF(&result, "activeConfig=%d, ", mActiveConfig.value());
+ StringAppendF(&result, " activeMode=%s\n",
+ mSupportedModes.size() ? to_string(*getActiveMode()).c_str() : "none");
+
+ result.append(" supportedModes=\n");
+
+ for (const auto& mode : mSupportedModes) {
+ result.append(" ");
+ result.append(to_string(*mode));
+ result.append("\n");
+ }
+ StringAppendF(&result, " deviceProductInfo=");
+ if (mDeviceProductInfo) {
+ mDeviceProductInfo->dump(result);
+ } else {
+ result.append("{}");
+ }
+ result.append("\n");
getCompositionDisplay()->dump(result);
}
@@ -276,9 +292,7 @@
return mCompositionDisplay->getDisplayColorProfile()->hasRenderIntent(intent);
}
-// ----------------------------------------------------------------------------
-
-const std::optional<DisplayId>& DisplayDevice::getId() const {
+DisplayId DisplayDevice::getId() const {
return mCompositionDisplay->getId();
}
@@ -287,7 +301,7 @@
}
const Rect& DisplayDevice::getBounds() const {
- return mCompositionDisplay->getState().bounds;
+ return mCompositionDisplay->getState().displaySpace.bounds;
}
const Region& DisplayDevice::getUndefinedRegion() const {
@@ -302,20 +316,20 @@
return mCompositionDisplay->getState().layerStackId;
}
+ui::Transform::RotationFlags DisplayDevice::getTransformHint() const {
+ return mCompositionDisplay->getTransformHint();
+}
+
const ui::Transform& DisplayDevice::getTransform() const {
return mCompositionDisplay->getState().transform;
}
-const Rect& DisplayDevice::getViewport() const {
- return mCompositionDisplay->getState().viewport;
+const Rect& DisplayDevice::getLayerStackSpaceRect() const {
+ return mCompositionDisplay->getState().layerStackSpace.content;
}
-const Rect& DisplayDevice::getFrame() const {
- return mCompositionDisplay->getState().frame;
-}
-
-const Rect& DisplayDevice::getSourceClip() const {
- return mCompositionDisplay->getState().sourceClip;
+const Rect& DisplayDevice::getOrientedDisplaySpaceRect() const {
+ return mCompositionDisplay->getState().orientedDisplaySpace.content;
}
bool DisplayDevice::hasWideColorGamut() const {
@@ -342,8 +356,20 @@
return mCompositionDisplay->getDisplayColorProfile()->getSupportedPerFrameMetadata();
}
-const HdrCapabilities& DisplayDevice::getHdrCapabilities() const {
- return mCompositionDisplay->getDisplayColorProfile()->getHdrCapabilities();
+void DisplayDevice::overrideHdrTypes(const std::vector<ui::Hdr>& hdrTypes) {
+ mOverrideHdrTypes = hdrTypes;
+}
+
+HdrCapabilities DisplayDevice::getHdrCapabilities() const {
+ const HdrCapabilities& capabilities =
+ mCompositionDisplay->getDisplayColorProfile()->getHdrCapabilities();
+ std::vector<ui::Hdr> hdrTypes = capabilities.getSupportedHdrTypes();
+ if (!mOverrideHdrTypes.empty()) {
+ hdrTypes = mOverrideHdrTypes;
+ }
+ return HdrCapabilities(hdrTypes, capabilities.getDesiredMaxLuminance(),
+ capabilities.getDesiredMaxAverageLuminance(),
+ capabilities.getDesiredMinLuminance());
}
std::atomic<int32_t> DisplayDeviceState::sNextSequenceId(1);
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index ca7119e..68846d3 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -27,21 +27,22 @@
#include <math/mat4.h>
#include <renderengine/RenderEngine.h>
#include <system/window.h>
-#include <ui/DisplayInfo.h>
+#include <ui/DisplayId.h>
#include <ui/DisplayState.h>
#include <ui/GraphicTypes.h>
#include <ui/HdrCapabilities.h>
#include <ui/Region.h>
+#include <ui/StaticDisplayInfo.h>
#include <ui/Transform.h>
+#include <utils/Errors.h>
#include <utils/Mutex.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>
#include "DisplayHardware/DisplayIdentification.h"
+#include "DisplayHardware/DisplayMode.h"
#include "DisplayHardware/Hal.h"
#include "DisplayHardware/PowerAdvisor.h"
-#include "RenderArea.h"
-#include "Scheduler/HwcStrongTypes.h"
namespace android {
@@ -59,19 +60,21 @@
class DisplaySurface;
} // namespace compositionengine
-class DisplayDevice : public LightRefBase<DisplayDevice> {
+class DisplayDevice : public RefBase {
public:
constexpr static float sDefaultMinLumiance = 0.0;
constexpr static float sDefaultMaxLumiance = 500.0;
explicit DisplayDevice(DisplayDeviceCreationArgs& args);
+
+ // Must be destroyed on the main thread because it may call into HWComposer.
virtual ~DisplayDevice();
std::shared_ptr<compositionengine::Display> getCompositionDisplay() const {
return mCompositionDisplay;
}
- std::optional<DisplayConnectionType> getConnectionType() const { return mConnectionType; }
+ std::optional<ui::DisplayConnectionType> getConnectionType() const { return mConnectionType; }
bool isVirtual() const { return !mConnectionType; }
bool isPrimary() const { return mIsPrimary; }
@@ -93,18 +96,22 @@
static ui::Transform::RotationFlags getPrimaryDisplayRotationFlags();
- ui::Transform::RotationFlags getTransformHint() const {
- return static_cast<ui::Transform::RotationFlags>(getTransform().getOrientation());
- }
-
+ ui::Transform::RotationFlags getTransformHint() const;
const ui::Transform& getTransform() const;
- const Rect& getViewport() const;
- const Rect& getFrame() const;
- const Rect& getSourceClip() const;
+ const Rect& getLayerStackSpaceRect() const;
+ const Rect& getOrientedDisplaySpaceRect() const;
bool needsFiltering() const;
ui::LayerStack getLayerStack() const;
- const std::optional<DisplayId>& getId() const;
+ // Returns the physical ID of this display. This function asserts the ID is physical and it
+ // shouldn't be called for other display types, e.g. virtual.
+ PhysicalDisplayId getPhysicalId() const {
+ const auto displayIdOpt = PhysicalDisplayId::tryCast(getId());
+ LOG_FATAL_IF(!displayIdOpt);
+ return *displayIdOpt;
+ }
+
+ DisplayId getId() const;
const wp<IBinder>& getDisplayToken() const { return mDisplayToken; }
int32_t getSequenceId() const { return mSequenceId; }
@@ -119,13 +126,15 @@
bool hasHLGSupport() const;
bool hasDolbyVisionSupport() const;
+ void overrideHdrTypes(const std::vector<ui::Hdr>& hdrTypes);
+
// The returned HdrCapabilities is the combination of HDR capabilities from
// hardware composer and RenderEngine. When the DisplayDevice supports wide
// color gamut, RenderEngine is able to simulate HDR support in Display P3
// color space for both PQ and HLG HDR contents. The minimum and maximum
// luminance will be set to sDefaultMinLumiance and sDefaultMaxLumiance
// respectively if hardware composer doesn't return meaningful values.
- const HdrCapabilities& getHdrCapabilities() const;
+ HdrCapabilities getHdrCapabilities() const;
// Return true if intent is supported by the display.
bool hasRenderIntent(ui::RenderIntent intent) const;
@@ -136,6 +145,11 @@
void setDisplayName(const std::string& displayName);
const std::string& getDisplayName() const { return mDisplayName; }
+ void setDeviceProductInfo(std::optional<DeviceProductInfo> info);
+ const std::optional<DeviceProductInfo>& getDeviceProductInfo() const {
+ return mDeviceProductInfo;
+ }
+
/* ------------------------------------------------------------------------
* Display power mode management.
*/
@@ -146,10 +160,27 @@
ui::Dataspace getCompositionDataSpace() const;
/* ------------------------------------------------------------------------
- * Display active config management.
+ * Display mode management.
*/
- HwcConfigIndexType getActiveConfig() const;
- void setActiveConfig(HwcConfigIndexType mode);
+ const DisplayModePtr& getActiveMode() const;
+ void setActiveMode(DisplayModeId);
+ status_t initiateModeChange(DisplayModeId modeId,
+ const hal::VsyncPeriodChangeConstraints& constraints,
+ hal::VsyncPeriodChangeTimeline* outTimeline) const;
+
+ // Return the immutable list of supported display modes. The HWC may report different modes
+ // after a hotplug reconnect event, in which case the DisplayDevice object will be recreated.
+ // Hotplug reconnects are common for external displays.
+ const DisplayModes& getSupportedModes() const;
+
+ // Returns nullptr if the given mode ID is not supported. A previously
+ // supported mode may be no longer supported for some devices like TVs and
+ // set-top boxes after a hotplug reconnect.
+ DisplayModePtr getMode(DisplayModeId) const;
+
+ void onVsync(nsecs_t timestamp);
+ nsecs_t getVsyncPeriodFromHWC() const;
+ nsecs_t getRefreshTimestamp() const;
// release HWC resources (if any) for removable displays
void disconnect();
@@ -163,9 +194,10 @@
private:
const sp<SurfaceFlinger> mFlinger;
+ HWComposer& mHwComposer;
const wp<IBinder> mDisplayToken;
const int32_t mSequenceId;
- const std::optional<DisplayConnectionType> mConnectionType;
+ const std::optional<ui::DisplayConnectionType> mConnectionType;
const std::shared_ptr<compositionengine::Display> mCompositionDisplay;
@@ -178,17 +210,27 @@
hardware::graphics::composer::hal::PowerMode mPowerMode =
hardware::graphics::composer::hal::PowerMode::OFF;
- HwcConfigIndexType mActiveConfig;
+ DisplayModePtr mActiveMode;
+ const DisplayModes mSupportedModes;
+
+ std::atomic<nsecs_t> mLastHwVsync = 0;
// TODO(b/74619554): Remove special cases for primary display.
const bool mIsPrimary;
+
+ std::optional<DeviceProductInfo> mDeviceProductInfo;
+
+ std::vector<ui::Hdr> mOverrideHdrTypes;
};
struct DisplayDeviceState {
struct Physical {
- DisplayId id;
- DisplayConnectionType type;
+ PhysicalDisplayId id;
+ ui::DisplayConnectionType type;
hardware::graphics::composer::hal::HWDisplayId hwcDisplayId;
+ std::optional<DeviceProductInfo> deviceProductInfo;
+ DisplayModes supportedModes;
+ DisplayModePtr activeMode;
bool operator==(const Physical& other) const {
return id == other.id && type == other.type && hwcDisplayId == other.hwcDisplayId;
@@ -201,8 +243,8 @@
std::optional<Physical> physical;
sp<IGraphicBufferProducer> surface;
ui::LayerStack layerStack = ui::NO_LAYER_STACK;
- Rect viewport;
- Rect frame;
+ Rect layerStackSpaceRect;
+ Rect orientedDisplaySpaceRect;
ui::Rotation orientation = ui::ROTATION_0;
uint32_t width = 0;
uint32_t height = 0;
@@ -216,14 +258,16 @@
struct DisplayDeviceCreationArgs {
// We use a constructor to ensure some of the values are set, without
// assuming a default value.
- DisplayDeviceCreationArgs(const sp<SurfaceFlinger>&, const wp<IBinder>& displayToken,
+ DisplayDeviceCreationArgs(const sp<SurfaceFlinger>&, HWComposer& hwComposer,
+ const wp<IBinder>& displayToken,
std::shared_ptr<compositionengine::Display>);
const sp<SurfaceFlinger> flinger;
+ HWComposer& hwComposer;
const wp<IBinder> displayToken;
const std::shared_ptr<compositionengine::Display> compositionDisplay;
int32_t sequenceId{0};
- std::optional<DisplayConnectionType> connectionType;
+ std::optional<ui::DisplayConnectionType> connectionType;
bool isSecure{false};
sp<ANativeWindow> nativeWindow;
sp<compositionengine::DisplaySurface> displaySurface;
@@ -235,120 +279,7 @@
hardware::graphics::composer::hal::PowerMode initialPowerMode{
hardware::graphics::composer::hal::PowerMode::ON};
bool isPrimary{false};
-};
-
-class DisplayRenderArea : public RenderArea {
-public:
- DisplayRenderArea(const sp<const DisplayDevice>& display,
- RotationFlags rotation = ui::Transform::ROT_0)
- : DisplayRenderArea(display, display->getBounds(),
- static_cast<uint32_t>(display->getWidth()),
- static_cast<uint32_t>(display->getHeight()),
- display->getCompositionDataSpace(), rotation) {}
-
- DisplayRenderArea(sp<const DisplayDevice> display, const Rect& sourceCrop, uint32_t reqWidth,
- uint32_t reqHeight, ui::Dataspace reqDataSpace, RotationFlags rotation,
- bool allowSecureLayers = true)
- : RenderArea(reqWidth, reqHeight, CaptureFill::OPAQUE, reqDataSpace,
- display->getViewport(), applyDeviceOrientation(rotation, display)),
- mDisplay(std::move(display)),
- mSourceCrop(sourceCrop),
- mAllowSecureLayers(allowSecureLayers) {}
-
- const ui::Transform& getTransform() const override { return mTransform; }
- Rect getBounds() const override { return mDisplay->getBounds(); }
- int getHeight() const override { return mDisplay->getHeight(); }
- int getWidth() const override { return mDisplay->getWidth(); }
- bool isSecure() const override { return mAllowSecureLayers && mDisplay->isSecure(); }
- sp<const DisplayDevice> getDisplayDevice() const override { return mDisplay; }
-
- bool needsFiltering() const override {
- // check if the projection from the logical render area
- // to the physical render area requires filtering
- const Rect& sourceCrop = getSourceCrop();
- int width = sourceCrop.width();
- int height = sourceCrop.height();
- if (getRotationFlags() & ui::Transform::ROT_90) {
- std::swap(width, height);
- }
- return width != getReqWidth() || height != getReqHeight();
- }
-
- Rect getSourceCrop() const override {
- // use the projected display viewport by default.
- if (mSourceCrop.isEmpty()) {
- return mDisplay->getSourceClip();
- }
-
- // If there is a source crop provided then it is assumed that the device
- // was in portrait orientation. This may not logically be true, so
- // correct for the orientation error by undoing the rotation
-
- ui::Rotation logicalOrientation = mDisplay->getOrientation();
- if (logicalOrientation == ui::Rotation::Rotation90) {
- logicalOrientation = ui::Rotation::Rotation270;
- } else if (logicalOrientation == ui::Rotation::Rotation270) {
- logicalOrientation = ui::Rotation::Rotation90;
- }
-
- const auto flags = ui::Transform::toRotationFlags(logicalOrientation);
- int width = mDisplay->getSourceClip().getWidth();
- int height = mDisplay->getSourceClip().getHeight();
- ui::Transform rotation;
- rotation.set(flags, width, height);
- return rotation.transform(mSourceCrop);
- }
-
-private:
- static RotationFlags applyDeviceOrientation(RotationFlags orientationFlag,
- const sp<const DisplayDevice>& device) {
- uint32_t inverseRotate90 = 0;
- uint32_t inverseReflect = 0;
-
- // Reverse the logical orientation.
- ui::Rotation logicalOrientation = device->getOrientation();
- if (logicalOrientation == ui::Rotation::Rotation90) {
- logicalOrientation = ui::Rotation::Rotation270;
- } else if (logicalOrientation == ui::Rotation::Rotation270) {
- logicalOrientation = ui::Rotation::Rotation90;
- }
-
- const ui::Rotation orientation = logicalOrientation;
-
- switch (orientation) {
- case ui::ROTATION_0:
- return orientationFlag;
-
- case ui::ROTATION_90:
- inverseRotate90 = ui::Transform::ROT_90;
- inverseReflect = ui::Transform::ROT_180;
- break;
-
- case ui::ROTATION_180:
- inverseReflect = ui::Transform::ROT_180;
- break;
-
- case ui::ROTATION_270:
- inverseRotate90 = ui::Transform::ROT_90;
- break;
- }
-
- const uint32_t rotate90 = orientationFlag & ui::Transform::ROT_90;
- uint32_t reflect = orientationFlag & ui::Transform::ROT_180;
-
- // Apply reflection for double rotation.
- if (rotate90 & inverseRotate90) {
- reflect = ~reflect & ui::Transform::ROT_180;
- }
-
- return static_cast<RotationFlags>((rotate90 ^ inverseRotate90) |
- (reflect ^ inverseReflect));
- }
-
- const sp<const DisplayDevice> mDisplay;
- const Rect mSourceCrop;
- const bool mAllowSecureLayers;
- const ui::Transform mTransform = ui::Transform();
+ DisplayModes supportedModes;
};
} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index 1bf43da..fd70988 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -20,11 +20,7 @@
#undef LOG_TAG
#define LOG_TAG "HwcComposer"
-
-#include <log/log.h>
-
-#include <algorithm>
-#include <cinttypes>
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "ComposerHal.h"
@@ -32,6 +28,11 @@
#include <gui/BufferQueue.h>
#include <hidl/HidlTransportSupport.h>
#include <hidl/HidlTransportUtils.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+#include <algorithm>
+#include <cinttypes>
namespace android {
@@ -492,6 +493,7 @@
Error Composer::presentDisplay(Display display, int* outPresentFence)
{
+ ATRACE_NAME("HwcPresentDisplay");
mWriter.selectDisplay(display);
mWriter.presentDisplay();
@@ -586,6 +588,7 @@
Error Composer::validateDisplay(Display display, uint32_t* outNumTypes,
uint32_t* outNumRequests)
{
+ ATRACE_NAME("HwcValidateDisplay");
mWriter.selectDisplay(display);
mWriter.validateDisplay();
@@ -601,13 +604,14 @@
Error Composer::presentOrValidateDisplay(Display display, uint32_t* outNumTypes,
uint32_t* outNumRequests, int* outPresentFence, uint32_t* state) {
- mWriter.selectDisplay(display);
- mWriter.presentOrvalidateDisplay();
+ ATRACE_NAME("HwcPresentOrValidateDisplay");
+ mWriter.selectDisplay(display);
+ mWriter.presentOrvalidateDisplay();
- Error error = execute();
- if (error != Error::NONE) {
- return error;
- }
+ Error error = execute();
+ if (error != Error::NONE) {
+ return error;
+ }
mReader.takePresentOrValidateStage(display, state);
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 5b66809..c756d65 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -26,6 +26,7 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
#include <android/hardware/graphics/common/1.1/types.h>
#include <android/hardware/graphics/composer/2.4/IComposer.h>
@@ -38,7 +39,7 @@
#include <utils/StrongPointer.h>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
namespace android {
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
index 4dfc743..98209bb 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#undef LOG_TAG
#define LOG_TAG "DisplayIdentification"
@@ -38,7 +34,6 @@
constexpr size_t kEdidBlockSize = 128;
constexpr size_t kEdidHeaderLength = 5;
-constexpr uint16_t kFallbackEdidManufacturerId = 0;
constexpr uint16_t kVirtualEdidManufacturerId = 0xffffu;
std::optional<uint8_t> getEdidDescriptorType(const byte_view& view) {
@@ -71,12 +66,8 @@
DeviceProductInfo buildDeviceProductInfo(const Edid& edid) {
DeviceProductInfo info;
- std::copy(edid.displayName.begin(), edid.displayName.end(), info.name.begin());
- info.name[edid.displayName.size()] = '\0';
-
- const auto productId = std::to_string(edid.productId);
- std::copy(productId.begin(), productId.end(), info.productId.begin());
- info.productId[productId.size()] = '\0';
+ info.name.assign(edid.displayName);
+ info.productId = std::to_string(edid.productId);
info.manufacturerPnpId = edid.pnpId;
constexpr uint8_t kModelYearFlag = 0xff;
@@ -99,8 +90,6 @@
if (edid.cea861Block && edid.cea861Block->hdmiVendorDataBlock) {
const auto& address = edid.cea861Block->hdmiVendorDataBlock->physicalAddress;
info.relativeAddress = {address.a, address.b, address.c, address.d};
- } else {
- info.relativeAddress = DeviceProductInfo::NO_RELATIVE_ADDRESS;
}
return info;
}
@@ -132,8 +121,8 @@
constexpr uint8_t kVendorSpecificDataBlockTag = 0x3;
if (tag == kVendorSpecificDataBlockTag) {
- const uint32_t ieeeRegistrationId =
- dataBlock[1] | (dataBlock[2] << 8) | (dataBlock[3] << 16);
+ const uint32_t ieeeRegistrationId = static_cast<uint32_t>(
+ dataBlock[1] | (dataBlock[2] << 8) | (dataBlock[3] << 16));
constexpr uint32_t kHdmiIeeeRegistrationId = 0xc03;
if (ieeeRegistrationId == kHdmiIeeeRegistrationId) {
@@ -158,14 +147,6 @@
} // namespace
-uint16_t DisplayId::manufacturerId() const {
- return static_cast<uint16_t>(value >> 40);
-}
-
-DisplayId DisplayId::fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t modelHash) {
- return {(static_cast<Type>(manufacturerId) << 40) | (static_cast<Type>(modelHash) << 8) | port};
-}
-
bool isEdid(const DisplayIdentificationData& data) {
const uint8_t kMagic[] = {0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0};
return data.size() >= sizeof(kMagic) &&
@@ -190,7 +171,7 @@
// Plug and play ID encoded as big-endian 16-bit value.
const uint16_t manufacturerId =
- (edid[kManufacturerOffset] << 8) | edid[kManufacturerOffset + 1];
+ static_cast<uint16_t>((edid[kManufacturerOffset] << 8) | edid[kManufacturerOffset + 1]);
const auto pnpId = getPnpId(manufacturerId);
if (!pnpId) {
@@ -203,7 +184,8 @@
ALOGE("Invalid EDID: product ID is truncated.");
return {};
}
- const uint16_t productId = edid[kProductIdOffset] | (edid[kProductIdOffset + 1] << 8);
+ const uint16_t productId =
+ static_cast<uint16_t>(edid[kProductIdOffset] | (edid[kProductIdOffset + 1] << 8));
constexpr size_t kManufactureWeekOffset = 16;
if (edid.size() < kManufactureWeekOffset + sizeof(uint8_t)) {
@@ -238,7 +220,6 @@
constexpr size_t kDescriptorCount = 4;
constexpr size_t kDescriptorLength = 18;
- static_assert(kDescriptorLength - kEdidHeaderLength < DeviceProductInfo::TEXT_BUFFER_SIZE);
for (size_t i = 0; i < kDescriptorCount; i++) {
if (view.size() < kDescriptorLength) {
@@ -330,8 +311,8 @@
return a && b && c ? std::make_optional(PnpId{a, b, c}) : std::nullopt;
}
-std::optional<PnpId> getPnpId(DisplayId displayId) {
- return getPnpId(displayId.manufacturerId());
+std::optional<PnpId> getPnpId(PhysicalDisplayId displayId) {
+ return getPnpId(displayId.getManufacturerId());
}
std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData(
@@ -346,21 +327,15 @@
return {};
}
- const auto displayId = DisplayId::fromEdid(port, edid->manufacturerId, edid->modelHash);
+ const auto displayId = PhysicalDisplayId::fromEdid(port, edid->manufacturerId, edid->modelHash);
return DisplayIdentificationInfo{.id = displayId,
.name = std::string(edid->displayName),
.deviceProductInfo = buildDeviceProductInfo(*edid)};
}
-DisplayId getFallbackDisplayId(uint8_t port) {
- return DisplayId::fromEdid(port, kFallbackEdidManufacturerId, 0);
-}
-
-DisplayId getVirtualDisplayId(uint32_t id) {
- return DisplayId::fromEdid(0, kVirtualEdidManufacturerId, id);
+PhysicalDisplayId getVirtualDisplayId(uint32_t id) {
+ return PhysicalDisplayId::fromEdid(0, kVirtualEdidManufacturerId, id);
}
} // namespace android
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
index 4819d1d..fbea4e5 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 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.
@@ -24,38 +24,18 @@
#include <vector>
#include <ui/DeviceProductInfo.h>
-#include <ui/PhysicalDisplayId.h>
+#include <ui/DisplayId.h>
#define LEGACY_DISPLAY_TYPE_PRIMARY 0
#define LEGACY_DISPLAY_TYPE_EXTERNAL 1
namespace android {
-struct DisplayId {
- using Type = PhysicalDisplayId;
- Type value;
-
- uint16_t manufacturerId() const;
-
- static DisplayId fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t modelHash);
-};
-
-inline bool operator==(DisplayId lhs, DisplayId rhs) {
- return lhs.value == rhs.value;
-}
-
-inline bool operator!=(DisplayId lhs, DisplayId rhs) {
- return !(lhs == rhs);
-}
-
-inline std::string to_string(DisplayId displayId) {
- return std::to_string(displayId.value);
-}
using DisplayIdentificationData = std::vector<uint8_t>;
struct DisplayIdentificationInfo {
- DisplayId id;
+ PhysicalDisplayId id;
std::string name;
std::optional<DeviceProductInfo> deviceProductInfo;
};
@@ -94,23 +74,12 @@
bool isEdid(const DisplayIdentificationData&);
std::optional<Edid> parseEdid(const DisplayIdentificationData&);
std::optional<PnpId> getPnpId(uint16_t manufacturerId);
-std::optional<PnpId> getPnpId(DisplayId);
+std::optional<PnpId> getPnpId(PhysicalDisplayId);
std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData(
uint8_t port, const DisplayIdentificationData&);
-DisplayId getFallbackDisplayId(uint8_t port);
-DisplayId getVirtualDisplayId(uint32_t id);
+PhysicalDisplayId getVirtualDisplayId(uint32_t id);
} // namespace android
-namespace std {
-
-template <>
-struct hash<android::DisplayId> {
- size_t operator()(android::DisplayId displayId) const {
- return hash<android::DisplayId::Type>()(displayId.value);
- }
-};
-
-} // namespace std
diff --git a/services/surfaceflinger/DisplayHardware/DisplayMode.h b/services/surfaceflinger/DisplayHardware/DisplayMode.h
new file mode 100644
index 0000000..85cc993
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include "DisplayHardware/Hal.h"
+#include "Fps.h"
+#include "Scheduler/StrongTyping.h"
+
+#include <android-base/stringprintf.h>
+#include <android/configuration.h>
+#include <ui/DisplayMode.h>
+#include <ui/Size.h>
+#include <utils/Timers.h>
+
+#include <cstddef>
+#include <memory>
+#include <vector>
+
+namespace android {
+
+namespace hal = android::hardware::graphics::composer::hal;
+
+class DisplayMode;
+using DisplayModePtr = std::shared_ptr<const DisplayMode>;
+using DisplayModes = std::vector<DisplayModePtr>;
+using DisplayModeId = StrongTyping<ui::DisplayModeId, struct DisplayModeIdTag, Compare, Hash>;
+
+class DisplayMode {
+public:
+ class Builder {
+ public:
+ explicit Builder(hal::HWConfigId id) : mDisplayMode(new DisplayMode(id)) {}
+
+ DisplayModePtr build() {
+ return std::const_pointer_cast<const DisplayMode>(std::move(mDisplayMode));
+ }
+
+ Builder& setId(DisplayModeId id) {
+ mDisplayMode->mId = id;
+ return *this;
+ }
+
+ Builder& setWidth(int32_t width) {
+ mDisplayMode->mWidth = width;
+ return *this;
+ }
+
+ Builder& setHeight(int32_t height) {
+ mDisplayMode->mHeight = height;
+ return *this;
+ }
+
+ Builder& setVsyncPeriod(int32_t vsyncPeriod) {
+ mDisplayMode->mFps = Fps::fromPeriodNsecs(vsyncPeriod);
+ return *this;
+ }
+
+ Builder& setDpiX(int32_t dpiX) {
+ if (dpiX == -1) {
+ mDisplayMode->mDpiX = getDefaultDensity();
+ } else {
+ mDisplayMode->mDpiX = dpiX / 1000.0f;
+ }
+ return *this;
+ }
+
+ Builder& setDpiY(int32_t dpiY) {
+ if (dpiY == -1) {
+ mDisplayMode->mDpiY = getDefaultDensity();
+ } else {
+ mDisplayMode->mDpiY = dpiY / 1000.0f;
+ }
+ return *this;
+ }
+
+ Builder& setGroup(int32_t group) {
+ mDisplayMode->mGroup = group;
+ return *this;
+ }
+
+ private:
+ float getDefaultDensity() {
+ // Default density is based on TVs: 1080p displays get XHIGH density, lower-
+ // resolution displays get TV density. Maybe eventually we'll need to update
+ // it for 4k displays, though hopefully those will just report accurate DPI
+ // information to begin with. This is also used for virtual displays and
+ // older HWC implementations, so be careful about orientation.
+
+ auto longDimension = std::max(mDisplayMode->mWidth, mDisplayMode->mHeight);
+ if (longDimension >= 1080) {
+ return ACONFIGURATION_DENSITY_XHIGH;
+ } else {
+ return ACONFIGURATION_DENSITY_TV;
+ }
+ }
+ std::shared_ptr<DisplayMode> mDisplayMode;
+ };
+
+ DisplayModeId getId() const { return mId; }
+ hal::HWConfigId getHwcId() const { return mHwcId; }
+
+ int32_t getWidth() const { return mWidth; }
+ int32_t getHeight() const { return mHeight; }
+ ui::Size getSize() const { return {mWidth, mHeight}; }
+ Fps getFps() const { return mFps; }
+ nsecs_t getVsyncPeriod() const { return mFps.getPeriodNsecs(); }
+ float getDpiX() const { return mDpiX; }
+ float getDpiY() const { return mDpiY; }
+
+ // Switches between modes in the same group are seamless, i.e.
+ // without visual interruptions such as a black screen.
+ int32_t getGroup() const { return mGroup; }
+
+ bool equalsExceptDisplayModeId(const DisplayModePtr& other) const {
+ return mHwcId == other->mHwcId && mWidth == other->mWidth && mHeight == other->mHeight &&
+ getVsyncPeriod() == other->getVsyncPeriod() && mDpiX == other->mDpiX &&
+ mDpiY == other->mDpiY && mGroup == other->mGroup;
+ }
+
+private:
+ explicit DisplayMode(hal::HWConfigId id) : mHwcId(id) {}
+
+ hal::HWConfigId mHwcId;
+ DisplayModeId mId;
+
+ int32_t mWidth = -1;
+ int32_t mHeight = -1;
+ Fps mFps;
+ float mDpiX = -1;
+ float mDpiY = -1;
+ int32_t mGroup = -1;
+};
+
+inline std::string to_string(const DisplayMode& mode) {
+ return base::StringPrintf("{id=%d, hwcId=%d, width=%d, height=%d, refreshRate=%s, "
+ "dpiX=%.2f, dpiY=%.2f, group=%d}",
+ mode.getId().value(), mode.getHwcId(), mode.getWidth(),
+ mode.getHeight(), to_string(mode.getFps()).c_str(), mode.getDpiX(),
+ mode.getDpiY(), mode.getGroup());
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index 4c3b3e5..8d685cf 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -56,13 +56,12 @@
*
*/
-FramebufferSurface::FramebufferSurface(HWComposer& hwc, DisplayId displayId,
+FramebufferSurface::FramebufferSurface(HWComposer& hwc, PhysicalDisplayId displayId,
const sp<IGraphicBufferConsumer>& consumer,
- uint32_t maxWidth, uint32_t maxHeight)
+ const ui::Size& size, const ui::Size& maxSize)
: ConsumerBase(consumer),
mDisplayId(displayId),
- mMaxWidth(maxWidth),
- mMaxHeight(maxHeight),
+ mMaxSize(maxSize),
mCurrentBufferSlot(-1),
mCurrentBuffer(),
mCurrentFence(Fence::NO_FENCE),
@@ -77,16 +76,14 @@
mConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_FB |
GRALLOC_USAGE_HW_RENDER |
GRALLOC_USAGE_HW_COMPOSER);
- const auto& activeConfig = mHwc.getActiveConfig(displayId);
- ui::Size limitedSize =
- limitFramebufferSize(activeConfig->getWidth(), activeConfig->getHeight());
+ const auto limitedSize = limitSize(size);
mConsumer->setDefaultBufferSize(limitedSize.width, limitedSize.height);
mConsumer->setMaxAcquiredBufferCount(
SurfaceFlinger::maxFrameBufferAcquiredBuffers - 1);
}
-void FramebufferSurface::resizeBuffers(uint32_t width, uint32_t height) {
- ui::Size limitedSize = limitFramebufferSize(width, height);
+void FramebufferSurface::resizeBuffers(const ui::Size& newSize) {
+ const auto limitedSize = limitSize(newSize);
mConsumer->setDefaultBufferSize(limitedSize.width, limitedSize.height);
}
@@ -182,24 +179,28 @@
}
}
-ui::Size FramebufferSurface::limitFramebufferSize(uint32_t width, uint32_t height) {
- ui::Size framebufferSize(width, height);
- bool wasLimited = true;
- if (width > mMaxWidth && mMaxWidth != 0) {
- float aspectRatio = float(width) / float(height);
- framebufferSize.height = mMaxWidth / aspectRatio;
- framebufferSize.width = mMaxWidth;
+ui::Size FramebufferSurface::limitSize(const ui::Size& size) {
+ return limitSizeInternal(size, mMaxSize);
+}
+
+ui::Size FramebufferSurface::limitSizeInternal(const ui::Size& size, const ui::Size& maxSize) {
+ ui::Size limitedSize = size;
+ bool wasLimited = false;
+ if (size.width > maxSize.width && maxSize.width != 0) {
+ const float aspectRatio = static_cast<float>(size.width) / size.height;
+ limitedSize.height = maxSize.width / aspectRatio;
+ limitedSize.width = maxSize.width;
wasLimited = true;
}
- if (height > mMaxHeight && mMaxHeight != 0) {
- float aspectRatio = float(width) / float(height);
- framebufferSize.height = mMaxHeight;
- framebufferSize.width = mMaxHeight * aspectRatio;
+ if (limitedSize.height > maxSize.height && maxSize.height != 0) {
+ const float aspectRatio = static_cast<float>(size.width) / size.height;
+ limitedSize.height = maxSize.height;
+ limitedSize.width = maxSize.height * aspectRatio;
wasLimited = true;
}
ALOGI_IF(wasLimited, "framebuffer size has been limited to [%dx%d] from [%dx%d]",
- framebufferSize.width, framebufferSize.height, width, height);
- return framebufferSize;
+ limitedSize.width, limitedSize.height, size.width, size.height);
+ return limitedSize;
}
void FramebufferSurface::dumpAsString(String8& result) const {
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index a1859f3..3123351 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -23,6 +23,7 @@
#include <compositionengine/DisplaySurface.h>
#include <compositionengine/impl/HwcBufferCache.h>
#include <gui/ConsumerBase.h>
+#include <ui/DisplayId.h>
#include <ui/Size.h>
#include "DisplayIdentification.h"
@@ -39,9 +40,9 @@
class FramebufferSurface : public ConsumerBase, public compositionengine::DisplaySurface {
public:
- FramebufferSurface(HWComposer& hwc, DisplayId displayId,
- const sp<IGraphicBufferConsumer>& consumer, uint32_t maxWidth,
- uint32_t maxHeight);
+ FramebufferSurface(HWComposer& hwc, PhysicalDisplayId displayId,
+ const sp<IGraphicBufferConsumer>& consumer, const ui::Size& size,
+ const ui::Size& maxSize);
virtual status_t beginFrame(bool mustRecompose);
virtual status_t prepareFrame(CompositionType compositionType);
@@ -49,35 +50,36 @@
virtual void onFrameCommitted();
virtual void dumpAsString(String8& result) const;
- virtual void resizeBuffers(uint32_t width, uint32_t height);
+ virtual void resizeBuffers(const ui::Size&) override;
virtual const sp<Fence>& getClientTargetAcquireFence() const override;
private:
+ friend class FramebufferSurfaceTest;
+
+ // Limits the width and height by the maximum width specified.
+ ui::Size limitSize(const ui::Size&);
+
+ // Used for testing purposes.
+ static ui::Size limitSizeInternal(const ui::Size&, const ui::Size& maxSize);
+
virtual ~FramebufferSurface() { }; // this class cannot be overloaded
virtual void freeBufferLocked(int slotIndex);
virtual void dumpLocked(String8& result, const char* prefix) const;
- // Limits the width and height by the maximum width specified in the constructor.
- ui::Size limitFramebufferSize(uint32_t width, uint32_t height);
-
// nextBuffer waits for and then latches the next buffer from the
// BufferQueue and releases the previously latched buffer to the
// BufferQueue. The new buffer is returned in the 'buffer' argument.
status_t nextBuffer(uint32_t& outSlot, sp<GraphicBuffer>& outBuffer,
sp<Fence>& outFence, ui::Dataspace& outDataspace);
- const DisplayId mDisplayId;
+ const PhysicalDisplayId mDisplayId;
// Framebuffer size has a dimension limitation in pixels based on the graphics capabilities of
// the device.
- const uint32_t mMaxWidth;
-
- // Framebuffer size has a dimension limitation in pixels based on the graphics capabilities of
- // the device.
- const uint32_t mMaxHeight;
+ const ui::Size mMaxSize;
// mCurrentBufferIndex is the slot index of the current buffer or
// INVALID_BUFFER_SLOT to indicate that either there is no current buffer
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index e6bff04..d04b5f7 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -26,18 +26,17 @@
#include "HWC2.h"
+#include <android/configuration.h>
+#include <ftl/future.h>
#include <ui/Fence.h>
#include <ui/FloatRect.h>
#include <ui/GraphicBuffer.h>
-#include <android/configuration.h>
-
-#include <inttypes.h>
#include <algorithm>
+#include <cinttypes>
#include <iterator>
#include <set>
-#include "../Promise.h"
#include "ComposerHal.h"
namespace android {
@@ -69,33 +68,6 @@
// Display methods
Display::~Display() = default;
-Display::Config::Config(Display& display, HWConfigId id)
- : mDisplay(display),
- mId(id),
- mWidth(-1),
- mHeight(-1),
- mVsyncPeriod(-1),
- mDpiX(-1),
- mDpiY(-1) {}
-
-Display::Config::Builder::Builder(Display& display, HWConfigId id)
- : mConfig(new Config(display, id)) {}
-
-float Display::Config::Builder::getDefaultDensity() {
- // Default density is based on TVs: 1080p displays get XHIGH density, lower-
- // resolution displays get TV density. Maybe eventually we'll need to update
- // it for 4k displays, though hopefully those will just report accurate DPI
- // information to begin with. This is also used for virtual displays and
- // older HWC implementations, so be careful about orientation.
-
- auto longDimension = std::max(mConfig->mWidth, mConfig->mHeight);
- if (longDimension >= 1080) {
- return ACONFIGURATION_DENSITY_XHIGH;
- } else {
- return ACONFIGURATION_DENSITY_TV;
- }
-}
-
namespace impl {
Display::Display(android::Hwc2::Composer& composer,
@@ -163,93 +135,12 @@
return Error::NONE;
}
-Error Display::getActiveConfig(
- std::shared_ptr<const Display::Config>* outConfig) const
-{
- ALOGV("[%" PRIu64 "] getActiveConfig", mId);
- HWConfigId configId = 0;
- auto intError = mComposer.getActiveConfig(mId, &configId);
- auto error = static_cast<Error>(intError);
-
- if (error != Error::NONE) {
- ALOGE("Unable to get active config for mId:[%" PRIu64 "]", mId);
- *outConfig = nullptr;
- return error;
- }
-
- if (mConfigs.count(configId) != 0) {
- *outConfig = mConfigs.at(configId);
- } else {
- ALOGE("[%" PRIu64 "] getActiveConfig returned unknown config %u", mId,
- configId);
- // Return no error, but the caller needs to check for a null pointer to
- // detect this case
- *outConfig = nullptr;
- }
-
- return Error::NONE;
-}
-
bool Display::isVsyncPeriodSwitchSupported() const {
ALOGV("[%" PRIu64 "] isVsyncPeriodSwitchSupported()", mId);
return mComposer.isVsyncPeriodSwitchSupported();
}
-Error Display::getDisplayVsyncPeriod(nsecs_t* outVsyncPeriod) const {
- ALOGV("[%" PRIu64 "] getDisplayVsyncPeriod", mId);
-
- Error error;
-
- if (isVsyncPeriodSwitchSupported()) {
- Hwc2::VsyncPeriodNanos vsyncPeriodNanos = 0;
- auto intError = mComposer.getDisplayVsyncPeriod(mId, &vsyncPeriodNanos);
- error = static_cast<Error>(intError);
- *outVsyncPeriod = static_cast<nsecs_t>(vsyncPeriodNanos);
- } else {
- // Get the default vsync period
- std::shared_ptr<const Display::Config> config;
- error = getActiveConfig(&config);
- if (error != Error::NONE) {
- return error;
- }
- if (!config) {
- // HWC has updated the display modes and hasn't notified us yet.
- return Error::BAD_CONFIG;
- }
-
- *outVsyncPeriod = config->getVsyncPeriod();
- }
-
- return error;
-}
-
-Error Display::getActiveConfigIndex(int* outIndex) const {
- ALOGV("[%" PRIu64 "] getActiveConfigIndex", mId);
- HWConfigId configId = 0;
- auto intError = mComposer.getActiveConfig(mId, &configId);
- auto error = static_cast<Error>(intError);
-
- if (error != Error::NONE) {
- ALOGE("Unable to get active config for mId:[%" PRIu64 "]", mId);
- *outIndex = -1;
- return error;
- }
-
- auto pos = mConfigs.find(configId);
- if (pos != mConfigs.end()) {
- *outIndex = std::distance(mConfigs.begin(), pos);
- ALOGV("[%" PRIu64 "] index = %d", mId, *outIndex);
- } else {
- ALOGE("[%" PRIu64 "] getActiveConfig returned unknown config %u", mId, configId);
- // Return no error, but the caller needs to check for a negative index
- // to detect this case
- *outIndex = -1;
- }
-
- return Error::NONE;
-}
-
Error Display::getChangedCompositionTypes(std::unordered_map<HWC2::Layer*, Composition>* outTypes) {
std::vector<Hwc2::Layer> layerIds;
std::vector<Hwc2::IComposerClient::Composition> types;
@@ -336,15 +227,6 @@
return static_cast<Error>(intError);
}
-std::vector<std::shared_ptr<const Display::Config>> Display::getConfigs() const
-{
- std::vector<std::shared_ptr<const Config>> configs;
- for (const auto& element : mConfigs) {
- configs.emplace_back(element.second);
- }
- return configs;
-}
-
Error Display::getName(std::string* outName) const
{
auto intError = mComposer.getDisplayName(mId, outName);
@@ -382,7 +264,7 @@
return Error::NONE;
}
-Error Display::getConnectionType(android::DisplayConnectionType* outType) const {
+Error Display::getConnectionType(ui::DisplayConnectionType* outType) const {
if (mType != DisplayType::PHYSICAL) return Error::BAD_DISPLAY;
using ConnectionType = Hwc2::IComposerClient::DisplayConnectionType;
@@ -392,9 +274,8 @@
return error;
}
- *outType = connectionType == ConnectionType::INTERNAL
- ? android::DisplayConnectionType::Internal
- : android::DisplayConnectionType::External;
+ *outType = connectionType == ConnectionType::INTERNAL ? ui::DisplayConnectionType::Internal
+ : ui::DisplayConnectionType::External;
return Error::NONE;
}
@@ -487,16 +368,10 @@
return Error::NONE;
}
-Error Display::setActiveConfigWithConstraints(
- const std::shared_ptr<const HWC2::Display::Config>& config,
- const VsyncPeriodChangeConstraints& constraints, VsyncPeriodChangeTimeline* outTimeline) {
+Error Display::setActiveConfigWithConstraints(hal::HWConfigId configId,
+ const VsyncPeriodChangeConstraints& constraints,
+ VsyncPeriodChangeTimeline* outTimeline) {
ALOGV("[%" PRIu64 "] setActiveConfigWithConstraints", mId);
- if (config->getDisplayId() != mId) {
- ALOGE("setActiveConfigWithConstraints received config %u for the wrong display %" PRIu64
- " (expected %" PRIu64 ")",
- config->getId(), config->getDisplayId(), mId);
- return Error::BAD_CONFIG;
- }
if (isVsyncPeriodSwitchSupported()) {
Hwc2::IComposerClient::VsyncPeriodChangeConstraints hwc2Constraints;
@@ -504,9 +379,8 @@
hwc2Constraints.seamlessRequired = constraints.seamlessRequired;
Hwc2::VsyncPeriodChangeTimeline vsyncPeriodChangeTimeline = {};
- auto intError =
- mComposer.setActiveConfigWithConstraints(mId, config->getId(), hwc2Constraints,
- &vsyncPeriodChangeTimeline);
+ auto intError = mComposer.setActiveConfigWithConstraints(mId, configId, hwc2Constraints,
+ &vsyncPeriodChangeTimeline);
outTimeline->newVsyncAppliedTimeNanos = vsyncPeriodChangeTimeline.newVsyncAppliedTimeNanos;
outTimeline->refreshRequired = vsyncPeriodChangeTimeline.refreshRequired;
outTimeline->refreshTimeNanos = vsyncPeriodChangeTimeline.refreshTimeNanos;
@@ -520,25 +394,13 @@
ALOGE("setActiveConfigWithConstraints received constraints that can't be satisfied");
}
- auto intError_2_4 = mComposer.setActiveConfig(mId, config->getId());
+ auto intError_2_4 = mComposer.setActiveConfig(mId, configId);
outTimeline->newVsyncAppliedTimeNanos = std::max(now, constraints.desiredTimeNanos);
outTimeline->refreshRequired = true;
outTimeline->refreshTimeNanos = now;
return static_cast<Error>(intError_2_4);
}
-Error Display::setActiveConfig(const std::shared_ptr<const Config>& config)
-{
- if (config->getDisplayId() != mId) {
- ALOGE("setActiveConfig received config %u for the wrong display %"
- PRIu64 " (expected %" PRIu64 ")", config->getId(),
- config->getDisplayId(), mId);
- return Error::BAD_CONFIG;
- }
- auto intError = mComposer.setActiveConfig(mId, config->getId());
- return static_cast<Error>(intError);
-}
-
Error Display::setClientTarget(uint32_t slot, const sp<GraphicBuffer>& target,
const sp<Fence>& acquireFence, Dataspace dataspace)
{
@@ -647,7 +509,7 @@
}
std::future<Error> Display::setDisplayBrightness(float brightness) {
- return promise::defer([composer = &mComposer, id = mId, brightness] {
+ return ftl::defer([composer = &mComposer, id = mId, brightness] {
const auto intError = composer->setDisplayBrightness(id, brightness);
return static_cast<Error>(intError);
});
@@ -682,58 +544,10 @@
void Display::setConnected(bool connected) {
if (!mIsConnected && connected) {
mComposer.setClientTargetSlotCount(mId);
- if (mType == DisplayType::PHYSICAL) {
- loadConfigs();
- }
}
mIsConnected = connected;
}
-int32_t Display::getAttribute(HWConfigId configId, Attribute attribute) {
- int32_t value = 0;
- auto intError = mComposer.getDisplayAttribute(mId, configId, attribute, &value);
- auto error = static_cast<Error>(intError);
- if (error != Error::NONE) {
- ALOGE("getDisplayAttribute(%" PRIu64 ", %u, %s) failed: %s (%d)", mId,
- configId, to_string(attribute).c_str(),
- to_string(error).c_str(), intError);
- return -1;
- }
- return value;
-}
-
-void Display::loadConfig(HWConfigId configId) {
- ALOGV("[%" PRIu64 "] loadConfig(%u)", mId, configId);
-
- auto config = Config::Builder(*this, configId)
- .setWidth(getAttribute(configId, hal::Attribute::WIDTH))
- .setHeight(getAttribute(configId, hal::Attribute::HEIGHT))
- .setVsyncPeriod(getAttribute(configId, hal::Attribute::VSYNC_PERIOD))
- .setDpiX(getAttribute(configId, hal::Attribute::DPI_X))
- .setDpiY(getAttribute(configId, hal::Attribute::DPI_Y))
- .setConfigGroup(getAttribute(configId, hal::Attribute::CONFIG_GROUP))
- .build();
- mConfigs.emplace(configId, std::move(config));
-}
-
-void Display::loadConfigs()
-{
- ALOGV("[%" PRIu64 "] loadConfigs", mId);
-
- std::vector<HWConfigId> configIds;
- auto intError = mComposer.getDisplayConfigs(mId, &configIds);
- auto error = static_cast<Error>(intError);
- if (error != Error::NONE) {
- ALOGE("[%" PRIu64 "] getDisplayConfigs [2] failed: %s (%d)", mId,
- to_string(error).c_str(), intError);
- return;
- }
-
- for (auto configId : configIds) {
- loadConfig(configId);
- }
-}
-
// Other Display methods
HWC2::Layer* Display::getLayerById(HWLayerId id) const {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 8bd6ea8..e7bf286 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -14,14 +14,13 @@
* limitations under the License.
*/
-#ifndef ANDROID_SF_HWC2_H
-#define ANDROID_SF_HWC2_H
+#pragma once
#include <gui/HdrMetadata.h>
#include <math/mat4.h>
-#include <ui/DisplayInfo.h>
#include <ui/HdrCapabilities.h>
#include <ui/Region.h>
+#include <ui/StaticDisplayInfo.h>
#include <utils/Log.h>
#include <utils/StrongPointer.h>
#include <utils/Timers.h>
@@ -36,15 +35,16 @@
#include "Hal.h"
namespace android {
- struct DisplayedFrameStats;
- class Fence;
- class FloatRect;
- class GraphicBuffer;
- namespace Hwc2 {
- class Composer;
- }
- class TestableSurfaceFlinger;
+class Fence;
+class FloatRect;
+class GraphicBuffer;
+class TestableSurfaceFlinger;
+struct DisplayedFrameStats;
+
+namespace Hwc2 {
+class Composer;
+} // namespace Hwc2
namespace HWC2 {
@@ -61,19 +61,17 @@
// All calls receive a sequenceId, which will be the value that was supplied to
// HWC2::Device::registerCallback(). It's used to help differentiate callbacks
// from different hardware composer instances.
-class ComposerCallback {
- public:
- virtual void onHotplugReceived(int32_t sequenceId, hal::HWDisplayId display,
- hal::Connection connection) = 0;
- virtual void onRefreshReceived(int32_t sequenceId, hal::HWDisplayId display) = 0;
- virtual void onVsyncReceived(int32_t sequenceId, hal::HWDisplayId display, int64_t timestamp,
- std::optional<hal::VsyncPeriodNanos> vsyncPeriod) = 0;
- virtual void onVsyncPeriodTimingChangedReceived(
- int32_t sequenceId, hal::HWDisplayId display,
- const hal::VsyncPeriodChangeTimeline& updatedTimeline) = 0;
- virtual void onSeamlessPossible(int32_t sequenceId, hal::HWDisplayId display) = 0;
+struct ComposerCallback {
+ virtual void onHotplugReceived(int32_t sequenceId, hal::HWDisplayId, hal::Connection) = 0;
+ virtual void onRefreshReceived(int32_t sequenceId, hal::HWDisplayId) = 0;
+ virtual void onVsyncReceived(int32_t sequenceId, hal::HWDisplayId, int64_t timestamp,
+ std::optional<hal::VsyncPeriodNanos>) = 0;
+ virtual void onVsyncPeriodTimingChangedReceived(int32_t sequenceId, hal::HWDisplayId,
+ const hal::VsyncPeriodChangeTimeline&) = 0;
+ virtual void onSeamlessPossible(int32_t sequenceId, hal::HWDisplayId) = 0;
- virtual ~ComposerCallback() = default;
+protected:
+ ~ComposerCallback() = default;
};
// Convenience C++ class to access per display functions directly.
@@ -81,80 +79,6 @@
public:
virtual ~Display();
- class Config {
- public:
- class Builder
- {
- public:
- Builder(Display& display, hal::HWConfigId id);
-
- std::shared_ptr<const Config> build() {
- return std::const_pointer_cast<const Config>(
- std::move(mConfig));
- }
-
- Builder& setWidth(int32_t width) {
- mConfig->mWidth = width;
- return *this;
- }
- Builder& setHeight(int32_t height) {
- mConfig->mHeight = height;
- return *this;
- }
- Builder& setVsyncPeriod(int32_t vsyncPeriod) {
- mConfig->mVsyncPeriod = vsyncPeriod;
- return *this;
- }
- Builder& setDpiX(int32_t dpiX) {
- if (dpiX == -1) {
- mConfig->mDpiX = getDefaultDensity();
- } else {
- mConfig->mDpiX = dpiX / 1000.0f;
- }
- return *this;
- }
- Builder& setDpiY(int32_t dpiY) {
- if (dpiY == -1) {
- mConfig->mDpiY = getDefaultDensity();
- } else {
- mConfig->mDpiY = dpiY / 1000.0f;
- }
- return *this;
- }
- Builder& setConfigGroup(int32_t configGroup) {
- mConfig->mConfigGroup = configGroup;
- return *this;
- }
-
- private:
- float getDefaultDensity();
- std::shared_ptr<Config> mConfig;
- };
-
- hal::HWDisplayId getDisplayId() const { return mDisplay.getId(); }
- hal::HWConfigId getId() const { return mId; }
-
- int32_t getWidth() const { return mWidth; }
- int32_t getHeight() const { return mHeight; }
- nsecs_t getVsyncPeriod() const { return mVsyncPeriod; }
- float getDpiX() const { return mDpiX; }
- float getDpiY() const { return mDpiY; }
- int32_t getConfigGroup() const { return mConfigGroup; }
-
- private:
- Config(Display& display, hal::HWConfigId id);
-
- Display& mDisplay;
- hal::HWConfigId mId;
-
- int32_t mWidth;
- int32_t mHeight;
- nsecs_t mVsyncPeriod;
- float mDpiX;
- float mDpiY;
- int32_t mConfigGroup;
- };
-
virtual hal::HWDisplayId getId() const = 0;
virtual bool isConnected() const = 0;
virtual void setConnected(bool connected) = 0; // For use by Device only
@@ -164,9 +88,6 @@
[[clang::warn_unused_result]] virtual hal::Error acceptChanges() = 0;
[[clang::warn_unused_result]] virtual hal::Error createLayer(Layer** outLayer) = 0;
[[clang::warn_unused_result]] virtual hal::Error destroyLayer(Layer* layer) = 0;
- [[clang::warn_unused_result]] virtual hal::Error getActiveConfig(
- std::shared_ptr<const Config>* outConfig) const = 0;
- [[clang::warn_unused_result]] virtual hal::Error getActiveConfigIndex(int* outIndex) const = 0;
[[clang::warn_unused_result]] virtual hal::Error getChangedCompositionTypes(
std::unordered_map<Layer*, hal::Composition>* outTypes) = 0;
[[clang::warn_unused_result]] virtual hal::Error getColorModes(
@@ -178,16 +99,12 @@
[[clang::warn_unused_result]] virtual hal::Error getDataspaceSaturationMatrix(
hal::Dataspace dataspace, android::mat4* outMatrix) = 0;
- // Doesn't call into the HWC2 device, so no errors are possible
- [[clang::warn_unused_result]] virtual std::vector<std::shared_ptr<const Config>> getConfigs()
- const = 0;
-
[[clang::warn_unused_result]] virtual hal::Error getName(std::string* outName) const = 0;
[[clang::warn_unused_result]] virtual hal::Error getRequests(
hal::DisplayRequest* outDisplayRequests,
std::unordered_map<Layer*, hal::LayerRequest>* outLayerRequests) = 0;
[[clang::warn_unused_result]] virtual hal::Error getConnectionType(
- android::DisplayConnectionType*) const = 0;
+ ui::DisplayConnectionType*) const = 0;
[[clang::warn_unused_result]] virtual hal::Error supportsDoze(bool* outSupport) const = 0;
[[clang::warn_unused_result]] virtual hal::Error getHdrCapabilities(
android::HdrCapabilities* outCapabilities) const = 0;
@@ -203,8 +120,6 @@
std::unordered_map<Layer*, android::sp<android::Fence>>* outFences) const = 0;
[[clang::warn_unused_result]] virtual hal::Error present(
android::sp<android::Fence>* outPresentFence) = 0;
- [[clang::warn_unused_result]] virtual hal::Error setActiveConfig(
- const std::shared_ptr<const Config>& config) = 0;
[[clang::warn_unused_result]] virtual hal::Error setClientTarget(
uint32_t slot, const android::sp<android::GraphicBuffer>& target,
const android::sp<android::Fence>& acquireFence, hal::Dataspace dataspace) = 0;
@@ -224,11 +139,8 @@
android::sp<android::Fence>* outPresentFence, uint32_t* state) = 0;
[[clang::warn_unused_result]] virtual std::future<hal::Error> setDisplayBrightness(
float brightness) = 0;
- [[clang::warn_unused_result]] virtual hal::Error getDisplayVsyncPeriod(
- nsecs_t* outVsyncPeriod) const = 0;
[[clang::warn_unused_result]] virtual hal::Error setActiveConfigWithConstraints(
- const std::shared_ptr<const HWC2::Display::Config>& config,
- const hal::VsyncPeriodChangeConstraints& constraints,
+ hal::HWConfigId configId, const hal::VsyncPeriodChangeConstraints& constraints,
hal::VsyncPeriodChangeTimeline* outTimeline) = 0;
[[clang::warn_unused_result]] virtual hal::Error setAutoLowLatencyMode(bool on) = 0;
[[clang::warn_unused_result]] virtual hal::Error getSupportedContentTypes(
@@ -242,17 +154,14 @@
class Display : public HWC2::Display {
public:
- Display(android::Hwc2::Composer& composer,
- const std::unordered_set<hal::Capability>& capabilities, hal::HWDisplayId id,
- hal::DisplayType type);
+ Display(android::Hwc2::Composer&, const std::unordered_set<hal::Capability>&, hal::HWDisplayId,
+ hal::DisplayType);
~Display() override;
// Required by HWC2
hal::Error acceptChanges() override;
hal::Error createLayer(Layer** outLayer) override;
- hal::Error destroyLayer(Layer* layer) override;
- hal::Error getActiveConfig(std::shared_ptr<const Config>* outConfig) const override;
- hal::Error getActiveConfigIndex(int* outIndex) const override;
+ hal::Error destroyLayer(Layer*) override;
hal::Error getChangedCompositionTypes(
std::unordered_map<Layer*, hal::Composition>* outTypes) override;
hal::Error getColorModes(std::vector<hal::ColorMode>* outModes) const override;
@@ -260,17 +169,13 @@
int32_t getSupportedPerFrameMetadata() const override;
hal::Error getRenderIntents(hal::ColorMode colorMode,
std::vector<hal::RenderIntent>* outRenderIntents) const override;
- hal::Error getDataspaceSaturationMatrix(hal::Dataspace dataspace,
- android::mat4* outMatrix) override;
-
- // Doesn't call into the HWC2 device, so no errors are possible
- std::vector<std::shared_ptr<const Config>> getConfigs() const override;
+ hal::Error getDataspaceSaturationMatrix(hal::Dataspace, android::mat4* outMatrix) override;
hal::Error getName(std::string* outName) const override;
hal::Error getRequests(
hal::DisplayRequest* outDisplayRequests,
std::unordered_map<Layer*, hal::LayerRequest>* outLayerRequests) override;
- hal::Error getConnectionType(android::DisplayConnectionType*) const override;
+ hal::Error getConnectionType(ui::DisplayConnectionType*) const override;
hal::Error supportsDoze(bool* outSupport) const override;
hal::Error getHdrCapabilities(android::HdrCapabilities* outCapabilities) const override;
hal::Error getDisplayedContentSamplingAttributes(hal::PixelFormat* outFormat,
@@ -283,26 +188,23 @@
hal::Error getReleaseFences(
std::unordered_map<Layer*, android::sp<android::Fence>>* outFences) const override;
hal::Error present(android::sp<android::Fence>* outPresentFence) override;
- hal::Error setActiveConfig(const std::shared_ptr<const HWC2::Display::Config>& config) override;
hal::Error setClientTarget(uint32_t slot, const android::sp<android::GraphicBuffer>& target,
const android::sp<android::Fence>& acquireFence,
hal::Dataspace dataspace) override;
- hal::Error setColorMode(hal::ColorMode mode, hal::RenderIntent renderIntent) override;
+ hal::Error setColorMode(hal::ColorMode, hal::RenderIntent) override;
hal::Error setColorTransform(const android::mat4& matrix, hal::ColorTransform hint) override;
- hal::Error setOutputBuffer(const android::sp<android::GraphicBuffer>& buffer,
+ hal::Error setOutputBuffer(const android::sp<android::GraphicBuffer>&,
const android::sp<android::Fence>& releaseFence) override;
- hal::Error setPowerMode(hal::PowerMode mode) override;
+ hal::Error setPowerMode(hal::PowerMode) override;
hal::Error setVsyncEnabled(hal::Vsync enabled) override;
hal::Error validate(uint32_t* outNumTypes, uint32_t* outNumRequests) override;
hal::Error presentOrValidate(uint32_t* outNumTypes, uint32_t* outNumRequests,
android::sp<android::Fence>* outPresentFence,
uint32_t* state) override;
std::future<hal::Error> setDisplayBrightness(float brightness) override;
- hal::Error getDisplayVsyncPeriod(nsecs_t* outVsyncPeriod) const override;
- hal::Error setActiveConfigWithConstraints(
- const std::shared_ptr<const HWC2::Display::Config>& config,
- const hal::VsyncPeriodChangeConstraints& constraints,
- hal::VsyncPeriodChangeTimeline* outTimeline) override;
+ hal::Error setActiveConfigWithConstraints(hal::HWConfigId configId,
+ const hal::VsyncPeriodChangeConstraints& constraints,
+ hal::VsyncPeriodChangeTimeline* outTimeline) override;
hal::Error setAutoLowLatencyMode(bool on) override;
hal::Error getSupportedContentTypes(
std::vector<hal::ContentType>* outSupportedContentTypes) const override;
@@ -319,13 +221,10 @@
virtual bool isVsyncPeriodSwitchSupported() const override;
private:
- int32_t getAttribute(hal::HWConfigId configId, hal::Attribute attribute);
- void loadConfig(hal::HWConfigId configId);
- void loadConfigs();
// This may fail (and return a null pointer) if no layer with this ID exists
// on this display
- Layer* getLayerById(hal::HWLayerId id) const;
+ Layer* getLayerById(hal::HWLayerId) const;
friend android::TestableSurfaceFlinger;
@@ -342,7 +241,6 @@
bool mIsConnected = false;
std::unordered_map<hal::HWLayerId, std::unique_ptr<Layer>> mLayers;
- std::unordered_map<hal::HWConfigId, std::shared_ptr<const Config>> mConfigs;
std::once_flag mDisplayCapabilityQueryFlag;
std::unordered_set<hal::DisplayCapability> mDisplayCapabilities;
@@ -452,5 +350,3 @@
} // namespace impl
} // namespace HWC2
} // namespace android
-
-#endif // ANDROID_SF_HWC2_H
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 7db9359..b73d032 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -26,9 +26,11 @@
#include "HWComposer.h"
+#include <android-base/properties.h>
#include <compositionengine/Output.h>
#include <compositionengine/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <ftl/future.h>
#include <log/log.h>
#include <ui/DebugUtils.h>
#include <ui/GraphicBuffer.h>
@@ -36,8 +38,8 @@
#include <utils/Trace.h>
#include "../Layer.h" // needed only for debugging
-#include "../Promise.h"
#include "../SurfaceFlinger.h"
+#include "../SurfaceFlingerProperties.h"
#include "ComposerHal.h"
#include "HWC2.h"
@@ -72,6 +74,7 @@
namespace hal = android::hardware::graphics::composer::hal;
+namespace android {
namespace {
using android::hardware::Return;
@@ -86,47 +89,46 @@
mSequenceId(sequenceId),
mVsyncSwitchingSupported(vsyncSwitchingSupported) {}
- android::hardware::Return<void> onHotplug(hal::HWDisplayId display,
- hal::Connection conn) override {
+ Return<void> onHotplug(hal::HWDisplayId display, hal::Connection conn) override {
mCallback->onHotplugReceived(mSequenceId, display, conn);
- return android::hardware::Void();
+ return Void();
}
- android::hardware::Return<void> onRefresh(hal::HWDisplayId display) override {
+ Return<void> onRefresh(hal::HWDisplayId display) override {
mCallback->onRefreshReceived(mSequenceId, display);
- return android::hardware::Void();
+ return Void();
}
- android::hardware::Return<void> onVsync(hal::HWDisplayId display, int64_t timestamp) override {
+ Return<void> onVsync(hal::HWDisplayId display, int64_t timestamp) override {
if (!mVsyncSwitchingSupported) {
mCallback->onVsyncReceived(mSequenceId, display, timestamp, std::nullopt);
} else {
ALOGW("Unexpected onVsync callback on composer >= 2.4, ignoring.");
}
- return android::hardware::Void();
+ return Void();
}
- android::hardware::Return<void> onVsync_2_4(hal::HWDisplayId display, int64_t timestamp,
- hal::VsyncPeriodNanos vsyncPeriodNanos) override {
+ Return<void> onVsync_2_4(hal::HWDisplayId display, int64_t timestamp,
+ hal::VsyncPeriodNanos vsyncPeriodNanos) override {
if (mVsyncSwitchingSupported) {
mCallback->onVsyncReceived(mSequenceId, display, timestamp,
std::make_optional(vsyncPeriodNanos));
} else {
ALOGW("Unexpected onVsync_2_4 callback on composer <= 2.3, ignoring.");
}
- return android::hardware::Void();
+ return Void();
}
- android::hardware::Return<void> onVsyncPeriodTimingChanged(
+ Return<void> onVsyncPeriodTimingChanged(
hal::HWDisplayId display,
const hal::VsyncPeriodChangeTimeline& updatedTimeline) override {
mCallback->onVsyncPeriodTimingChangedReceived(mSequenceId, display, updatedTimeline);
- return android::hardware::Void();
+ return Void();
}
- android::hardware::Return<void> onSeamlessPossible(hal::HWDisplayId display) override {
+ Return<void> onSeamlessPossible(hal::HWDisplayId display) override {
mCallback->onSeamlessPossible(mSequenceId, display);
- return android::hardware::Void();
+ return Void();
}
private:
@@ -137,18 +139,17 @@
} // namespace
-namespace android {
-
HWComposer::~HWComposer() = default;
namespace impl {
-HWComposer::HWComposer(std::unique_ptr<Hwc2::Composer> composer) : mComposer(std::move(composer)) {
-}
+HWComposer::HWComposer(std::unique_ptr<Hwc2::Composer> composer)
+ : mComposer(std::move(composer)),
+ mUpdateDeviceProductInfoOnHotplugReconnect(
+ android::sysprop::update_device_product_info_on_hotplug_reconnect(false)) {}
HWComposer::HWComposer(const std::string& composerServiceName)
- : mComposer(std::make_unique<Hwc2::impl::Composer>(composerServiceName)) {
-}
+ : HWComposer(std::make_unique<Hwc2::impl::Composer>(composerServiceName)) {}
HWComposer::~HWComposer() {
mDisplayData.clear();
@@ -186,7 +187,7 @@
return mCapabilities.count(capability) > 0;
}
-bool HWComposer::hasDisplayCapability(DisplayId displayId,
+bool HWComposer::hasDisplayCapability(HalDisplayId displayId,
hal::DisplayCapability capability) const {
RETURN_IF_INVALID_DISPLAY(displayId, false);
return mDisplayData.at(displayId).hwcDisplay->getCapabilities().count(capability) > 0;
@@ -204,6 +205,10 @@
}
}
+bool HWComposer::updatesDeviceProductInfoOnHotplugReconnect() const {
+ return mUpdateDeviceProductInfoOnHotplugReconnect;
+}
+
bool HWComposer::onVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp) {
const auto displayId = toPhysicalDisplayId(hwcDisplayId);
if (!displayId) {
@@ -214,14 +219,10 @@
RETURN_IF_INVALID_DISPLAY(*displayId, false);
auto& displayData = mDisplayData[*displayId];
- if (displayData.isVirtual) {
- LOG_DISPLAY_ERROR(*displayId, "Invalid operation on virtual display");
- return false;
- }
+ LOG_FATAL_IF(displayData.isVirtual, "%s: Invalid operation on virtual display with ID %s",
+ __FUNCTION__, to_string(*displayId).c_str());
{
- std::lock_guard lock(displayData.lastHwVsyncLock);
-
// There have been reports of HWCs that signal several vsync events
// with the same timestamp when turning the display off and on. This
// is a bug in the HWC implementation, but filter the extra events
@@ -244,11 +245,6 @@
std::optional<DisplayId> HWComposer::allocateVirtualDisplay(uint32_t width, uint32_t height,
ui::PixelFormat* format) {
- if (mRemainingHwcVirtualDisplays == 0) {
- ALOGE("%s: No remaining virtual displays", __FUNCTION__);
- return {};
- }
-
if (SurfaceFlinger::maxVirtualDisplaySize != 0 &&
(width > SurfaceFlinger::maxVirtualDisplaySize ||
height > SurfaceFlinger::maxVirtualDisplaySize)) {
@@ -256,35 +252,35 @@
height, SurfaceFlinger::maxVirtualDisplaySize);
return {};
}
+
+ const auto displayId = mVirtualIdGenerator.nextId();
+ if (!displayId) {
+ ALOGE("%s: No remaining virtual displays", __FUNCTION__);
+ return {};
+ }
+
hal::HWDisplayId hwcDisplayId = 0;
const auto error = static_cast<hal::Error>(
mComposer->createVirtualDisplay(width, height, format, &hwcDisplayId));
if (error != hal::Error::NONE) {
ALOGE("%s: Failed to create HWC virtual display", __FUNCTION__);
+ mVirtualIdGenerator.markUnused(*displayId);
return {};
}
auto display = std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities,
hwcDisplayId, hal::DisplayType::VIRTUAL);
display->setConnected(true);
-
- DisplayId displayId;
- if (mFreeVirtualDisplayIds.empty()) {
- displayId = getVirtualDisplayId(mNextVirtualDisplayId++);
- } else {
- displayId = *mFreeVirtualDisplayIds.begin();
- mFreeVirtualDisplayIds.erase(displayId);
- }
-
- auto& displayData = mDisplayData[displayId];
+ auto& displayData = mDisplayData[*displayId];
displayData.hwcDisplay = std::move(display);
displayData.isVirtual = true;
-
- --mRemainingHwcVirtualDisplays;
return displayId;
}
-void HWComposer::allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId, DisplayId displayId) {
+void HWComposer::allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId,
+ PhysicalDisplayId displayId) {
+ mPhysicalDisplayIdMap[hwcDisplayId] = displayId;
+
if (!mInternalHwcDisplayId) {
mInternalHwcDisplayId = hwcDisplayId;
} else if (mInternalHwcDisplayId != hwcDisplayId && !mExternalHwcDisplayId) {
@@ -297,10 +293,19 @@
hal::DisplayType::PHYSICAL);
newDisplay->setConnected(true);
displayData.hwcDisplay = std::move(newDisplay);
- mPhysicalDisplayIdMap[hwcDisplayId] = displayId;
}
-HWC2::Layer* HWComposer::createLayer(DisplayId displayId) {
+int32_t HWComposer::getAttribute(hal::HWDisplayId hwcDisplayId, hal::HWConfigId configId,
+ hal::Attribute attribute) const {
+ int32_t value = 0;
+ auto error = static_cast<hal::Error>(
+ mComposer->getDisplayAttribute(hwcDisplayId, configId, attribute, &value));
+
+ RETURN_IF_HWC_ERROR_FOR("getDisplayAttribute", error, *toPhysicalDisplayId(hwcDisplayId), -1);
+ return value;
+}
+
+HWC2::Layer* HWComposer::createLayer(HalDisplayId displayId) {
RETURN_IF_INVALID_DISPLAY(displayId, nullptr);
HWC2::Layer* layer;
@@ -309,117 +314,101 @@
return layer;
}
-void HWComposer::destroyLayer(DisplayId displayId, HWC2::Layer* layer) {
+void HWComposer::destroyLayer(HalDisplayId displayId, HWC2::Layer* layer) {
RETURN_IF_INVALID_DISPLAY(displayId);
auto error = mDisplayData[displayId].hwcDisplay->destroyLayer(layer);
RETURN_IF_HWC_ERROR(error, displayId);
}
-nsecs_t HWComposer::getRefreshTimestamp(DisplayId displayId) const {
- RETURN_IF_INVALID_DISPLAY(displayId, 0);
- const auto& displayData = mDisplayData.at(displayId);
- // this returns the last refresh timestamp.
- // if the last one is not available, we estimate it based on
- // the refresh period and whatever closest timestamp we have.
- std::lock_guard lock(displayData.lastHwVsyncLock);
- nsecs_t now = systemTime(CLOCK_MONOTONIC);
- auto vsyncPeriodNanos = getDisplayVsyncPeriod(displayId);
- return now - ((now - displayData.lastHwVsync) % vsyncPeriodNanos);
+bool HWComposer::isConnected(PhysicalDisplayId displayId) const {
+ if (mDisplayData.count(displayId)) {
+ return mDisplayData.at(displayId).hwcDisplay->isConnected();
+ }
+
+ return false;
}
-bool HWComposer::isConnected(DisplayId displayId) const {
- RETURN_IF_INVALID_DISPLAY(displayId, false);
- return mDisplayData.at(displayId).hwcDisplay->isConnected();
-}
-
-std::vector<std::shared_ptr<const HWC2::Display::Config>> HWComposer::getConfigs(
- DisplayId displayId) const {
+std::vector<HWComposer::HWCDisplayMode> HWComposer::getModes(PhysicalDisplayId displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, {});
- const auto& displayData = mDisplayData.at(displayId);
- auto configs = displayData.hwcDisplay->getConfigs();
- if (displayData.configMap.empty()) {
- for (size_t i = 0; i < configs.size(); ++i) {
- displayData.configMap[i] = configs[i];
- }
+ const auto hwcDisplayId = mDisplayData.at(displayId).hwcDisplay->getId();
+ std::vector<hal::HWConfigId> configIds;
+ auto error = static_cast<hal::Error>(mComposer->getDisplayConfigs(hwcDisplayId, &configIds));
+ RETURN_IF_HWC_ERROR_FOR("getDisplayConfigs", error, *toPhysicalDisplayId(hwcDisplayId), {});
+
+ std::vector<HWCDisplayMode> modes;
+ modes.reserve(configIds.size());
+ for (auto configId : configIds) {
+ modes.push_back(HWCDisplayMode{
+ .hwcId = configId,
+ .width = getAttribute(hwcDisplayId, configId, hal::Attribute::WIDTH),
+ .height = getAttribute(hwcDisplayId, configId, hal::Attribute::HEIGHT),
+ .vsyncPeriod = getAttribute(hwcDisplayId, configId, hal::Attribute::VSYNC_PERIOD),
+ .dpiX = getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_X),
+ .dpiY = getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_Y),
+ .configGroup = getAttribute(hwcDisplayId, configId, hal::Attribute::CONFIG_GROUP),
+ });
}
- return configs;
+
+ return modes;
}
-std::shared_ptr<const HWC2::Display::Config> HWComposer::getActiveConfig(
- DisplayId displayId) const {
- RETURN_IF_INVALID_DISPLAY(displayId, nullptr);
+std::optional<hal::HWConfigId> HWComposer::getActiveMode(PhysicalDisplayId displayId) const {
+ RETURN_IF_INVALID_DISPLAY(displayId, std::nullopt);
- std::shared_ptr<const HWC2::Display::Config> config;
- auto error = mDisplayData.at(displayId).hwcDisplay->getActiveConfig(&config);
+ const auto hwcId = *fromPhysicalDisplayId(displayId);
+ ALOGV("[%" PRIu64 "] getActiveMode", hwcId);
+ hal::HWConfigId configId;
+ auto error = static_cast<hal::Error>(mComposer->getActiveConfig(hwcId, &configId));
+
if (error == hal::Error::BAD_CONFIG) {
- LOG_DISPLAY_ERROR(displayId, "No active config");
- return nullptr;
+ LOG_DISPLAY_ERROR(displayId, "No active mode");
+ return std::nullopt;
}
- RETURN_IF_HWC_ERROR(error, displayId, nullptr);
-
- if (!config) {
- LOG_DISPLAY_ERROR(displayId, "Unknown config");
- return nullptr;
- }
-
- return config;
+ return configId;
}
// Composer 2.4
-DisplayConnectionType HWComposer::getDisplayConnectionType(DisplayId displayId) const {
- RETURN_IF_INVALID_DISPLAY(displayId, DisplayConnectionType::Internal);
+ui::DisplayConnectionType HWComposer::getDisplayConnectionType(PhysicalDisplayId displayId) const {
+ RETURN_IF_INVALID_DISPLAY(displayId, ui::DisplayConnectionType::Internal);
const auto& hwcDisplay = mDisplayData.at(displayId).hwcDisplay;
- DisplayConnectionType type;
+ ui::DisplayConnectionType type;
const auto error = hwcDisplay->getConnectionType(&type);
const auto FALLBACK_TYPE = hwcDisplay->getId() == mInternalHwcDisplayId
- ? DisplayConnectionType::Internal
- : DisplayConnectionType::External;
+ ? ui::DisplayConnectionType::Internal
+ : ui::DisplayConnectionType::External;
RETURN_IF_HWC_ERROR(error, displayId, FALLBACK_TYPE);
return type;
}
-bool HWComposer::isVsyncPeriodSwitchSupported(DisplayId displayId) const {
+bool HWComposer::isVsyncPeriodSwitchSupported(PhysicalDisplayId displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, false);
return mDisplayData.at(displayId).hwcDisplay->isVsyncPeriodSwitchSupported();
}
-nsecs_t HWComposer::getDisplayVsyncPeriod(DisplayId displayId) const {
+status_t HWComposer::getDisplayVsyncPeriod(PhysicalDisplayId displayId,
+ nsecs_t* outVsyncPeriod) const {
RETURN_IF_INVALID_DISPLAY(displayId, 0);
- nsecs_t vsyncPeriodNanos;
- auto error = mDisplayData.at(displayId).hwcDisplay->getDisplayVsyncPeriod(&vsyncPeriodNanos);
+ if (!isVsyncPeriodSwitchSupported(displayId)) {
+ return INVALID_OPERATION;
+ }
+ const auto hwcId = *fromPhysicalDisplayId(displayId);
+ Hwc2::VsyncPeriodNanos vsyncPeriodNanos = 0;
+ auto error =
+ static_cast<hal::Error>(mComposer->getDisplayVsyncPeriod(hwcId, &vsyncPeriodNanos));
RETURN_IF_HWC_ERROR(error, displayId, 0);
- return vsyncPeriodNanos;
+ *outVsyncPeriod = static_cast<nsecs_t>(vsyncPeriodNanos);
+ return NO_ERROR;
}
-int HWComposer::getActiveConfigIndex(DisplayId displayId) const {
- RETURN_IF_INVALID_DISPLAY(displayId, -1);
-
- int index;
- auto error = mDisplayData.at(displayId).hwcDisplay->getActiveConfigIndex(&index);
- if (error == hal::Error::BAD_CONFIG) {
- LOG_DISPLAY_ERROR(displayId, "No active config");
- return -1;
- }
-
- RETURN_IF_HWC_ERROR(error, displayId, -1);
-
- if (index < 0) {
- LOG_DISPLAY_ERROR(displayId, "Unknown config");
- return -1;
- }
-
- return index;
-}
-
-std::vector<ui::ColorMode> HWComposer::getColorModes(DisplayId displayId) const {
+std::vector<ui::ColorMode> HWComposer::getColorModes(PhysicalDisplayId displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, {});
std::vector<ui::ColorMode> modes;
@@ -428,7 +417,7 @@
return modes;
}
-status_t HWComposer::setActiveColorMode(DisplayId displayId, ui::ColorMode mode,
+status_t HWComposer::setActiveColorMode(PhysicalDisplayId displayId, ui::ColorMode mode,
ui::RenderIntent renderIntent) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
@@ -442,14 +431,12 @@
return NO_ERROR;
}
-void HWComposer::setVsyncEnabled(DisplayId displayId, hal::Vsync enabled) {
+void HWComposer::setVsyncEnabled(PhysicalDisplayId displayId, hal::Vsync enabled) {
RETURN_IF_INVALID_DISPLAY(displayId);
auto& displayData = mDisplayData[displayId];
- if (displayData.isVirtual) {
- LOG_DISPLAY_ERROR(displayId, "Invalid operation on virtual display");
- return;
- }
+ LOG_FATAL_IF(displayData.isVirtual, "%s: Invalid operation on virtual display with ID %s",
+ __FUNCTION__, to_string(displayId).c_str());
// NOTE: we use our own internal lock here because we have to call
// into the HWC with the lock held, and we want to make sure
@@ -470,7 +457,7 @@
ATRACE_INT(tag.c_str(), enabled == hal::Vsync::ENABLE ? 1 : 0);
}
-status_t HWComposer::setClientTarget(DisplayId displayId, uint32_t slot,
+status_t HWComposer::setClientTarget(HalDisplayId displayId, uint32_t slot,
const sp<Fence>& acquireFence, const sp<GraphicBuffer>& target,
ui::Dataspace dataspace) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
@@ -483,7 +470,7 @@
}
status_t HWComposer::getDeviceCompositionChanges(
- DisplayId displayId, bool frameUsesClientComposition,
+ HalDisplayId displayId, bool frameUsesClientComposition,
std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) {
ATRACE_CALL();
@@ -553,12 +540,12 @@
return NO_ERROR;
}
-sp<Fence> HWComposer::getPresentFence(DisplayId displayId) const {
+sp<Fence> HWComposer::getPresentFence(HalDisplayId displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, Fence::NO_FENCE);
return mDisplayData.at(displayId).lastPresentFence;
}
-sp<Fence> HWComposer::getLayerReleaseFence(DisplayId displayId, HWC2::Layer* layer) const {
+sp<Fence> HWComposer::getLayerReleaseFence(HalDisplayId displayId, HWC2::Layer* layer) const {
RETURN_IF_INVALID_DISPLAY(displayId, Fence::NO_FENCE);
const auto& displayFences = mDisplayData.at(displayId).releaseFences;
auto fence = displayFences.find(layer);
@@ -569,7 +556,7 @@
return fence->second;
}
-status_t HWComposer::presentAndGetReleaseFences(DisplayId displayId) {
+status_t HWComposer::presentAndGetReleaseFences(HalDisplayId displayId) {
ATRACE_CALL();
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
@@ -597,14 +584,12 @@
return NO_ERROR;
}
-status_t HWComposer::setPowerMode(DisplayId displayId, hal::PowerMode mode) {
+status_t HWComposer::setPowerMode(PhysicalDisplayId displayId, hal::PowerMode mode) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
const auto& displayData = mDisplayData[displayId];
- if (displayData.isVirtual) {
- LOG_DISPLAY_ERROR(displayId, "Invalid operation on virtual display");
- return INVALID_OPERATION;
- }
+ LOG_FATAL_IF(displayData.isVirtual, "%s: Invalid operation on virtual display with ID %s",
+ __FUNCTION__, to_string(displayId).c_str());
if (mode == hal::PowerMode::OFF) {
setVsyncEnabled(displayId, hal::Vsync::DISABLE);
@@ -652,25 +637,20 @@
return NO_ERROR;
}
-status_t HWComposer::setActiveConfigWithConstraints(
- DisplayId displayId, size_t configId, const hal::VsyncPeriodChangeConstraints& constraints,
+status_t HWComposer::setActiveModeWithConstraints(
+ PhysicalDisplayId displayId, hal::HWConfigId hwcModeId,
+ const hal::VsyncPeriodChangeConstraints& constraints,
hal::VsyncPeriodChangeTimeline* outTimeline) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
- auto& displayData = mDisplayData[displayId];
- if (displayData.configMap.count(configId) == 0) {
- LOG_DISPLAY_ERROR(displayId, ("Invalid config " + std::to_string(configId)).c_str());
- return BAD_INDEX;
- }
-
- auto error =
- displayData.hwcDisplay->setActiveConfigWithConstraints(displayData.configMap[configId],
- constraints, outTimeline);
+ auto error = mDisplayData[displayId].hwcDisplay->setActiveConfigWithConstraints(hwcModeId,
+ constraints,
+ outTimeline);
RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
return NO_ERROR;
}
-status_t HWComposer::setColorTransform(DisplayId displayId, const mat4& transform) {
+status_t HWComposer::setColorTransform(HalDisplayId displayId, const mat4& transform) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
auto& displayData = mDisplayData[displayId];
@@ -683,15 +663,14 @@
return NO_ERROR;
}
-void HWComposer::disconnectDisplay(DisplayId displayId) {
+void HWComposer::disconnectDisplay(HalDisplayId displayId) {
RETURN_IF_INVALID_DISPLAY(displayId);
auto& displayData = mDisplayData[displayId];
// If this was a virtual display, add its slot back for reuse by future
// virtual displays
if (displayData.isVirtual) {
- mFreeVirtualDisplayIds.insert(displayId);
- ++mRemainingHwcVirtualDisplays;
+ mVirtualIdGenerator.markUnused(*HalVirtualDisplayId::tryCast(displayId));
}
const auto hwcDisplayId = displayData.hwcDisplay->getId();
@@ -706,27 +685,25 @@
mDisplayData.erase(displayId);
}
-status_t HWComposer::setOutputBuffer(DisplayId displayId, const sp<Fence>& acquireFence,
+status_t HWComposer::setOutputBuffer(HalVirtualDisplayId displayId, const sp<Fence>& acquireFence,
const sp<GraphicBuffer>& buffer) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
const auto& displayData = mDisplayData[displayId];
- if (!displayData.isVirtual) {
- LOG_DISPLAY_ERROR(displayId, "Invalid operation on physical display");
- return INVALID_OPERATION;
- }
+ LOG_FATAL_IF(!displayData.isVirtual, "%s: Invalid operation on physical display with ID %s",
+ __FUNCTION__, to_string(displayId).c_str());
auto error = displayData.hwcDisplay->setOutputBuffer(buffer, acquireFence);
RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
return NO_ERROR;
}
-void HWComposer::clearReleaseFences(DisplayId displayId) {
+void HWComposer::clearReleaseFences(HalDisplayId displayId) {
RETURN_IF_INVALID_DISPLAY(displayId);
mDisplayData[displayId].releaseFences.clear();
}
-status_t HWComposer::getHdrCapabilities(DisplayId displayId, HdrCapabilities* outCapabilities) {
+status_t HWComposer::getHdrCapabilities(HalDisplayId displayId, HdrCapabilities* outCapabilities) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
auto& hwcDisplay = mDisplayData[displayId].hwcDisplay;
@@ -735,12 +712,12 @@
return NO_ERROR;
}
-int32_t HWComposer::getSupportedPerFrameMetadata(DisplayId displayId) const {
+int32_t HWComposer::getSupportedPerFrameMetadata(HalDisplayId displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, 0);
return mDisplayData.at(displayId).hwcDisplay->getSupportedPerFrameMetadata();
}
-std::vector<ui::RenderIntent> HWComposer::getRenderIntents(DisplayId displayId,
+std::vector<ui::RenderIntent> HWComposer::getRenderIntents(HalDisplayId displayId,
ui::ColorMode colorMode) const {
RETURN_IF_INVALID_DISPLAY(displayId, {});
@@ -750,7 +727,7 @@
return renderIntents;
}
-mat4 HWComposer::getDataspaceSaturationMatrix(DisplayId displayId, ui::Dataspace dataspace) {
+mat4 HWComposer::getDataspaceSaturationMatrix(HalDisplayId displayId, ui::Dataspace dataspace) {
RETURN_IF_INVALID_DISPLAY(displayId, {});
mat4 matrix;
@@ -760,7 +737,7 @@
return matrix;
}
-status_t HWComposer::getDisplayedContentSamplingAttributes(DisplayId displayId,
+status_t HWComposer::getDisplayedContentSamplingAttributes(HalDisplayId displayId,
ui::PixelFormat* outFormat,
ui::Dataspace* outDataspace,
uint8_t* outComponentMask) {
@@ -774,7 +751,7 @@
return NO_ERROR;
}
-status_t HWComposer::setDisplayContentSamplingEnabled(DisplayId displayId, bool enabled,
+status_t HWComposer::setDisplayContentSamplingEnabled(HalDisplayId displayId, bool enabled,
uint8_t componentMask, uint64_t maxFrames) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
const auto error =
@@ -788,7 +765,7 @@
return NO_ERROR;
}
-status_t HWComposer::getDisplayedContentSample(DisplayId displayId, uint64_t maxFrames,
+status_t HWComposer::getDisplayedContentSample(HalDisplayId displayId, uint64_t maxFrames,
uint64_t timestamp, DisplayedFrameStats* outStats) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
const auto error =
@@ -798,11 +775,12 @@
return NO_ERROR;
}
-std::future<status_t> HWComposer::setDisplayBrightness(DisplayId displayId, float brightness) {
- RETURN_IF_INVALID_DISPLAY(displayId, promise::yield<status_t>(BAD_INDEX));
+std::future<status_t> HWComposer::setDisplayBrightness(PhysicalDisplayId displayId,
+ float brightness) {
+ RETURN_IF_INVALID_DISPLAY(displayId, ftl::yield<status_t>(BAD_INDEX));
auto& display = mDisplayData[displayId].hwcDisplay;
- return promise::chain(display->setDisplayBrightness(brightness))
+ return ftl::chain(display->setDisplayBrightness(brightness))
.then([displayId](hal::Error error) -> status_t {
if (error == hal::Error::UNSUPPORTED) {
RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
@@ -815,7 +793,7 @@
});
}
-status_t HWComposer::setAutoLowLatencyMode(DisplayId displayId, bool on) {
+status_t HWComposer::setAutoLowLatencyMode(PhysicalDisplayId displayId, bool on) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
const auto error = mDisplayData[displayId].hwcDisplay->setAutoLowLatencyMode(on);
if (error == hal::Error::UNSUPPORTED) {
@@ -829,7 +807,7 @@
}
status_t HWComposer::getSupportedContentTypes(
- DisplayId displayId, std::vector<hal::ContentType>* outSupportedContentTypes) {
+ PhysicalDisplayId displayId, std::vector<hal::ContentType>* outSupportedContentTypes) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
const auto error =
mDisplayData[displayId].hwcDisplay->getSupportedContentTypes(outSupportedContentTypes);
@@ -839,7 +817,7 @@
return NO_ERROR;
}
-status_t HWComposer::setContentType(DisplayId displayId, hal::ContentType contentType) {
+status_t HWComposer::setContentType(PhysicalDisplayId displayId, hal::ContentType contentType) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
const auto error = mDisplayData[displayId].hwcDisplay->setContentType(contentType);
if (error == hal::Error::UNSUPPORTED) {
@@ -861,7 +839,8 @@
result.append(mComposer->dumpDebugInfo());
}
-std::optional<DisplayId> HWComposer::toPhysicalDisplayId(hal::HWDisplayId hwcDisplayId) const {
+std::optional<PhysicalDisplayId> HWComposer::toPhysicalDisplayId(
+ hal::HWDisplayId hwcDisplayId) const {
if (const auto it = mPhysicalDisplayIdMap.find(hwcDisplayId);
it != mPhysicalDisplayIdMap.end()) {
return it->second;
@@ -869,7 +848,8 @@
return {};
}
-std::optional<hal::HWDisplayId> HWComposer::fromPhysicalDisplayId(DisplayId displayId) const {
+std::optional<hal::HWDisplayId> HWComposer::fromPhysicalDisplayId(
+ PhysicalDisplayId displayId) const {
if (const auto it = mDisplayData.find(displayId);
it != mDisplayData.end() && !it->second.isVirtual) {
return it->second.hwcDisplay->getId();
@@ -900,6 +880,16 @@
info = DisplayIdentificationInfo{.id = *displayId,
.name = std::string(),
.deviceProductInfo = std::nullopt};
+ if (mUpdateDeviceProductInfoOnHotplugReconnect) {
+ uint8_t port;
+ DisplayIdentificationData data;
+ getDisplayIdentificationData(hwcDisplayId, &port, &data);
+ if (auto newInfo = parseDisplayIdentificationData(port, data)) {
+ info->deviceProductInfo = std::move(newInfo->deviceProductInfo);
+ } else {
+ ALOGE("Failed to parse identification data for display %" PRIu64, hwcDisplayId);
+ }
+ }
} else {
uint8_t port;
DisplayIdentificationData data;
@@ -928,7 +918,7 @@
port = isPrimary ? LEGACY_DISPLAY_TYPE_PRIMARY : LEGACY_DISPLAY_TYPE_EXTERNAL;
}
- return DisplayIdentificationInfo{.id = getFallbackDisplayId(port),
+ return DisplayIdentificationInfo{.id = PhysicalDisplayId::fromPort(port),
.name = isPrimary ? "Internal display"
: "External display",
.deviceProductInfo = std::nullopt};
@@ -977,8 +967,10 @@
std::vector<Hwc2::IComposerClient::LayerGenericMetadataKey> supportedMetadataKeyInfo;
const auto error = mComposer->getLayerGenericMetadataKeys(&supportedMetadataKeyInfo);
if (error != hardware::graphics::composer::V2_4::Error::NONE) {
- ALOGE("%s: %s failed: %s (%d)", __FUNCTION__, "getLayerGenericMetadataKeys",
- toString(error).c_str(), static_cast<int32_t>(error));
+ if (error != hardware::graphics::composer::V2_4::Error::UNSUPPORTED) {
+ ALOGE("%s: %s failed: %s (%d)", __FUNCTION__, "getLayerGenericMetadataKeys",
+ toString(error).c_str(), static_cast<int32_t>(error));
+ }
return;
}
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index e05e41a..f532e50 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -32,13 +32,16 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
#include <ui/GraphicTypes.h>
-#pragma clang diagnostic pop
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
#include <utils/StrongPointer.h>
#include <utils/Timers.h>
+#include "DisplayIdGenerator.h"
#include "DisplayIdentification.h"
+#include "DisplayMode.h"
#include "HWC2.h"
#include "Hal.h"
@@ -64,6 +67,8 @@
const uint32_t id;
};
+// See the comment for SurfaceFlinger::getHwComposer for the thread safety rules for accessing
+// this class.
class HWComposer {
public:
struct DeviceRequestedChanges {
@@ -78,27 +83,42 @@
ClientTargetProperty clientTargetProperty;
};
+ struct HWCDisplayMode {
+ hal::HWConfigId hwcId;
+ int32_t width = -1;
+ int32_t height = -1;
+ nsecs_t vsyncPeriod = -1;
+ int32_t dpiX = -1;
+ int32_t dpiY = -1;
+ int32_t configGroup = -1;
+
+ friend std::ostream& operator<<(std::ostream& os, const HWCDisplayMode& mode) {
+ return os << "id=" << mode.hwcId << " res=" << mode.width << "x" << mode.height
+ << " vsyncPeriod=" << mode.vsyncPeriod << " dpi=" << mode.dpiX << "x"
+ << mode.dpiY << " group=" << mode.configGroup;
+ }
+ };
+
virtual ~HWComposer();
virtual void setConfiguration(HWC2::ComposerCallback* callback, int32_t sequenceId) = 0;
- virtual bool getDisplayIdentificationData(hal::HWDisplayId hwcDisplayId, uint8_t* outPort,
+ virtual bool getDisplayIdentificationData(hal::HWDisplayId, uint8_t* outPort,
DisplayIdentificationData* outData) const = 0;
- virtual bool hasCapability(hal::Capability capability) const = 0;
- virtual bool hasDisplayCapability(DisplayId displayId,
- hal::DisplayCapability capability) const = 0;
+ virtual bool hasCapability(hal::Capability) const = 0;
+ virtual bool hasDisplayCapability(HalDisplayId, hal::DisplayCapability) const = 0;
// Attempts to allocate a virtual display and returns its ID if created on the HWC device.
virtual std::optional<DisplayId> allocateVirtualDisplay(uint32_t width, uint32_t height,
- ui::PixelFormat* format) = 0;
+ ui::PixelFormat*) = 0;
- virtual void allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId, DisplayId displayId) = 0;
+ virtual void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) = 0;
// Attempts to create a new layer on this display
- virtual HWC2::Layer* createLayer(DisplayId displayId) = 0;
+ virtual HWC2::Layer* createLayer(HalDisplayId) = 0;
// Destroy a previously created layer
- virtual void destroyLayer(DisplayId displayId, HWC2::Layer* layer) = 0;
+ virtual void destroyLayer(HalDisplayId, HWC2::Layer*) = 0;
// Gets any required composition change requests from the HWC device.
//
@@ -108,105 +128,99 @@
// with fewer handshakes, but this does not work if client composition is
// expected.
virtual status_t getDeviceCompositionChanges(
- DisplayId, bool frameUsesClientComposition,
+ HalDisplayId, bool frameUsesClientComposition,
std::optional<DeviceRequestedChanges>* outChanges) = 0;
- virtual status_t setClientTarget(DisplayId displayId, uint32_t slot,
- const sp<Fence>& acquireFence, const sp<GraphicBuffer>& target,
- ui::Dataspace dataspace) = 0;
+ virtual status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence,
+ const sp<GraphicBuffer>& target, ui::Dataspace) = 0;
// Present layers to the display and read releaseFences.
- virtual status_t presentAndGetReleaseFences(DisplayId displayId) = 0;
+ virtual status_t presentAndGetReleaseFences(HalDisplayId) = 0;
// set power mode
- virtual status_t setPowerMode(DisplayId displayId, hal::PowerMode mode) = 0;
+ virtual status_t setPowerMode(PhysicalDisplayId, hal::PowerMode) = 0;
// Sets a color transform to be applied to the result of composition
- virtual status_t setColorTransform(DisplayId displayId, const mat4& transform) = 0;
+ virtual status_t setColorTransform(HalDisplayId, const mat4& transform) = 0;
- // reset state when an external, non-virtual display is disconnected
- virtual void disconnectDisplay(DisplayId displayId) = 0;
+ // reset state when a display is disconnected
+ virtual void disconnectDisplay(HalDisplayId) = 0;
// get the present fence received from the last call to present.
- virtual sp<Fence> getPresentFence(DisplayId displayId) const = 0;
+ virtual sp<Fence> getPresentFence(HalDisplayId) const = 0;
// Get last release fence for the given layer
- virtual sp<Fence> getLayerReleaseFence(DisplayId displayId, HWC2::Layer* layer) const = 0;
+ virtual sp<Fence> getLayerReleaseFence(HalDisplayId, HWC2::Layer*) const = 0;
// Set the output buffer and acquire fence for a virtual display.
- // Returns INVALID_OPERATION if displayId is not a virtual display.
- virtual status_t setOutputBuffer(DisplayId displayId, const sp<Fence>& acquireFence,
+ virtual status_t setOutputBuffer(HalVirtualDisplayId, const sp<Fence>& acquireFence,
const sp<GraphicBuffer>& buffer) = 0;
// After SurfaceFlinger has retrieved the release fences for all the frames,
// it can call this to clear the shared pointers in the release fence map
- virtual void clearReleaseFences(DisplayId displayId) = 0;
+ virtual void clearReleaseFences(HalDisplayId) = 0;
// Fetches the HDR capabilities of the given display
- virtual status_t getHdrCapabilities(DisplayId displayId, HdrCapabilities* outCapabilities) = 0;
+ virtual status_t getHdrCapabilities(HalDisplayId, HdrCapabilities* outCapabilities) = 0;
- virtual int32_t getSupportedPerFrameMetadata(DisplayId displayId) const = 0;
+ virtual int32_t getSupportedPerFrameMetadata(HalDisplayId) const = 0;
// Returns the available RenderIntent of the given display.
- virtual std::vector<ui::RenderIntent> getRenderIntents(DisplayId displayId,
- ui::ColorMode colorMode) const = 0;
+ virtual std::vector<ui::RenderIntent> getRenderIntents(HalDisplayId, ui::ColorMode) const = 0;
- virtual mat4 getDataspaceSaturationMatrix(DisplayId displayId, ui::Dataspace dataspace) = 0;
+ virtual mat4 getDataspaceSaturationMatrix(HalDisplayId, ui::Dataspace) = 0;
// Returns the attributes of the color sampling engine.
- virtual status_t getDisplayedContentSamplingAttributes(DisplayId displayId,
- ui::PixelFormat* outFormat,
+ virtual status_t getDisplayedContentSamplingAttributes(HalDisplayId, ui::PixelFormat* outFormat,
ui::Dataspace* outDataspace,
uint8_t* outComponentMask) = 0;
- virtual status_t setDisplayContentSamplingEnabled(DisplayId displayId, bool enabled,
+ virtual status_t setDisplayContentSamplingEnabled(HalDisplayId, bool enabled,
uint8_t componentMask,
uint64_t maxFrames) = 0;
- virtual status_t getDisplayedContentSample(DisplayId displayId, uint64_t maxFrames,
- uint64_t timestamp,
+ virtual status_t getDisplayedContentSample(HalDisplayId, uint64_t maxFrames, uint64_t timestamp,
DisplayedFrameStats* outStats) = 0;
// Sets the brightness of a display.
- virtual std::future<status_t> setDisplayBrightness(DisplayId displayId, float brightness) = 0;
+ virtual std::future<status_t> setDisplayBrightness(PhysicalDisplayId, float brightness) = 0;
// Events handling ---------------------------------------------------------
// Returns stable display ID (and display name on connection of new or previously disconnected
// display), or std::nullopt if hotplug event was ignored.
// This function is called from SurfaceFlinger.
- virtual std::optional<DisplayIdentificationInfo> onHotplug(hal::HWDisplayId hwcDisplayId,
- hal::Connection connection) = 0;
+ virtual std::optional<DisplayIdentificationInfo> onHotplug(hal::HWDisplayId,
+ hal::Connection) = 0;
- virtual bool onVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp) = 0;
- virtual void setVsyncEnabled(DisplayId displayId, hal::Vsync enabled) = 0;
+ // If true we'll update the DeviceProductInfo on subsequent hotplug connected events.
+ // TODO(b/157555476): Remove when the framework has proper support for headless mode
+ virtual bool updatesDeviceProductInfoOnHotplugReconnect() const = 0;
- virtual nsecs_t getRefreshTimestamp(DisplayId displayId) const = 0;
- virtual bool isConnected(DisplayId displayId) const = 0;
+ virtual bool onVsync(hal::HWDisplayId, int64_t timestamp) = 0;
+ virtual void setVsyncEnabled(PhysicalDisplayId, hal::Vsync enabled) = 0;
- // Non-const because it can update configMap inside of mDisplayData
- virtual std::vector<std::shared_ptr<const HWC2::Display::Config>> getConfigs(
- DisplayId displayId) const = 0;
+ virtual bool isConnected(PhysicalDisplayId) const = 0;
- virtual std::shared_ptr<const HWC2::Display::Config> getActiveConfig(
- DisplayId displayId) const = 0;
- virtual int getActiveConfigIndex(DisplayId displayId) const = 0;
+ virtual std::vector<HWCDisplayMode> getModes(PhysicalDisplayId) const = 0;
- virtual std::vector<ui::ColorMode> getColorModes(DisplayId displayId) const = 0;
+ virtual std::optional<hal::HWConfigId> getActiveMode(PhysicalDisplayId) const = 0;
- virtual status_t setActiveColorMode(DisplayId displayId, ui::ColorMode mode,
- ui::RenderIntent renderIntent) = 0;
+ virtual std::vector<ui::ColorMode> getColorModes(PhysicalDisplayId) const = 0;
+
+ virtual status_t setActiveColorMode(PhysicalDisplayId, ui::ColorMode mode,
+ ui::RenderIntent) = 0;
// Composer 2.4
- virtual DisplayConnectionType getDisplayConnectionType(DisplayId) const = 0;
- virtual bool isVsyncPeriodSwitchSupported(DisplayId displayId) const = 0;
- virtual nsecs_t getDisplayVsyncPeriod(DisplayId displayId) const = 0;
- virtual status_t setActiveConfigWithConstraints(
- DisplayId displayId, size_t configId,
- const hal::VsyncPeriodChangeConstraints& constraints,
- hal::VsyncPeriodChangeTimeline* outTimeline) = 0;
- virtual status_t setAutoLowLatencyMode(DisplayId displayId, bool on) = 0;
+ virtual ui::DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const = 0;
+ virtual bool isVsyncPeriodSwitchSupported(PhysicalDisplayId) const = 0;
+ virtual status_t getDisplayVsyncPeriod(PhysicalDisplayId displayId,
+ nsecs_t* outVsyncPeriod) const = 0;
+ virtual status_t setActiveModeWithConstraints(PhysicalDisplayId, hal::HWConfigId,
+ const hal::VsyncPeriodChangeConstraints&,
+ hal::VsyncPeriodChangeTimeline* outTimeline) = 0;
+ virtual status_t setAutoLowLatencyMode(PhysicalDisplayId, bool on) = 0;
virtual status_t getSupportedContentTypes(
- DisplayId displayId, std::vector<hal::ContentType>* outSupportedContentTypes) = 0;
- virtual status_t setContentType(DisplayId displayId, hal::ContentType contentType) = 0;
+ PhysicalDisplayId, std::vector<hal::ContentType>* outSupportedContentTypes) = 0;
+ virtual status_t setContentType(PhysicalDisplayId, hal::ContentType) = 0;
virtual const std::unordered_map<std::string, bool>& getSupportedLayerGenericMetadata()
const = 0;
@@ -219,8 +233,8 @@
virtual std::optional<hal::HWDisplayId> getInternalHwcDisplayId() const = 0;
virtual std::optional<hal::HWDisplayId> getExternalHwcDisplayId() const = 0;
- virtual std::optional<DisplayId> toPhysicalDisplayId(hal::HWDisplayId hwcDisplayId) const = 0;
- virtual std::optional<hal::HWDisplayId> fromPhysicalDisplayId(DisplayId displayId) const = 0;
+ virtual std::optional<PhysicalDisplayId> toPhysicalDisplayId(hal::HWDisplayId) const = 0;
+ virtual std::optional<hal::HWDisplayId> fromPhysicalDisplayId(PhysicalDisplayId) const = 0;
};
namespace impl {
@@ -234,116 +248,109 @@
void setConfiguration(HWC2::ComposerCallback* callback, int32_t sequenceId) override;
- bool getDisplayIdentificationData(hal::HWDisplayId hwcDisplayId, uint8_t* outPort,
+ bool getDisplayIdentificationData(hal::HWDisplayId, uint8_t* outPort,
DisplayIdentificationData* outData) const override;
- bool hasCapability(hal::Capability capability) const override;
- bool hasDisplayCapability(DisplayId displayId,
- hal::DisplayCapability capability) const override;
+ bool hasCapability(hal::Capability) const override;
+ bool hasDisplayCapability(HalDisplayId, hal::DisplayCapability) const override;
// Attempts to allocate a virtual display and returns its ID if created on the HWC device.
std::optional<DisplayId> allocateVirtualDisplay(uint32_t width, uint32_t height,
- ui::PixelFormat* format) override;
+ ui::PixelFormat*) override;
// Called from SurfaceFlinger, when the state for a new physical display needs to be recreated.
- void allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId, DisplayId displayId) override;
+ void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) override;
// Attempts to create a new layer on this display
- HWC2::Layer* createLayer(DisplayId displayId) override;
+ HWC2::Layer* createLayer(HalDisplayId) override;
// Destroy a previously created layer
- void destroyLayer(DisplayId displayId, HWC2::Layer* layer) override;
+ void destroyLayer(HalDisplayId, HWC2::Layer*) override;
status_t getDeviceCompositionChanges(
- DisplayId, bool frameUsesClientComposition,
+ HalDisplayId, bool frameUsesClientComposition,
std::optional<DeviceRequestedChanges>* outChanges) override;
- status_t setClientTarget(DisplayId displayId, uint32_t slot, const sp<Fence>& acquireFence,
- const sp<GraphicBuffer>& target, ui::Dataspace dataspace) override;
+ status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence,
+ const sp<GraphicBuffer>& target, ui::Dataspace) override;
// Present layers to the display and read releaseFences.
- status_t presentAndGetReleaseFences(DisplayId displayId) override;
+ status_t presentAndGetReleaseFences(HalDisplayId) override;
// set power mode
- status_t setPowerMode(DisplayId displayId, hal::PowerMode mode) override;
+ status_t setPowerMode(PhysicalDisplayId, hal::PowerMode mode) override;
// Sets a color transform to be applied to the result of composition
- status_t setColorTransform(DisplayId displayId, const mat4& transform) override;
+ status_t setColorTransform(HalDisplayId, const mat4& transform) override;
- // reset state when an external, non-virtual display is disconnected
- void disconnectDisplay(DisplayId displayId) override;
+ // reset state when a display is disconnected
+ void disconnectDisplay(HalDisplayId) override;
// get the present fence received from the last call to present.
- sp<Fence> getPresentFence(DisplayId displayId) const override;
+ sp<Fence> getPresentFence(HalDisplayId) const override;
// Get last release fence for the given layer
- sp<Fence> getLayerReleaseFence(DisplayId displayId, HWC2::Layer* layer) const override;
+ sp<Fence> getLayerReleaseFence(HalDisplayId, HWC2::Layer*) const override;
// Set the output buffer and acquire fence for a virtual display.
- // Returns INVALID_OPERATION if displayId is not a virtual display.
- status_t setOutputBuffer(DisplayId displayId, const sp<Fence>& acquireFence,
+ status_t setOutputBuffer(HalVirtualDisplayId, const sp<Fence>& acquireFence,
const sp<GraphicBuffer>& buffer) override;
// After SurfaceFlinger has retrieved the release fences for all the frames,
// it can call this to clear the shared pointers in the release fence map
- void clearReleaseFences(DisplayId displayId) override;
+ void clearReleaseFences(HalDisplayId) override;
// Fetches the HDR capabilities of the given display
- status_t getHdrCapabilities(DisplayId displayId, HdrCapabilities* outCapabilities) override;
+ status_t getHdrCapabilities(HalDisplayId, HdrCapabilities* outCapabilities) override;
- int32_t getSupportedPerFrameMetadata(DisplayId displayId) const override;
+ int32_t getSupportedPerFrameMetadata(HalDisplayId) const override;
// Returns the available RenderIntent of the given display.
- std::vector<ui::RenderIntent> getRenderIntents(DisplayId displayId,
- ui::ColorMode colorMode) const override;
+ std::vector<ui::RenderIntent> getRenderIntents(HalDisplayId, ui::ColorMode) const override;
- mat4 getDataspaceSaturationMatrix(DisplayId displayId, ui::Dataspace dataspace) override;
+ mat4 getDataspaceSaturationMatrix(HalDisplayId, ui::Dataspace) override;
// Returns the attributes of the color sampling engine.
- status_t getDisplayedContentSamplingAttributes(DisplayId displayId, ui::PixelFormat* outFormat,
+ status_t getDisplayedContentSamplingAttributes(HalDisplayId, ui::PixelFormat* outFormat,
ui::Dataspace* outDataspace,
uint8_t* outComponentMask) override;
- status_t setDisplayContentSamplingEnabled(DisplayId displayId, bool enabled,
- uint8_t componentMask, uint64_t maxFrames) override;
- status_t getDisplayedContentSample(DisplayId displayId, uint64_t maxFrames, uint64_t timestamp,
+ status_t setDisplayContentSamplingEnabled(HalDisplayId, bool enabled, uint8_t componentMask,
+ uint64_t maxFrames) override;
+ status_t getDisplayedContentSample(HalDisplayId, uint64_t maxFrames, uint64_t timestamp,
DisplayedFrameStats* outStats) override;
- std::future<status_t> setDisplayBrightness(DisplayId displayId, float brightness) override;
+ std::future<status_t> setDisplayBrightness(PhysicalDisplayId, float brightness) override;
// Events handling ---------------------------------------------------------
- // Returns stable display ID (and display name on connection of new or previously disconnected
+ // Returns PhysicalDisplayId (and display name on connection of new or previously disconnected
// display), or std::nullopt if hotplug event was ignored.
- std::optional<DisplayIdentificationInfo> onHotplug(hal::HWDisplayId hwcDisplayId,
- hal::Connection connection) override;
+ std::optional<DisplayIdentificationInfo> onHotplug(hal::HWDisplayId, hal::Connection) override;
- bool onVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp) override;
- void setVsyncEnabled(DisplayId displayId, hal::Vsync enabled) override;
+ bool updatesDeviceProductInfoOnHotplugReconnect() const override;
- nsecs_t getRefreshTimestamp(DisplayId displayId) const override;
- bool isConnected(DisplayId displayId) const override;
+ bool onVsync(hal::HWDisplayId, int64_t timestamp) override;
+ void setVsyncEnabled(PhysicalDisplayId, hal::Vsync enabled) override;
- // Non-const because it can update configMap inside of mDisplayData
- std::vector<std::shared_ptr<const HWC2::Display::Config>> getConfigs(
- DisplayId displayId) const override;
+ bool isConnected(PhysicalDisplayId) const override;
- std::shared_ptr<const HWC2::Display::Config> getActiveConfig(
- DisplayId displayId) const override;
- int getActiveConfigIndex(DisplayId displayId) const override;
+ std::vector<HWCDisplayMode> getModes(PhysicalDisplayId) const override;
- std::vector<ui::ColorMode> getColorModes(DisplayId displayId) const override;
+ std::optional<hal::HWConfigId> getActiveMode(PhysicalDisplayId) const override;
- status_t setActiveColorMode(DisplayId displayId, ui::ColorMode mode,
- ui::RenderIntent renderIntent) override;
+ std::vector<ui::ColorMode> getColorModes(PhysicalDisplayId) const override;
+
+ status_t setActiveColorMode(PhysicalDisplayId, ui::ColorMode, ui::RenderIntent) override;
// Composer 2.4
- DisplayConnectionType getDisplayConnectionType(DisplayId) const override;
- bool isVsyncPeriodSwitchSupported(DisplayId displayId) const override;
- nsecs_t getDisplayVsyncPeriod(DisplayId displayId) const override;
- status_t setActiveConfigWithConstraints(DisplayId displayId, size_t configId,
- const hal::VsyncPeriodChangeConstraints& constraints,
- hal::VsyncPeriodChangeTimeline* outTimeline) override;
- status_t setAutoLowLatencyMode(DisplayId displayId, bool) override;
- status_t getSupportedContentTypes(DisplayId displayId, std::vector<hal::ContentType>*) override;
- status_t setContentType(DisplayId displayId, hal::ContentType) override;
+ ui::DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const override;
+ bool isVsyncPeriodSwitchSupported(PhysicalDisplayId) const override;
+ status_t getDisplayVsyncPeriod(PhysicalDisplayId displayId,
+ nsecs_t* outVsyncPeriod) const override;
+ status_t setActiveModeWithConstraints(PhysicalDisplayId, hal::HWConfigId,
+ const hal::VsyncPeriodChangeConstraints&,
+ hal::VsyncPeriodChangeTimeline* outTimeline) override;
+ status_t setAutoLowLatencyMode(PhysicalDisplayId, bool) override;
+ status_t getSupportedContentTypes(PhysicalDisplayId, std::vector<hal::ContentType>*) override;
+ status_t setContentType(PhysicalDisplayId, hal::ContentType) override;
const std::unordered_map<std::string, bool>& getSupportedLayerGenericMetadata() const override;
@@ -360,22 +367,13 @@
return mExternalHwcDisplayId;
}
- std::optional<DisplayId> toPhysicalDisplayId(hal::HWDisplayId hwcDisplayId) const override;
- std::optional<hal::HWDisplayId> fromPhysicalDisplayId(DisplayId displayId) const override;
+ std::optional<PhysicalDisplayId> toPhysicalDisplayId(hal::HWDisplayId) const override;
+ std::optional<hal::HWDisplayId> fromPhysicalDisplayId(PhysicalDisplayId) const override;
private:
// For unit tests
friend TestableSurfaceFlinger;
- std::optional<DisplayIdentificationInfo> onHotplugConnect(hal::HWDisplayId hwcDisplayId);
- std::optional<DisplayIdentificationInfo> onHotplugDisconnect(hal::HWDisplayId hwcDisplayId);
- bool shouldIgnoreHotplugConnect(hal::HWDisplayId hwcDisplayId,
- bool hasDisplayIdentificationData) const;
-
- void loadCapabilities();
- void loadLayerMetadataSupport();
- uint32_t getMaxVirtualDisplayCount() const;
-
struct DisplayData {
bool isVirtual = false;
std::unique_ptr<HWC2::Display> hwcDisplay;
@@ -383,8 +381,6 @@
std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences;
buffer_handle_t outbufHandle = nullptr;
sp<Fence> outbufAcquireFence = Fence::NO_FENCE;
- mutable std::unordered_map<int32_t,
- std::shared_ptr<const HWC2::Display::Config>> configMap;
bool validateWasSkipped;
hal::Error presentError;
@@ -394,25 +390,35 @@
std::mutex vsyncEnabledLock;
hal::Vsync vsyncEnabled GUARDED_BY(vsyncEnabledLock) = hal::Vsync::DISABLE;
- mutable std::mutex lastHwVsyncLock;
- nsecs_t lastHwVsync GUARDED_BY(lastHwVsyncLock) = 0;
+ nsecs_t lastHwVsync = 0;
};
- std::unordered_map<DisplayId, DisplayData> mDisplayData;
+ std::optional<DisplayIdentificationInfo> onHotplugConnect(hal::HWDisplayId);
+ std::optional<DisplayIdentificationInfo> onHotplugDisconnect(hal::HWDisplayId);
+ bool shouldIgnoreHotplugConnect(hal::HWDisplayId, bool hasDisplayIdentificationData) const;
+
+ int32_t getAttribute(hal::HWDisplayId hwcDisplayId, hal::HWConfigId configId,
+ hal::Attribute attribute) const;
+
+ void loadCapabilities();
+ void loadLayerMetadataSupport();
+ uint32_t getMaxVirtualDisplayCount() const;
+
+ std::unordered_map<HalDisplayId, DisplayData> mDisplayData;
std::unique_ptr<android::Hwc2::Composer> mComposer;
std::unordered_set<hal::Capability> mCapabilities;
std::unordered_map<std::string, bool> mSupportedLayerGenericMetadata;
bool mRegisteredCallback = false;
- std::unordered_map<hal::HWDisplayId, DisplayId> mPhysicalDisplayIdMap;
+ std::unordered_map<hal::HWDisplayId, PhysicalDisplayId> mPhysicalDisplayIdMap;
std::optional<hal::HWDisplayId> mInternalHwcDisplayId;
std::optional<hal::HWDisplayId> mExternalHwcDisplayId;
bool mHasMultiDisplaySupport = false;
- std::unordered_set<DisplayId> mFreeVirtualDisplayIds;
- uint32_t mNextVirtualDisplayId = 0;
- uint32_t mRemainingHwcVirtualDisplays{getMaxVirtualDisplayCount()};
+ RandomDisplayIdGenerator<HalVirtualDisplayId> mVirtualIdGenerator{getMaxVirtualDisplayCount()};
+
+ const bool mUpdateDeviceProductInfoOnHotplugReconnect;
};
} // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 4b4c050..901e19a 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -64,7 +64,7 @@
PowerAdvisor::PowerAdvisor()
: mUseUpdateImminentTimer(getUpdateTimeout() > 0),
mUpdateImminentTimer(
- OneShotTimer::Interval(getUpdateTimeout()),
+ "UpdateImminentTimer", OneShotTimer::Interval(getUpdateTimeout()),
/* resetCallback */ [this] { mSendUpdateImminent.store(false); },
/* timeoutCallback */ [this] { mSendUpdateImminent.store(true); }) {
if (mUseUpdateImminentTimer) {
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index c8cd442..e26ab11 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -57,8 +57,7 @@
}
}
-VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc,
- const std::optional<DisplayId>& displayId,
+VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, VirtualDisplayId displayId,
const sp<IGraphicBufferProducer>& sink,
const sp<IGraphicBufferProducer>& bqProducer,
const sp<IGraphicBufferConsumer>& bqConsumer,
@@ -126,7 +125,7 @@
}
status_t VirtualDisplaySurface::beginFrame(bool mustRecompose) {
- if (!mDisplayId) {
+ if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
return NO_ERROR;
}
@@ -140,7 +139,7 @@
}
status_t VirtualDisplaySurface::prepareFrame(CompositionType compositionType) {
- if (!mDisplayId) {
+ if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
return NO_ERROR;
}
@@ -188,7 +187,7 @@
}
status_t VirtualDisplaySurface::advanceFrame() {
- if (!mDisplayId) {
+ if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
return NO_ERROR;
}
@@ -220,9 +219,11 @@
mFbProducerSlot, fbBuffer.get(),
mOutputProducerSlot, outBuffer.get());
+ const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId);
+ LOG_FATAL_IF(!halDisplayId);
// At this point we know the output buffer acquire fence,
// so update HWC state with it.
- mHwc.setOutputBuffer(*mDisplayId, mOutputFence, outBuffer);
+ mHwc.setOutputBuffer(*halDisplayId, mOutputFence, outBuffer);
status_t result = NO_ERROR;
if (fbBuffer != nullptr) {
@@ -231,7 +232,7 @@
mHwcBufferCache.getHwcBuffer(mFbProducerSlot, fbBuffer, &hwcSlot, &hwcBuffer);
// TODO: Correctly propagate the dataspace from GL composition
- result = mHwc.setClientTarget(*mDisplayId, hwcSlot, mFbFence, hwcBuffer,
+ result = mHwc.setClientTarget(*halDisplayId, hwcSlot, mFbFence, hwcBuffer,
ui::Dataspace::UNKNOWN);
}
@@ -239,7 +240,8 @@
}
void VirtualDisplaySurface::onFrameCommitted() {
- if (!mDisplayId) {
+ const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId);
+ if (!halDisplayId) {
return;
}
@@ -247,7 +249,7 @@
"Unexpected onFrameCommitted() in %s state", dbgStateStr());
mDbgState = DBG_STATE_IDLE;
- sp<Fence> retireFence = mHwc.getPresentFence(*mDisplayId);
+ sp<Fence> retireFence = mHwc.getPresentFence(*halDisplayId);
if (mCompositionType == COMPOSITION_MIXED && mFbProducerSlot >= 0) {
// release the scratch buffer back to the pool
Mutex::Autolock lock(mMutex);
@@ -289,11 +291,11 @@
void VirtualDisplaySurface::dumpAsString(String8& /* result */) const {
}
-void VirtualDisplaySurface::resizeBuffers(const uint32_t w, const uint32_t h) {
- mQueueBufferOutput.width = w;
- mQueueBufferOutput.height = h;
- mSinkBufferWidth = w;
- mSinkBufferHeight = h;
+void VirtualDisplaySurface::resizeBuffers(const ui::Size& newSize) {
+ mQueueBufferOutput.width = newSize.width;
+ mQueueBufferOutput.height = newSize.height;
+ mSinkBufferWidth = newSize.width;
+ mSinkBufferHeight = newSize.height;
}
const sp<Fence>& VirtualDisplaySurface::getClientTargetAcquireFence() const {
@@ -302,7 +304,7 @@
status_t VirtualDisplaySurface::requestBuffer(int pslot,
sp<GraphicBuffer>* outBuf) {
- if (!mDisplayId) {
+ if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
return mSource[SOURCE_SINK]->requestBuffer(pslot, outBuf);
}
@@ -324,7 +326,7 @@
status_t VirtualDisplaySurface::dequeueBuffer(Source source,
PixelFormat format, uint64_t usage, int* sslot, sp<Fence>* fence) {
- LOG_FATAL_IF(!mDisplayId);
+ LOG_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId));
status_t result =
mSource[source]->dequeueBuffer(sslot, fence, mSinkBufferWidth, mSinkBufferHeight,
@@ -377,7 +379,7 @@
PixelFormat format, uint64_t usage,
uint64_t* outBufferAge,
FrameEventHistoryDelta* outTimestamps) {
- if (!mDisplayId) {
+ if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
return mSource[SOURCE_SINK]->dequeueBuffer(pslot, fence, w, h, format, usage, outBufferAge,
outTimestamps);
}
@@ -469,7 +471,7 @@
status_t VirtualDisplaySurface::queueBuffer(int pslot,
const QueueBufferInput& input, QueueBufferOutput* output) {
- if (!mDisplayId) {
+ if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
return mSource[SOURCE_SINK]->queueBuffer(pslot, input, output);
}
@@ -527,7 +529,7 @@
status_t VirtualDisplaySurface::cancelBuffer(int pslot,
const sp<Fence>& fence) {
- if (!mDisplayId) {
+ if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
return mSource[SOURCE_SINK]->cancelBuffer(mapProducer2SourceSlot(SOURCE_SINK, pslot), fence);
}
@@ -639,7 +641,7 @@
}
status_t VirtualDisplaySurface::refreshOutputBuffer() {
- LOG_FATAL_IF(!mDisplayId);
+ LOG_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId));
if (mOutputProducerSlot >= 0) {
mSource[SOURCE_SINK]->cancelBuffer(
@@ -658,7 +660,9 @@
// until after GPU calls queueBuffer(). So here we just set the buffer
// (for use in HWC prepare) but not the fence; we'll call this again with
// the proper fence once we have it.
- result = mHwc.setOutputBuffer(*mDisplayId, Fence::NO_FENCE,
+ const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId);
+ LOG_FATAL_IF(!halDisplayId);
+ result = mHwc.setOutputBuffer(*halDisplayId, Fence::NO_FENCE,
mProducerBuffers[mOutputProducerSlot]);
return result;
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 22b5528..bbb6306 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -24,6 +24,7 @@
#include <compositionengine/impl/HwcBufferCache.h>
#include <gui/ConsumerBase.h>
#include <gui/IGraphicBufferProducer.h>
+#include <ui/DisplayId.h>
#include "DisplayIdentification.h"
@@ -77,8 +78,7 @@
public BnGraphicBufferProducer,
private ConsumerBase {
public:
- VirtualDisplaySurface(HWComposer& hwc, const std::optional<DisplayId>& displayId,
- const sp<IGraphicBufferProducer>& sink,
+ VirtualDisplaySurface(HWComposer&, VirtualDisplayId, const sp<IGraphicBufferProducer>& sink,
const sp<IGraphicBufferProducer>& bqProducer,
const sp<IGraphicBufferConsumer>& bqConsumer, const std::string& name);
@@ -86,11 +86,11 @@
// DisplaySurface interface
//
virtual status_t beginFrame(bool mustRecompose);
- virtual status_t prepareFrame(CompositionType compositionType);
+ virtual status_t prepareFrame(CompositionType);
virtual status_t advanceFrame();
virtual void onFrameCommitted();
virtual void dumpAsString(String8& result) const;
- virtual void resizeBuffers(const uint32_t w, const uint32_t h);
+ virtual void resizeBuffers(const ui::Size&) override;
virtual const sp<Fence>& getClientTargetAcquireFence() const override;
private:
@@ -104,25 +104,22 @@
virtual status_t requestBuffer(int pslot, sp<GraphicBuffer>* outBuf);
virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers);
virtual status_t setAsyncMode(bool async);
- virtual status_t dequeueBuffer(int* pslot, sp<Fence>* fence, uint32_t w, uint32_t h,
- PixelFormat format, uint64_t usage, uint64_t* outBufferAge,
+ virtual status_t dequeueBuffer(int* pslot, sp<Fence>*, uint32_t w, uint32_t h, PixelFormat,
+ uint64_t usage, uint64_t* outBufferAge,
FrameEventHistoryDelta* outTimestamps);
virtual status_t detachBuffer(int slot);
- virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
- sp<Fence>* outFence);
- virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer);
- virtual status_t queueBuffer(int pslot,
- const QueueBufferInput& input, QueueBufferOutput* output);
- virtual status_t cancelBuffer(int pslot, const sp<Fence>& fence);
+ virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence);
+ virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>&);
+ virtual status_t queueBuffer(int pslot, const QueueBufferInput&, QueueBufferOutput*);
+ virtual status_t cancelBuffer(int pslot, const sp<Fence>&);
virtual int query(int what, int* value);
- virtual status_t connect(const sp<IProducerListener>& listener,
- int api, bool producerControlledByApp, QueueBufferOutput* output);
- virtual status_t disconnect(int api, DisconnectMode mode);
+ virtual status_t connect(const sp<IProducerListener>&, int api, bool producerControlledByApp,
+ QueueBufferOutput*);
+ virtual status_t disconnect(int api, DisconnectMode);
virtual status_t setSidebandStream(const sp<NativeHandle>& stream);
- virtual void allocateBuffers(uint32_t width, uint32_t height,
- PixelFormat format, uint64_t usage);
+ virtual void allocateBuffers(uint32_t width, uint32_t height, PixelFormat, uint64_t usage);
virtual status_t allowAllocation(bool allow);
- virtual status_t setGenerationNumber(uint32_t generationNumber);
+ virtual status_t setGenerationNumber(uint32_t);
virtual String8 getConsumerName() const override;
virtual status_t setSharedBufferMode(bool sharedBufferMode) override;
virtual status_t setAutoRefresh(bool autoRefresh) override;
@@ -135,10 +132,9 @@
//
// Utility methods
//
- static Source fbSourceForCompositionType(CompositionType type);
- status_t dequeueBuffer(Source source, PixelFormat format, uint64_t usage,
- int* sslot, sp<Fence>* fence);
- void updateQueueBufferOutput(QueueBufferOutput&& qbo);
+ static Source fbSourceForCompositionType(CompositionType);
+ status_t dequeueBuffer(Source, PixelFormat, uint64_t usage, int* sslot, sp<Fence>*);
+ void updateQueueBufferOutput(QueueBufferOutput&&);
void resetPerFrameState();
status_t refreshOutputBuffer();
@@ -148,14 +144,14 @@
// internally in the VirtualDisplaySurface. To minimize the number of times
// a producer slot switches which source it comes from, we map source slot
// numbers to producer slot numbers differently for each source.
- static int mapSource2ProducerSlot(Source source, int sslot);
- static int mapProducer2SourceSlot(Source source, int pslot);
+ static int mapSource2ProducerSlot(Source, int sslot);
+ static int mapProducer2SourceSlot(Source, int pslot);
//
// Immutable after construction
//
HWComposer& mHwc;
- const std::optional<DisplayId> mDisplayId;
+ const VirtualDisplayId mDisplayId;
const std::string mDisplayName;
sp<IGraphicBufferProducer> mSource[2]; // indexed by SOURCE_*
uint32_t mDefaultOutputFormat;
diff --git a/services/surfaceflinger/DisplayIdGenerator.h b/services/surfaceflinger/DisplayIdGenerator.h
new file mode 100644
index 0000000..e7c69a8
--- /dev/null
+++ b/services/surfaceflinger/DisplayIdGenerator.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <ui/DisplayId.h>
+
+#include <limits>
+#include <optional>
+#include <random>
+#include <unordered_set>
+
+#include <log/log.h>
+
+namespace android {
+
+template <typename T>
+class DisplayIdGenerator {
+public:
+ virtual std::optional<T> nextId() = 0;
+ virtual void markUnused(T id) = 0;
+
+protected:
+ ~DisplayIdGenerator() {}
+};
+
+template <typename T>
+class RandomDisplayIdGenerator final : public DisplayIdGenerator<T> {
+public:
+ explicit RandomDisplayIdGenerator(size_t maxIdsCount = std::numeric_limits<size_t>::max())
+ : mMaxIdsCount(maxIdsCount) {}
+
+ std::optional<T> nextId() override {
+ if (mUsedIds.size() >= mMaxIdsCount) {
+ return std::nullopt;
+ }
+
+ constexpr int kMaxAttempts = 1000;
+
+ for (int attempts = 0; attempts < kMaxAttempts; attempts++) {
+ const auto baseId = mDistribution(mGenerator);
+ const T id(baseId);
+ if (mUsedIds.count(id) == 0) {
+ mUsedIds.insert(id);
+ return id;
+ }
+ }
+
+ LOG_ALWAYS_FATAL("Couldn't generate ID after %d attempts", kMaxAttempts);
+ }
+
+ void markUnused(T id) override { mUsedIds.erase(id); }
+
+private:
+ const size_t mMaxIdsCount;
+
+ std::unordered_set<T> mUsedIds;
+ std::default_random_engine mGenerator{std::random_device()()};
+ std::uniform_int_distribution<typename T::BaseId> mDistribution;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/DisplayRenderArea.cpp b/services/surfaceflinger/DisplayRenderArea.cpp
new file mode 100644
index 0000000..20486e0
--- /dev/null
+++ b/services/surfaceflinger/DisplayRenderArea.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2020 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 "DisplayRenderArea.h"
+#include "DisplayDevice.h"
+
+namespace android {
+namespace {
+
+RenderArea::RotationFlags applyDeviceOrientation(bool useIdentityTransform,
+ const DisplayDevice& display) {
+ if (!useIdentityTransform) {
+ return RenderArea::RotationFlags::ROT_0;
+ }
+
+ return ui::Transform::toRotationFlags(display.getOrientation());
+}
+
+} // namespace
+
+std::unique_ptr<RenderArea> DisplayRenderArea::create(wp<const DisplayDevice> displayWeak,
+ const Rect& sourceCrop, ui::Size reqSize,
+ ui::Dataspace reqDataSpace,
+ bool useIdentityTransform,
+ bool allowSecureLayers) {
+ if (auto display = displayWeak.promote()) {
+ // Using new to access a private constructor.
+ return std::unique_ptr<DisplayRenderArea>(
+ new DisplayRenderArea(std::move(display), sourceCrop, reqSize, reqDataSpace,
+ useIdentityTransform, allowSecureLayers));
+ }
+ return nullptr;
+}
+
+DisplayRenderArea::DisplayRenderArea(sp<const DisplayDevice> display, const Rect& sourceCrop,
+ ui::Size reqSize, ui::Dataspace reqDataSpace,
+ bool useIdentityTransform, bool allowSecureLayers)
+ : RenderArea(reqSize, CaptureFill::OPAQUE, reqDataSpace, display->getLayerStackSpaceRect(),
+ allowSecureLayers, applyDeviceOrientation(useIdentityTransform, *display)),
+ mDisplay(std::move(display)),
+ mSourceCrop(sourceCrop) {}
+
+const ui::Transform& DisplayRenderArea::getTransform() const {
+ return mTransform;
+}
+
+Rect DisplayRenderArea::getBounds() const {
+ return mDisplay->getBounds();
+}
+
+int DisplayRenderArea::getHeight() const {
+ return mDisplay->getHeight();
+}
+
+int DisplayRenderArea::getWidth() const {
+ return mDisplay->getWidth();
+}
+
+bool DisplayRenderArea::isSecure() const {
+ return mAllowSecureLayers && mDisplay->isSecure();
+}
+
+sp<const DisplayDevice> DisplayRenderArea::getDisplayDevice() const {
+ return mDisplay;
+}
+
+bool DisplayRenderArea::needsFiltering() const {
+ // check if the projection from the logical render area
+ // to the physical render area requires filtering
+ const Rect& sourceCrop = getSourceCrop();
+ int width = sourceCrop.width();
+ int height = sourceCrop.height();
+ if (getRotationFlags() & ui::Transform::ROT_90) {
+ std::swap(width, height);
+ }
+ return width != getReqWidth() || height != getReqHeight();
+}
+
+Rect DisplayRenderArea::getSourceCrop() const {
+ // use the projected display viewport by default.
+ if (mSourceCrop.isEmpty()) {
+ return mDisplay->getLayerStackSpaceRect();
+ }
+
+ // Correct for the orientation when the screen capture request contained
+ // useIdentityTransform. This will cause the rotation flag to be non 0 since
+ // it needs to rotate based on the screen orientation to allow the screenshot
+ // to be taken in the ROT_0 orientation
+ const auto flags = getRotationFlags();
+ int width = mDisplay->getLayerStackSpaceRect().getWidth();
+ int height = mDisplay->getLayerStackSpaceRect().getHeight();
+ ui::Transform rotation;
+ rotation.set(flags, width, height);
+ return rotation.transform(mSourceCrop);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/DisplayRenderArea.h b/services/surfaceflinger/DisplayRenderArea.h
new file mode 100644
index 0000000..3478fc1
--- /dev/null
+++ b/services/surfaceflinger/DisplayRenderArea.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <ui/GraphicTypes.h>
+#include <ui/Transform.h>
+
+#include "RenderArea.h"
+
+namespace android {
+
+class DisplayDevice;
+
+class DisplayRenderArea : public RenderArea {
+public:
+ static std::unique_ptr<RenderArea> create(wp<const DisplayDevice>, const Rect& sourceCrop,
+ ui::Size reqSize, ui::Dataspace,
+ bool useIdentityTransform,
+ bool allowSecureLayers = true);
+
+ const ui::Transform& getTransform() const override;
+ Rect getBounds() const override;
+ int getHeight() const override;
+ int getWidth() const override;
+ bool isSecure() const override;
+ sp<const DisplayDevice> getDisplayDevice() const override;
+ bool needsFiltering() const override;
+ Rect getSourceCrop() const override;
+
+private:
+ DisplayRenderArea(sp<const DisplayDevice>, const Rect& sourceCrop, ui::Size reqSize,
+ ui::Dataspace, bool useIdentityTransform, bool allowSecureLayers = true);
+
+ const sp<const DisplayDevice> mDisplay;
+ const Rect mSourceCrop;
+ const ui::Transform mTransform;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/EffectLayer.cpp b/services/surfaceflinger/EffectLayer.cpp
index 44d4d75..caef338 100644
--- a/services/surfaceflinger/EffectLayer.cpp
+++ b/services/surfaceflinger/EffectLayer.cpp
@@ -77,7 +77,7 @@
}
bool EffectLayer::isVisible() const {
- return !isHiddenByPolicy() && getAlpha() > 0.0_hf && hasSomethingToDraw();
+ return !isHiddenByPolicy() && (getAlpha() > 0.0_hf || hasBlur()) && hasSomethingToDraw();
}
bool EffectLayer::setColor(const half3& color) {
diff --git a/services/surfaceflinger/Fps.h b/services/surfaceflinger/Fps.h
new file mode 100644
index 0000000..e9f06e5
--- /dev/null
+++ b/services/surfaceflinger/Fps.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+
+#include <cmath>
+#include <ostream>
+#include <string>
+
+#include <android-base/stringprintf.h>
+#include <utils/Timers.h>
+
+namespace android {
+
+// Value which represents "frames per second". This class is a wrapper around
+// float, providing some useful utilities, such as comparisons with tolerance
+// and converting between period duration and frequency.
+class Fps {
+public:
+ static constexpr Fps fromPeriodNsecs(nsecs_t period) { return Fps(1e9f / period, period); }
+
+ Fps() = default;
+ explicit constexpr Fps(float fps)
+ : fps(fps), period(fps == 0.0f ? 0 : static_cast<nsecs_t>(1e9f / fps)) {}
+
+ constexpr float getValue() const { return fps; }
+
+ constexpr nsecs_t getPeriodNsecs() const { return period; }
+
+ bool equalsWithMargin(const Fps& other) const { return std::abs(fps - other.fps) < kMargin; }
+
+ // DO NOT use for std::sort. Instead use comparesLess().
+ bool lessThanWithMargin(const Fps& other) const { return fps + kMargin < other.fps; }
+
+ bool greaterThanWithMargin(const Fps& other) const { return fps > other.fps + kMargin; }
+
+ bool lessThanOrEqualWithMargin(const Fps& other) const { return !greaterThanWithMargin(other); }
+
+ bool greaterThanOrEqualWithMargin(const Fps& other) const { return !lessThanWithMargin(other); }
+
+ bool isValid() const { return fps > 0.0f; }
+
+ int getIntValue() const { return static_cast<int>(std::round(fps)); }
+
+ // Use this comparator for sorting. Using a comparator with margins can
+ // cause std::sort to crash.
+ inline static bool comparesLess(const Fps& left, const Fps& right) {
+ return left.fps < right.fps;
+ }
+
+ // Compares two FPS with margin.
+ // Transitivity is not guaranteed, i.e. a==b and b==c doesn't imply a==c.
+ // DO NOT use with hash maps. Instead use EqualsInBuckets.
+ struct EqualsWithMargin {
+ bool operator()(const Fps& left, const Fps& right) const {
+ return left.equalsWithMargin(right);
+ }
+ };
+
+ // Equals comparator which can be used with hash maps.
+ // It's guaranteed that if two elements are equal, then their hashes are equal.
+ struct EqualsInBuckets {
+ bool operator()(const Fps& left, const Fps& right) const {
+ return left.getBucket() == right.getBucket();
+ }
+ };
+
+ inline friend std::string to_string(const Fps& fps) {
+ return base::StringPrintf("%.2ffps", fps.fps);
+ }
+
+ inline friend std::ostream& operator<<(std::ostream& os, const Fps& fps) {
+ return os << to_string(fps);
+ }
+
+private:
+ friend std::hash<android::Fps>;
+
+ constexpr Fps(float fps, nsecs_t period) : fps(fps), period(period) {}
+
+ float getBucket() const { return std::round(fps / kMargin); }
+
+ static constexpr float kMargin = 0.001f;
+ float fps = 0;
+ nsecs_t period = 0;
+};
+
+static_assert(std::is_trivially_copyable_v<Fps>);
+
+} // namespace android
+
+namespace std {
+template <>
+struct hash<android::Fps> {
+ std::size_t operator()(const android::Fps& fps) const {
+ return std::hash<float>()(fps.getBucket());
+ }
+};
+} // namespace std
\ No newline at end of file
diff --git a/services/surfaceflinger/FpsReporter.cpp b/services/surfaceflinger/FpsReporter.cpp
new file mode 100644
index 0000000..23db805
--- /dev/null
+++ b/services/surfaceflinger/FpsReporter.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "FpsReporter"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <algorithm>
+
+#include "FpsReporter.h"
+#include "Layer.h"
+#include "SurfaceFlinger.h"
+
+namespace android {
+
+FpsReporter::FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger,
+ std::unique_ptr<Clock> clock)
+ : mFrameTimeline(frameTimeline), mFlinger(flinger), mClock(std::move(clock)) {
+ LOG_ALWAYS_FATAL_IF(mClock == nullptr, "Passed in null clock when constructing FpsReporter!");
+}
+
+void FpsReporter::dispatchLayerFps() {
+ const auto now = mClock->now();
+ if (now - mLastDispatch < kMinDispatchDuration) {
+ return;
+ }
+
+ std::vector<TrackedListener> localListeners;
+ {
+ std::scoped_lock lock(mMutex);
+ if (mListeners.empty()) {
+ return;
+ }
+
+ std::transform(mListeners.begin(), mListeners.end(), std::back_inserter(localListeners),
+ [](const std::pair<wp<IBinder>, TrackedListener>& entry) {
+ return entry.second;
+ });
+ }
+
+ std::unordered_set<int32_t> seenTasks;
+ std::vector<std::pair<TrackedListener, sp<Layer>>> listenersAndLayersToReport;
+
+ mFlinger.mCurrentState.traverse([&](Layer* layer) {
+ auto& currentState = layer->getCurrentState();
+ if (currentState.metadata.has(METADATA_TASK_ID)) {
+ int32_t taskId = currentState.metadata.getInt32(METADATA_TASK_ID, 0);
+ if (seenTasks.count(taskId) == 0) {
+ // localListeners is expected to be tiny
+ for (TrackedListener& listener : localListeners) {
+ if (listener.taskId == taskId) {
+ seenTasks.insert(taskId);
+ listenersAndLayersToReport.push_back({listener, sp<Layer>(layer)});
+ break;
+ }
+ }
+ }
+ }
+ });
+
+ for (const auto& [listener, layer] : listenersAndLayersToReport) {
+ std::unordered_set<int32_t> layerIds;
+
+ layer->traverse(LayerVector::StateSet::Current,
+ [&](Layer* layer) { layerIds.insert(layer->getSequence()); });
+
+ listener.listener->onFpsReported(mFrameTimeline.computeFps(layerIds));
+ }
+
+ mLastDispatch = now;
+}
+
+void FpsReporter::binderDied(const wp<IBinder>& who) {
+ std::scoped_lock lock(mMutex);
+ mListeners.erase(who);
+}
+
+void FpsReporter::addListener(const sp<gui::IFpsListener>& listener, int32_t taskId) {
+ sp<IBinder> asBinder = IInterface::asBinder(listener);
+ asBinder->linkToDeath(this);
+ std::lock_guard lock(mMutex);
+ mListeners.emplace(wp<IBinder>(asBinder), TrackedListener{listener, taskId});
+}
+
+void FpsReporter::removeListener(const sp<gui::IFpsListener>& listener) {
+ std::lock_guard lock(mMutex);
+ mListeners.erase(wp<IBinder>(IInterface::asBinder(listener)));
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/FpsReporter.h b/services/surfaceflinger/FpsReporter.h
new file mode 100644
index 0000000..bd7b9a5
--- /dev/null
+++ b/services/surfaceflinger/FpsReporter.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#pragma once
+
+#include <android-base/thread_annotations.h>
+#include <android/gui/IFpsListener.h>
+#include <binder/IBinder.h>
+
+#include <unordered_map>
+
+#include "Clock.h"
+#include "FrameTimeline/FrameTimeline.h"
+
+namespace android {
+
+class Layer;
+class SurfaceFlinger;
+
+class FpsReporter : public IBinder::DeathRecipient {
+public:
+ FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger,
+ std::unique_ptr<Clock> clock = std::make_unique<SteadyClock>());
+
+ // Dispatches updated layer fps values for the registered listeners
+ // This method promotes Layer weak pointers and performs layer stack traversals, so mStateLock
+ // must be held when calling this method.
+ void dispatchLayerFps() EXCLUDES(mMutex);
+
+ // Override for IBinder::DeathRecipient
+ void binderDied(const wp<IBinder>&) override;
+
+ // Registers an Fps listener that listens to fps updates for the provided layer
+ void addListener(const sp<gui::IFpsListener>& listener, int32_t taskId);
+ // Deregisters an Fps listener
+ void removeListener(const sp<gui::IFpsListener>& listener);
+
+private:
+ mutable std::mutex mMutex;
+ struct WpHash {
+ size_t operator()(const wp<IBinder>& p) const {
+ return std::hash<IBinder*>()(p.unsafe_get());
+ }
+ };
+
+ struct TrackedListener {
+ sp<gui::IFpsListener> listener;
+ int32_t taskId;
+ };
+
+ frametimeline::FrameTimeline& mFrameTimeline;
+ SurfaceFlinger& mFlinger;
+ static const constexpr std::chrono::steady_clock::duration kMinDispatchDuration =
+ std::chrono::milliseconds(500);
+ std::unique_ptr<Clock> mClock;
+ std::chrono::steady_clock::time_point mLastDispatch;
+ std::unordered_map<wp<IBinder>, TrackedListener, WpHash> mListeners GUARDED_BY(mMutex);
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/FrameTimeline/Android.bp b/services/surfaceflinger/FrameTimeline/Android.bp
new file mode 100644
index 0000000..10a5833
--- /dev/null
+++ b/services/surfaceflinger/FrameTimeline/Android.bp
@@ -0,0 +1,30 @@
+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"],
+}
+
+cc_library_static {
+ name: "libframetimeline",
+ defaults: ["surfaceflinger_defaults"],
+ srcs: [
+ "FrameTimeline.cpp",
+ ],
+ shared_libs: [
+ "android.hardware.graphics.composer@2.4",
+ "libbase",
+ "libcutils",
+ "liblog",
+ "libgui",
+ "libtimestats",
+ "libui",
+ "libutils",
+ ],
+ static_libs: [
+ "libperfetto_client_experimental",
+ ],
+ export_include_dirs: ["."],
+}
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
new file mode 100644
index 0000000..0033dbe
--- /dev/null
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -0,0 +1,1308 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "FrameTimeline"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "FrameTimeline.h"
+
+#include <android-base/stringprintf.h>
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+#include <chrono>
+#include <cinttypes>
+#include <numeric>
+#include <unordered_set>
+
+namespace android::frametimeline {
+
+using base::StringAppendF;
+using FrameTimelineEvent = perfetto::protos::pbzero::FrameTimelineEvent;
+using FrameTimelineDataSource = impl::FrameTimeline::FrameTimelineDataSource;
+
+void dumpTable(std::string& result, TimelineItem predictions, TimelineItem actuals,
+ const std::string& indent, PredictionState predictionState, nsecs_t baseTime) {
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "\t\t");
+ StringAppendF(&result, " Start time\t\t|");
+ StringAppendF(&result, " End time\t\t|");
+ StringAppendF(&result, " Present time\n");
+ if (predictionState == PredictionState::Valid) {
+ // Dump the Predictions only if they are valid
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Expected\t|");
+ std::chrono::nanoseconds startTime(predictions.startTime - baseTime);
+ std::chrono::nanoseconds endTime(predictions.endTime - baseTime);
+ std::chrono::nanoseconds presentTime(predictions.presentTime - baseTime);
+ StringAppendF(&result, "\t%10.2f\t|\t%10.2f\t|\t%10.2f\n",
+ std::chrono::duration<double, std::milli>(startTime).count(),
+ std::chrono::duration<double, std::milli>(endTime).count(),
+ std::chrono::duration<double, std::milli>(presentTime).count());
+ }
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Actual \t|");
+
+ if (actuals.startTime == 0) {
+ StringAppendF(&result, "\t\tN/A\t|");
+ } else {
+ std::chrono::nanoseconds startTime(std::max<nsecs_t>(0, actuals.startTime - baseTime));
+ StringAppendF(&result, "\t%10.2f\t|",
+ std::chrono::duration<double, std::milli>(startTime).count());
+ }
+ if (actuals.endTime <= 0) {
+ // Animation leashes can send the endTime as -1
+ StringAppendF(&result, "\t\tN/A\t|");
+ } else {
+ std::chrono::nanoseconds endTime(actuals.endTime - baseTime);
+ StringAppendF(&result, "\t%10.2f\t|",
+ std::chrono::duration<double, std::milli>(endTime).count());
+ }
+ if (actuals.presentTime == 0) {
+ StringAppendF(&result, "\t\tN/A\n");
+ } else {
+ std::chrono::nanoseconds presentTime(std::max<nsecs_t>(0, actuals.presentTime - baseTime));
+ StringAppendF(&result, "\t%10.2f\n",
+ std::chrono::duration<double, std::milli>(presentTime).count());
+ }
+
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "----------------------");
+ StringAppendF(&result, "----------------------");
+ StringAppendF(&result, "----------------------");
+ StringAppendF(&result, "----------------------\n");
+}
+
+std::string toString(PredictionState predictionState) {
+ switch (predictionState) {
+ case PredictionState::Valid:
+ return "Valid";
+ case PredictionState::Expired:
+ return "Expired";
+ case PredictionState::None:
+ return "None";
+ }
+}
+
+std::string jankTypeBitmaskToString(int32_t jankType) {
+ if (jankType == JankType::None) {
+ return "None";
+ }
+
+ std::vector<std::string> janks;
+ if (jankType & JankType::DisplayHAL) {
+ janks.emplace_back("Display HAL");
+ jankType &= ~JankType::DisplayHAL;
+ }
+ if (jankType & JankType::SurfaceFlingerCpuDeadlineMissed) {
+ janks.emplace_back("SurfaceFlinger CPU Deadline Missed");
+ jankType &= ~JankType::SurfaceFlingerCpuDeadlineMissed;
+ }
+ if (jankType & JankType::SurfaceFlingerGpuDeadlineMissed) {
+ janks.emplace_back("SurfaceFlinger GPU Deadline Missed");
+ jankType &= ~JankType::SurfaceFlingerGpuDeadlineMissed;
+ }
+ if (jankType & JankType::AppDeadlineMissed) {
+ janks.emplace_back("App Deadline Missed");
+ jankType &= ~JankType::AppDeadlineMissed;
+ }
+ if (jankType & JankType::PredictionError) {
+ janks.emplace_back("Prediction Error");
+ jankType &= ~JankType::PredictionError;
+ }
+ if (jankType & JankType::SurfaceFlingerScheduling) {
+ janks.emplace_back("SurfaceFlinger Scheduling");
+ jankType &= ~JankType::SurfaceFlingerScheduling;
+ }
+ if (jankType & JankType::BufferStuffing) {
+ janks.emplace_back("Buffer Stuffing");
+ jankType &= ~JankType::BufferStuffing;
+ }
+ if (jankType & JankType::Unknown) {
+ janks.emplace_back("Unknown jank");
+ jankType &= ~JankType::Unknown;
+ }
+
+ // jankType should be 0 if all types of jank were checked for.
+ LOG_ALWAYS_FATAL_IF(jankType != 0, "Unrecognized jank type value 0x%x", jankType);
+ return std::accumulate(janks.begin(), janks.end(), std::string(),
+ [](const std::string& l, const std::string& r) {
+ return l.empty() ? r : l + ", " + r;
+ });
+}
+
+std::string toString(FramePresentMetadata presentMetadata) {
+ switch (presentMetadata) {
+ case FramePresentMetadata::OnTimePresent:
+ return "On Time Present";
+ case FramePresentMetadata::LatePresent:
+ return "Late Present";
+ case FramePresentMetadata::EarlyPresent:
+ return "Early Present";
+ case FramePresentMetadata::UnknownPresent:
+ return "Unknown Present";
+ }
+}
+
+std::string toString(FrameReadyMetadata finishMetadata) {
+ switch (finishMetadata) {
+ case FrameReadyMetadata::OnTimeFinish:
+ return "On Time Finish";
+ case FrameReadyMetadata::LateFinish:
+ return "Late Finish";
+ case FrameReadyMetadata::UnknownFinish:
+ return "Unknown Finish";
+ }
+}
+
+std::string toString(FrameStartMetadata startMetadata) {
+ switch (startMetadata) {
+ case FrameStartMetadata::OnTimeStart:
+ return "On Time Start";
+ case FrameStartMetadata::LateStart:
+ return "Late Start";
+ case FrameStartMetadata::EarlyStart:
+ return "Early Start";
+ case FrameStartMetadata::UnknownStart:
+ return "Unknown Start";
+ }
+}
+
+std::string toString(SurfaceFrame::PresentState presentState) {
+ using PresentState = SurfaceFrame::PresentState;
+ switch (presentState) {
+ case PresentState::Presented:
+ return "Presented";
+ case PresentState::Dropped:
+ return "Dropped";
+ case PresentState::Unknown:
+ return "Unknown";
+ }
+}
+
+FrameTimelineEvent::PresentType toProto(FramePresentMetadata presentMetadata) {
+ switch (presentMetadata) {
+ case FramePresentMetadata::EarlyPresent:
+ return FrameTimelineEvent::PRESENT_EARLY;
+ case FramePresentMetadata::LatePresent:
+ return FrameTimelineEvent::PRESENT_LATE;
+ case FramePresentMetadata::OnTimePresent:
+ return FrameTimelineEvent::PRESENT_ON_TIME;
+ case FramePresentMetadata::UnknownPresent:
+ return FrameTimelineEvent::PRESENT_UNSPECIFIED;
+ }
+}
+
+FrameTimelineEvent::PredictionType toProto(PredictionState predictionState) {
+ switch (predictionState) {
+ case PredictionState::Valid:
+ return FrameTimelineEvent::PREDICTION_VALID;
+ case PredictionState::Expired:
+ return FrameTimelineEvent::PREDICTION_EXPIRED;
+ case PredictionState::None:
+ return FrameTimelineEvent::PREDICTION_UNKNOWN;
+ }
+}
+
+int32_t jankTypeBitmaskToProto(int32_t jankType) {
+ if (jankType == JankType::None) {
+ return FrameTimelineEvent::JANK_NONE;
+ }
+
+ int32_t protoJank = 0;
+ if (jankType & JankType::DisplayHAL) {
+ protoJank |= FrameTimelineEvent::JANK_DISPLAY_HAL;
+ jankType &= ~JankType::DisplayHAL;
+ }
+ if (jankType & JankType::SurfaceFlingerCpuDeadlineMissed) {
+ protoJank |= FrameTimelineEvent::JANK_SF_CPU_DEADLINE_MISSED;
+ jankType &= ~JankType::SurfaceFlingerCpuDeadlineMissed;
+ }
+ if (jankType & JankType::SurfaceFlingerGpuDeadlineMissed) {
+ protoJank |= FrameTimelineEvent::JANK_SF_GPU_DEADLINE_MISSED;
+ jankType &= ~JankType::SurfaceFlingerGpuDeadlineMissed;
+ }
+ if (jankType & JankType::AppDeadlineMissed) {
+ protoJank |= FrameTimelineEvent::JANK_APP_DEADLINE_MISSED;
+ jankType &= ~JankType::AppDeadlineMissed;
+ }
+ if (jankType & JankType::PredictionError) {
+ protoJank |= FrameTimelineEvent::JANK_PREDICTION_ERROR;
+ jankType &= ~JankType::PredictionError;
+ }
+ if (jankType & JankType::SurfaceFlingerScheduling) {
+ protoJank |= FrameTimelineEvent::JANK_SF_SCHEDULING;
+ jankType &= ~JankType::SurfaceFlingerScheduling;
+ }
+ if (jankType & JankType::BufferStuffing) {
+ protoJank |= FrameTimelineEvent::JANK_BUFFER_STUFFING;
+ jankType &= ~JankType::BufferStuffing;
+ }
+ if (jankType & JankType::Unknown) {
+ protoJank |= FrameTimelineEvent::JANK_UNKNOWN;
+ jankType &= ~JankType::Unknown;
+ }
+ if (jankType & JankType::SurfaceFlingerStuffing) {
+ protoJank |= FrameTimelineEvent::JANK_SF_STUFFING;
+ jankType &= ~JankType::SurfaceFlingerStuffing;
+ }
+
+ // jankType should be 0 if all types of jank were checked for.
+ LOG_ALWAYS_FATAL_IF(jankType != 0, "Unrecognized jank type value 0x%x", jankType);
+ return protoJank;
+}
+
+// Returns the smallest timestamp from the set of predictions and actuals.
+nsecs_t getMinTime(PredictionState predictionState, TimelineItem predictions,
+ TimelineItem actuals) {
+ nsecs_t minTime = std::numeric_limits<nsecs_t>::max();
+ if (predictionState == PredictionState::Valid) {
+ // Checking start time for predictions is enough because start time is always lesser than
+ // endTime and presentTime.
+ minTime = std::min(minTime, predictions.startTime);
+ }
+
+ // Need to check startTime, endTime and presentTime for actuals because some frames might not
+ // have them set.
+ if (actuals.startTime != 0) {
+ minTime = std::min(minTime, actuals.startTime);
+ }
+ if (actuals.endTime != 0) {
+ minTime = std::min(minTime, actuals.endTime);
+ }
+ if (actuals.presentTime != 0) {
+ minTime = std::min(minTime, actuals.endTime);
+ }
+ return minTime;
+}
+
+int64_t TraceCookieCounter::getCookieForTracing() {
+ return ++mTraceCookie;
+}
+
+SurfaceFrame::SurfaceFrame(const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid,
+ uid_t ownerUid, int32_t layerId, std::string layerName,
+ std::string debugName, PredictionState predictionState,
+ frametimeline::TimelineItem&& predictions,
+ std::shared_ptr<TimeStats> timeStats,
+ JankClassificationThresholds thresholds,
+ TraceCookieCounter* traceCookieCounter, bool isBuffer)
+ : mToken(frameTimelineInfo.vsyncId),
+ mInputEventId(frameTimelineInfo.inputEventId),
+ mOwnerPid(ownerPid),
+ mOwnerUid(ownerUid),
+ mLayerName(std::move(layerName)),
+ mDebugName(std::move(debugName)),
+ mLayerId(layerId),
+ mPresentState(PresentState::Unknown),
+ mPredictionState(predictionState),
+ mPredictions(predictions),
+ mActuals({0, 0, 0}),
+ mTimeStats(timeStats),
+ mJankClassificationThresholds(thresholds),
+ mTraceCookieCounter(*traceCookieCounter),
+ mIsBuffer(isBuffer) {}
+
+void SurfaceFrame::setActualStartTime(nsecs_t actualStartTime) {
+ std::scoped_lock lock(mMutex);
+ mActuals.startTime = actualStartTime;
+}
+
+void SurfaceFrame::setActualQueueTime(nsecs_t actualQueueTime) {
+ std::scoped_lock lock(mMutex);
+ mActualQueueTime = actualQueueTime;
+}
+
+void SurfaceFrame::setAcquireFenceTime(nsecs_t acquireFenceTime) {
+ std::scoped_lock lock(mMutex);
+ mActuals.endTime = std::max(acquireFenceTime, mActualQueueTime);
+}
+
+void SurfaceFrame::setDropTime(nsecs_t dropTime) {
+ std::scoped_lock lock(mMutex);
+ mDropTime = dropTime;
+}
+
+void SurfaceFrame::setPresentState(PresentState presentState, nsecs_t lastLatchTime) {
+ std::scoped_lock lock(mMutex);
+ LOG_ALWAYS_FATAL_IF(mPresentState != PresentState::Unknown,
+ "setPresentState called on a SurfaceFrame from Layer - %s, that has a "
+ "PresentState - %s set already.",
+ mDebugName.c_str(), toString(mPresentState).c_str());
+ mPresentState = presentState;
+ mLastLatchTime = lastLatchTime;
+}
+
+void SurfaceFrame::setRenderRate(Fps renderRate) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mRenderRate = renderRate;
+}
+
+void SurfaceFrame::setGpuComposition() {
+ std::scoped_lock lock(mMutex);
+ mGpuComposition = true;
+}
+
+std::optional<int32_t> SurfaceFrame::getJankType() const {
+ std::scoped_lock lock(mMutex);
+ if (mPresentState == PresentState::Dropped) {
+ // Return no jank if it's a dropped frame since we cannot attribute a jank to a it.
+ return JankType::None;
+ }
+ if (mActuals.presentTime == 0) {
+ // Frame hasn't been presented yet.
+ return std::nullopt;
+ }
+ return mJankType;
+}
+
+nsecs_t SurfaceFrame::getBaseTime() const {
+ std::scoped_lock lock(mMutex);
+ return getMinTime(mPredictionState, mPredictions, mActuals);
+}
+
+TimelineItem SurfaceFrame::getActuals() const {
+ std::scoped_lock lock(mMutex);
+ return mActuals;
+}
+
+PredictionState SurfaceFrame::getPredictionState() const {
+ std::scoped_lock lock(mMutex);
+ return mPredictionState;
+}
+
+SurfaceFrame::PresentState SurfaceFrame::getPresentState() const {
+ std::scoped_lock lock(mMutex);
+ return mPresentState;
+}
+
+FramePresentMetadata SurfaceFrame::getFramePresentMetadata() const {
+ std::scoped_lock lock(mMutex);
+ return mFramePresentMetadata;
+}
+
+FrameReadyMetadata SurfaceFrame::getFrameReadyMetadata() const {
+ std::scoped_lock lock(mMutex);
+ return mFrameReadyMetadata;
+}
+
+nsecs_t SurfaceFrame::getDropTime() const {
+ std::scoped_lock lock(mMutex);
+ return mDropTime;
+}
+
+void SurfaceFrame::promoteToBuffer() {
+ std::scoped_lock lock(mMutex);
+ LOG_ALWAYS_FATAL_IF(mIsBuffer == true,
+ "Trying to promote an already promoted BufferSurfaceFrame from layer %s "
+ "with token %" PRId64 "",
+ mDebugName.c_str(), mToken);
+ mIsBuffer = true;
+}
+
+bool SurfaceFrame::getIsBuffer() const {
+ std::scoped_lock lock(mMutex);
+ return mIsBuffer;
+}
+
+void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t baseTime) const {
+ std::scoped_lock lock(mMutex);
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Layer - %s", mDebugName.c_str());
+ if (mJankType != JankType::None) {
+ // Easily identify a janky Surface Frame in the dump
+ StringAppendF(&result, " [*] ");
+ }
+ StringAppendF(&result, "\n");
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Token: %" PRId64 "\n", mToken);
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Is Buffer?: %d\n", mIsBuffer);
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Owner Pid : %d\n", mOwnerPid);
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Scheduled rendering rate: %d fps\n",
+ mRenderRate ? mRenderRate->getIntValue() : 0);
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Layer ID : %d\n", mLayerId);
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Present State : %s\n", toString(mPresentState).c_str());
+ StringAppendF(&result, "%s", indent.c_str());
+ if (mPresentState == PresentState::Dropped) {
+ std::chrono::nanoseconds dropTime(mDropTime - baseTime);
+ StringAppendF(&result, "Drop time : %10f\n",
+ std::chrono::duration<double, std::milli>(dropTime).count());
+ StringAppendF(&result, "%s", indent.c_str());
+ }
+ StringAppendF(&result, "Prediction State : %s\n", toString(mPredictionState).c_str());
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Jank Type : %s\n", jankTypeBitmaskToString(mJankType).c_str());
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Present Metadata : %s\n", toString(mFramePresentMetadata).c_str());
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Finish Metadata: %s\n", toString(mFrameReadyMetadata).c_str());
+ std::chrono::nanoseconds latchTime(
+ std::max(static_cast<int64_t>(0), mLastLatchTime - baseTime));
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Last latch time: %10f\n",
+ std::chrono::duration<double, std::milli>(latchTime).count());
+ if (mPredictionState == PredictionState::Valid) {
+ nsecs_t presentDelta = mActuals.presentTime - mPredictions.presentTime;
+ std::chrono::nanoseconds presentDeltaNs(std::abs(presentDelta));
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Present delta: %10f\n",
+ std::chrono::duration<double, std::milli>(presentDeltaNs).count());
+ }
+ dumpTable(result, mPredictions, mActuals, indent, mPredictionState, baseTime);
+}
+
+std::string SurfaceFrame::miniDump() const {
+ std::scoped_lock lock(mMutex);
+ std::string result;
+ StringAppendF(&result, "Layer - %s\n", mDebugName.c_str());
+ StringAppendF(&result, "Token: %" PRId64 "\n", mToken);
+ StringAppendF(&result, "Is Buffer?: %d\n", mIsBuffer);
+ StringAppendF(&result, "Present State : %s\n", toString(mPresentState).c_str());
+ StringAppendF(&result, "Prediction State : %s\n", toString(mPredictionState).c_str());
+ StringAppendF(&result, "Jank Type : %s\n", jankTypeBitmaskToString(mJankType).c_str());
+ StringAppendF(&result, "Present Metadata : %s\n", toString(mFramePresentMetadata).c_str());
+ StringAppendF(&result, "Finish Metadata: %s\n", toString(mFrameReadyMetadata).c_str());
+ StringAppendF(&result, "Present time: %" PRId64 "", mActuals.presentTime);
+ return result;
+}
+
+void SurfaceFrame::classifyJankLocked(int32_t displayFrameJankType, const Fps& refreshRate,
+ nsecs_t& deadlineDelta) {
+ if (mPredictionState == PredictionState::Expired ||
+ mActuals.presentTime == Fence::SIGNAL_TIME_INVALID) {
+ // Cannot do any classification for invalid present time.
+ // For prediction expired case, we do not know what happened here to classify this
+ // correctly. This could potentially be AppDeadlineMissed but that's assuming no app will
+ // request frames 120ms apart.
+ mJankType = JankType::Unknown;
+ deadlineDelta = -1;
+ return;
+ }
+
+ if (mPredictionState == PredictionState::None) {
+ // Cannot do jank classification on frames that don't have a token.
+ return;
+ }
+
+ deadlineDelta = mActuals.endTime - mPredictions.endTime;
+ const nsecs_t presentDelta = mActuals.presentTime - mPredictions.presentTime;
+ const nsecs_t deltaToVsync = refreshRate.getPeriodNsecs() > 0
+ ? std::abs(presentDelta) % refreshRate.getPeriodNsecs()
+ : 0;
+
+ if (deadlineDelta > mJankClassificationThresholds.deadlineThreshold) {
+ mFrameReadyMetadata = FrameReadyMetadata::LateFinish;
+ } else {
+ mFrameReadyMetadata = FrameReadyMetadata::OnTimeFinish;
+ }
+
+ if (std::abs(presentDelta) > mJankClassificationThresholds.presentThreshold) {
+ mFramePresentMetadata = presentDelta > 0 ? FramePresentMetadata::LatePresent
+ : FramePresentMetadata::EarlyPresent;
+ } else {
+ mFramePresentMetadata = FramePresentMetadata::OnTimePresent;
+ }
+
+ if (mFramePresentMetadata == FramePresentMetadata::OnTimePresent) {
+ // Frames presented on time are not janky.
+ mJankType = JankType::None;
+ } else if (mFramePresentMetadata == FramePresentMetadata::EarlyPresent) {
+ if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) {
+ // Finish on time, Present early
+ if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
+ deltaToVsync >= refreshRate.getPeriodNsecs() -
+ mJankClassificationThresholds.presentThreshold) {
+ // Delta factor of vsync
+ mJankType = JankType::SurfaceFlingerScheduling;
+ } else {
+ // Delta not a factor of vsync
+ mJankType = JankType::PredictionError;
+ }
+ } else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
+ // Finish late, Present early
+ mJankType = JankType::Unknown;
+ }
+ } else {
+ if (mLastLatchTime != 0 && mPredictions.endTime <= mLastLatchTime) {
+ // Buffer Stuffing.
+ mJankType |= JankType::BufferStuffing;
+ // In a stuffed state, the frame could be stuck on a dequeue wait for quite some time.
+ // Because of this dequeue wait, it can be hard to tell if a frame was genuinely late.
+ // We try to do this by moving the deadline. Since the queue could be stuffed by more
+ // than one buffer, we take the last latch time as reference and give one vsync
+ // worth of time for the frame to be ready.
+ nsecs_t adjustedDeadline = mLastLatchTime + refreshRate.getPeriodNsecs();
+ if (adjustedDeadline > mActuals.endTime) {
+ mFrameReadyMetadata = FrameReadyMetadata::OnTimeFinish;
+ } else {
+ mFrameReadyMetadata = FrameReadyMetadata::LateFinish;
+ }
+ }
+ if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) {
+ // Finish on time, Present late
+ if (displayFrameJankType != JankType::None) {
+ // Propagate displayFrame's jank if it exists
+ mJankType |= displayFrameJankType;
+ } else {
+ if (!(mJankType & JankType::BufferStuffing)) {
+ // In a stuffed state, if the app finishes on time and there is no display frame
+ // jank, only buffer stuffing is the root cause of the jank.
+ if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
+ deltaToVsync >= refreshRate.getPeriodNsecs() -
+ mJankClassificationThresholds.presentThreshold) {
+ // Delta factor of vsync
+ mJankType |= JankType::SurfaceFlingerScheduling;
+ } else {
+ // Delta not a factor of vsync
+ mJankType |= JankType::PredictionError;
+ }
+ }
+ }
+ } else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
+ // Finish late, Present late
+ mJankType |= JankType::AppDeadlineMissed;
+ // Propagate DisplayFrame's jankType if it is janky
+ mJankType |= displayFrameJankType;
+ }
+ }
+}
+
+void SurfaceFrame::onPresent(nsecs_t presentTime, int32_t displayFrameJankType, Fps refreshRate,
+ nsecs_t displayDeadlineDelta, nsecs_t displayPresentDelta) {
+ std::scoped_lock lock(mMutex);
+
+ if (mPresentState != PresentState::Presented) {
+ // No need to update dropped buffers
+ return;
+ }
+
+ mActuals.presentTime = presentTime;
+ nsecs_t deadlineDelta = 0;
+
+ classifyJankLocked(displayFrameJankType, refreshRate, deadlineDelta);
+
+ if (mPredictionState != PredictionState::None) {
+ // Only update janky frames if the app used vsync predictions
+ mTimeStats->incrementJankyFrames({refreshRate, mRenderRate, mOwnerUid, mLayerName,
+ mJankType, displayDeadlineDelta, displayPresentDelta,
+ deadlineDelta});
+ }
+}
+
+void SurfaceFrame::tracePredictions(int64_t displayFrameToken) const {
+ int64_t expectedTimelineCookie = mTraceCookieCounter.getCookieForTracing();
+
+ // Expected timeline start
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ std::scoped_lock lock(mMutex);
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+ packet->set_timestamp(static_cast<uint64_t>(mPredictions.startTime));
+
+ auto* event = packet->set_frame_timeline_event();
+ auto* expectedSurfaceFrameStartEvent = event->set_expected_surface_frame_start();
+
+ expectedSurfaceFrameStartEvent->set_cookie(expectedTimelineCookie);
+
+ expectedSurfaceFrameStartEvent->set_token(mToken);
+ expectedSurfaceFrameStartEvent->set_display_frame_token(displayFrameToken);
+
+ expectedSurfaceFrameStartEvent->set_pid(mOwnerPid);
+ expectedSurfaceFrameStartEvent->set_layer_name(mDebugName);
+ });
+
+ // Expected timeline end
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ std::scoped_lock lock(mMutex);
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+ packet->set_timestamp(static_cast<uint64_t>(mPredictions.endTime));
+
+ auto* event = packet->set_frame_timeline_event();
+ auto* expectedSurfaceFrameEndEvent = event->set_frame_end();
+
+ expectedSurfaceFrameEndEvent->set_cookie(expectedTimelineCookie);
+ });
+}
+
+void SurfaceFrame::traceActuals(int64_t displayFrameToken) const {
+ int64_t actualTimelineCookie = mTraceCookieCounter.getCookieForTracing();
+
+ // Actual timeline start
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ std::scoped_lock lock(mMutex);
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+ // Actual start time is not yet available, so use expected start instead
+ if (mPredictionState == PredictionState::Expired) {
+ // If prediction is expired, we can't use the predicted start time. Instead, just use a
+ // start time a little earlier than the end time so that we have some info about this
+ // frame in the trace.
+ nsecs_t endTime =
+ (mPresentState == PresentState::Dropped ? mDropTime : mActuals.endTime);
+ packet->set_timestamp(
+ static_cast<uint64_t>(endTime - kPredictionExpiredStartTimeDelta));
+ } else {
+ packet->set_timestamp(static_cast<uint64_t>(mPredictions.startTime));
+ }
+
+ auto* event = packet->set_frame_timeline_event();
+ auto* actualSurfaceFrameStartEvent = event->set_actual_surface_frame_start();
+
+ actualSurfaceFrameStartEvent->set_cookie(actualTimelineCookie);
+
+ actualSurfaceFrameStartEvent->set_token(mToken);
+ actualSurfaceFrameStartEvent->set_display_frame_token(displayFrameToken);
+
+ actualSurfaceFrameStartEvent->set_pid(mOwnerPid);
+ actualSurfaceFrameStartEvent->set_layer_name(mDebugName);
+
+ if (mPresentState == PresentState::Dropped) {
+ actualSurfaceFrameStartEvent->set_present_type(FrameTimelineEvent::PRESENT_DROPPED);
+ } else if (mPresentState == PresentState::Unknown) {
+ actualSurfaceFrameStartEvent->set_present_type(FrameTimelineEvent::PRESENT_UNSPECIFIED);
+ } else {
+ actualSurfaceFrameStartEvent->set_present_type(toProto(mFramePresentMetadata));
+ }
+ actualSurfaceFrameStartEvent->set_on_time_finish(mFrameReadyMetadata ==
+ FrameReadyMetadata::OnTimeFinish);
+ actualSurfaceFrameStartEvent->set_gpu_composition(mGpuComposition);
+ actualSurfaceFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(mJankType));
+ actualSurfaceFrameStartEvent->set_prediction_type(toProto(mPredictionState));
+ });
+
+ // Actual timeline end
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ std::scoped_lock lock(mMutex);
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+ if (mPresentState == PresentState::Dropped) {
+ packet->set_timestamp(static_cast<uint64_t>(mDropTime));
+ } else {
+ packet->set_timestamp(static_cast<uint64_t>(mActuals.endTime));
+ }
+
+ auto* event = packet->set_frame_timeline_event();
+ auto* actualSurfaceFrameEndEvent = event->set_frame_end();
+
+ actualSurfaceFrameEndEvent->set_cookie(actualTimelineCookie);
+ });
+}
+
+/**
+ * TODO(b/178637512): add inputEventId to the perfetto trace.
+ */
+void SurfaceFrame::trace(int64_t displayFrameToken) const {
+ if (mToken == FrameTimelineInfo::INVALID_VSYNC_ID ||
+ displayFrameToken == FrameTimelineInfo::INVALID_VSYNC_ID) {
+ // No packets can be traced with a missing token.
+ return;
+ }
+ if (getPredictionState() != PredictionState::Expired) {
+ // Expired predictions have zeroed timestamps. This cannot be used in any meaningful way in
+ // a trace.
+ tracePredictions(displayFrameToken);
+ }
+ traceActuals(displayFrameToken);
+}
+
+namespace impl {
+
+int64_t TokenManager::generateTokenForPredictions(TimelineItem&& predictions) {
+ ATRACE_CALL();
+ std::scoped_lock lock(mMutex);
+ const int64_t assignedToken = mCurrentToken++;
+ mPredictions[assignedToken] = {systemTime(), predictions};
+ flushTokens(systemTime());
+ return assignedToken;
+}
+
+std::optional<TimelineItem> TokenManager::getPredictionsForToken(int64_t token) const {
+ std::scoped_lock lock(mMutex);
+ auto predictionsIterator = mPredictions.find(token);
+ if (predictionsIterator != mPredictions.end()) {
+ return predictionsIterator->second.predictions;
+ }
+ return {};
+}
+
+void TokenManager::flushTokens(nsecs_t flushTime) {
+ for (auto it = mPredictions.begin(); it != mPredictions.end();) {
+ if (flushTime - it->second.timestamp >= kMaxRetentionTime) {
+ it = mPredictions.erase(it);
+ } else {
+ // Tokens are ordered by time. If i'th token is within the retention time, then the
+ // i+1'th token will also be within retention time.
+ break;
+ }
+ }
+}
+
+FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid,
+ JankClassificationThresholds thresholds)
+ : mMaxDisplayFrames(kDefaultMaxDisplayFrames),
+ mTimeStats(std::move(timeStats)),
+ mSurfaceFlingerPid(surfaceFlingerPid),
+ mJankClassificationThresholds(thresholds) {
+ mCurrentDisplayFrame =
+ std::make_shared<DisplayFrame>(mTimeStats, thresholds, &mTraceCookieCounter);
+}
+
+void FrameTimeline::onBootFinished() {
+ perfetto::TracingInitArgs args;
+ args.backends = perfetto::kSystemBackend;
+ perfetto::Tracing::Initialize(args);
+ registerDataSource();
+}
+
+void FrameTimeline::registerDataSource() {
+ perfetto::DataSourceDescriptor dsd;
+ dsd.set_name(kFrameTimelineDataSource);
+ FrameTimelineDataSource::Register(dsd);
+}
+
+std::shared_ptr<SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
+ const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid, int32_t layerId,
+ std::string layerName, std::string debugName, bool isBuffer) {
+ ATRACE_CALL();
+ if (frameTimelineInfo.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
+ return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
+ std::move(layerName), std::move(debugName),
+ PredictionState::None, TimelineItem(), mTimeStats,
+ mJankClassificationThresholds, &mTraceCookieCounter,
+ isBuffer);
+ }
+ std::optional<TimelineItem> predictions =
+ mTokenManager.getPredictionsForToken(frameTimelineInfo.vsyncId);
+ if (predictions) {
+ return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
+ std::move(layerName), std::move(debugName),
+ PredictionState::Valid, std::move(*predictions),
+ mTimeStats, mJankClassificationThresholds,
+ &mTraceCookieCounter, isBuffer);
+ }
+ return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
+ std::move(layerName), std::move(debugName),
+ PredictionState::Expired, TimelineItem(), mTimeStats,
+ mJankClassificationThresholds, &mTraceCookieCounter,
+ isBuffer);
+}
+
+FrameTimeline::DisplayFrame::DisplayFrame(std::shared_ptr<TimeStats> timeStats,
+ JankClassificationThresholds thresholds,
+ TraceCookieCounter* traceCookieCounter)
+ : mSurfaceFlingerPredictions(TimelineItem()),
+ mSurfaceFlingerActuals(TimelineItem()),
+ mTimeStats(timeStats),
+ mJankClassificationThresholds(thresholds),
+ mTraceCookieCounter(*traceCookieCounter) {
+ mSurfaceFrames.reserve(kNumSurfaceFramesInitial);
+}
+
+void FrameTimeline::addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame) {
+ ATRACE_CALL();
+ std::scoped_lock lock(mMutex);
+ mCurrentDisplayFrame->addSurfaceFrame(surfaceFrame);
+}
+
+void FrameTimeline::setSfWakeUp(int64_t token, nsecs_t wakeUpTime, Fps refreshRate) {
+ ATRACE_CALL();
+ std::scoped_lock lock(mMutex);
+ mCurrentDisplayFrame->onSfWakeUp(token, refreshRate,
+ mTokenManager.getPredictionsForToken(token), wakeUpTime);
+}
+
+void FrameTimeline::setSfPresent(nsecs_t sfPresentTime,
+ const std::shared_ptr<FenceTime>& presentFence,
+ const std::shared_ptr<FenceTime>& gpuFence) {
+ ATRACE_CALL();
+ std::scoped_lock lock(mMutex);
+ mCurrentDisplayFrame->setActualEndTime(sfPresentTime);
+ mCurrentDisplayFrame->setGpuFence(gpuFence);
+ mPendingPresentFences.emplace_back(std::make_pair(presentFence, mCurrentDisplayFrame));
+ flushPendingPresentFences();
+ finalizeCurrentDisplayFrame();
+}
+
+void FrameTimeline::DisplayFrame::addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame) {
+ mSurfaceFrames.push_back(surfaceFrame);
+}
+
+void FrameTimeline::DisplayFrame::onSfWakeUp(int64_t token, Fps refreshRate,
+ std::optional<TimelineItem> predictions,
+ nsecs_t wakeUpTime) {
+ mToken = token;
+ mRefreshRate = refreshRate;
+ if (!predictions) {
+ mPredictionState = PredictionState::Expired;
+ } else {
+ mPredictionState = PredictionState::Valid;
+ mSurfaceFlingerPredictions = *predictions;
+ }
+ mSurfaceFlingerActuals.startTime = wakeUpTime;
+}
+
+void FrameTimeline::DisplayFrame::setPredictions(PredictionState predictionState,
+ TimelineItem predictions) {
+ mPredictionState = predictionState;
+ mSurfaceFlingerPredictions = predictions;
+}
+
+void FrameTimeline::DisplayFrame::setActualStartTime(nsecs_t actualStartTime) {
+ mSurfaceFlingerActuals.startTime = actualStartTime;
+}
+
+void FrameTimeline::DisplayFrame::setActualEndTime(nsecs_t actualEndTime) {
+ mSurfaceFlingerActuals.endTime = actualEndTime;
+}
+
+void FrameTimeline::DisplayFrame::setGpuFence(const std::shared_ptr<FenceTime>& gpuFence) {
+ mGpuFence = gpuFence;
+}
+
+void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync,
+ nsecs_t previousPresentTime) {
+ if (mPredictionState == PredictionState::Expired ||
+ mSurfaceFlingerActuals.presentTime == Fence::SIGNAL_TIME_INVALID) {
+ // Cannot do jank classification with expired predictions or invalid signal times. Set the
+ // deltas to 0 as both negative and positive deltas are used as real values.
+ mJankType = JankType::Unknown;
+ deadlineDelta = 0;
+ deltaToVsync = 0;
+ return;
+ }
+
+ // Delta between the expected present and the actual present
+ const nsecs_t presentDelta =
+ mSurfaceFlingerActuals.presentTime - mSurfaceFlingerPredictions.presentTime;
+ // Sf actual end time represents the CPU end time. In case of HWC, SF's end time would have
+ // included the time for composition. However, for GPU composition, the final end time is max(sf
+ // end time, gpu fence time).
+ nsecs_t combinedEndTime = mSurfaceFlingerActuals.endTime;
+ if (mGpuFence != FenceTime::NO_FENCE) {
+ combinedEndTime = std::max(combinedEndTime, mGpuFence->getSignalTime());
+ }
+ deadlineDelta = combinedEndTime - mSurfaceFlingerPredictions.endTime;
+
+ // How far off was the presentDelta when compared to the vsyncPeriod. Used in checking if there
+ // was a prediction error or not.
+ deltaToVsync = mRefreshRate.getPeriodNsecs() > 0
+ ? std::abs(presentDelta) % mRefreshRate.getPeriodNsecs()
+ : 0;
+
+ if (std::abs(presentDelta) > mJankClassificationThresholds.presentThreshold) {
+ mFramePresentMetadata = presentDelta > 0 ? FramePresentMetadata::LatePresent
+ : FramePresentMetadata::EarlyPresent;
+ } else {
+ mFramePresentMetadata = FramePresentMetadata::OnTimePresent;
+ }
+
+ if (combinedEndTime > mSurfaceFlingerPredictions.endTime) {
+ mFrameReadyMetadata = FrameReadyMetadata::LateFinish;
+ } else {
+ mFrameReadyMetadata = FrameReadyMetadata::OnTimeFinish;
+ }
+
+ if (std::abs(mSurfaceFlingerActuals.startTime - mSurfaceFlingerPredictions.startTime) >
+ mJankClassificationThresholds.startThreshold) {
+ mFrameStartMetadata =
+ mSurfaceFlingerActuals.startTime > mSurfaceFlingerPredictions.startTime
+ ? FrameStartMetadata::LateStart
+ : FrameStartMetadata::EarlyStart;
+ }
+
+ if (mFramePresentMetadata != FramePresentMetadata::OnTimePresent) {
+ // Do jank classification only if present is not on time
+ if (mFramePresentMetadata == FramePresentMetadata::EarlyPresent) {
+ if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) {
+ // Finish on time, Present early
+ if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
+ deltaToVsync >= (mRefreshRate.getPeriodNsecs() -
+ mJankClassificationThresholds.presentThreshold)) {
+ // Delta is a factor of vsync if its within the presentTheshold on either side
+ // of the vsyncPeriod. Example: 0-2ms and 9-11ms are both within the threshold
+ // of the vsyncPeriod if the threshold was 2ms and the vsyncPeriod was 11ms.
+ mJankType = JankType::SurfaceFlingerScheduling;
+ } else {
+ // Delta is not a factor of vsync,
+ mJankType = JankType::PredictionError;
+ }
+ } else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
+ // Finish late, Present early
+ mJankType = JankType::SurfaceFlingerScheduling;
+ } else {
+ // Finish time unknown
+ mJankType = JankType::Unknown;
+ }
+ } else if (mFramePresentMetadata == FramePresentMetadata::LatePresent) {
+ if (std::abs(mSurfaceFlingerPredictions.presentTime - previousPresentTime) <=
+ mJankClassificationThresholds.presentThreshold ||
+ previousPresentTime > mSurfaceFlingerPredictions.presentTime) {
+ // The previous frame was either presented in the current frame's expected vsync or
+ // it was presented even later than the current frame's expected vsync.
+ mJankType = JankType::SurfaceFlingerStuffing;
+ }
+ if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish &&
+ !(mJankType & JankType::SurfaceFlingerStuffing)) {
+ // Finish on time, Present late
+ if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
+ deltaToVsync >= (mRefreshRate.getPeriodNsecs() -
+ mJankClassificationThresholds.presentThreshold)) {
+ // Delta is a factor of vsync if its within the presentTheshold on either side
+ // of the vsyncPeriod. Example: 0-2ms and 9-11ms are both within the threshold
+ // of the vsyncPeriod if the threshold was 2ms and the vsyncPeriod was 11ms.
+ mJankType = JankType::DisplayHAL;
+ } else {
+ // Delta is not a factor of vsync
+ mJankType = JankType::PredictionError;
+ }
+ } else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
+ if (!(mJankType & JankType::SurfaceFlingerStuffing) ||
+ mSurfaceFlingerActuals.presentTime - previousPresentTime >
+ mRefreshRate.getPeriodNsecs() +
+ mJankClassificationThresholds.presentThreshold) {
+ // Classify CPU vs GPU if SF wasn't stuffed or if SF was stuffed but this frame
+ // was presented more than a vsync late.
+ if (mGpuFence != FenceTime::NO_FENCE &&
+ mSurfaceFlingerActuals.endTime - mSurfaceFlingerActuals.startTime <
+ mRefreshRate.getPeriodNsecs()) {
+ // If SF was in GPU composition and the CPU work finished before the vsync
+ // period, classify it as GPU deadline missed.
+ mJankType = JankType::SurfaceFlingerGpuDeadlineMissed;
+ } else {
+ mJankType = JankType::SurfaceFlingerCpuDeadlineMissed;
+ }
+ }
+ } else {
+ // Finish time unknown
+ mJankType = JankType::Unknown;
+ }
+ } else {
+ // Present unknown
+ mJankType = JankType::Unknown;
+ }
+ }
+}
+
+void FrameTimeline::DisplayFrame::onPresent(nsecs_t signalTime, nsecs_t previousPresentTime) {
+ mSurfaceFlingerActuals.presentTime = signalTime;
+ nsecs_t deadlineDelta = 0;
+ nsecs_t deltaToVsync = 0;
+ classifyJank(deadlineDelta, deltaToVsync, previousPresentTime);
+
+ for (auto& surfaceFrame : mSurfaceFrames) {
+ surfaceFrame->onPresent(signalTime, mJankType, mRefreshRate, deadlineDelta, deltaToVsync);
+ }
+}
+
+void FrameTimeline::DisplayFrame::tracePredictions(pid_t surfaceFlingerPid) const {
+ int64_t expectedTimelineCookie = mTraceCookieCounter.getCookieForTracing();
+
+ // Expected timeline start
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+ packet->set_timestamp(static_cast<uint64_t>(mSurfaceFlingerPredictions.startTime));
+
+ auto* event = packet->set_frame_timeline_event();
+ auto* expectedDisplayFrameStartEvent = event->set_expected_display_frame_start();
+
+ expectedDisplayFrameStartEvent->set_cookie(expectedTimelineCookie);
+
+ expectedDisplayFrameStartEvent->set_token(mToken);
+ expectedDisplayFrameStartEvent->set_pid(surfaceFlingerPid);
+ });
+
+ // Expected timeline end
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+ packet->set_timestamp(static_cast<uint64_t>(mSurfaceFlingerPredictions.endTime));
+
+ auto* event = packet->set_frame_timeline_event();
+ auto* expectedDisplayFrameEndEvent = event->set_frame_end();
+
+ expectedDisplayFrameEndEvent->set_cookie(expectedTimelineCookie);
+ });
+}
+
+void FrameTimeline::DisplayFrame::traceActuals(pid_t surfaceFlingerPid) const {
+ int64_t actualTimelineCookie = mTraceCookieCounter.getCookieForTracing();
+
+ // Actual timeline start
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+ packet->set_timestamp(static_cast<uint64_t>(mSurfaceFlingerActuals.startTime));
+
+ auto* event = packet->set_frame_timeline_event();
+ auto* actualDisplayFrameStartEvent = event->set_actual_display_frame_start();
+
+ actualDisplayFrameStartEvent->set_cookie(actualTimelineCookie);
+
+ actualDisplayFrameStartEvent->set_token(mToken);
+ actualDisplayFrameStartEvent->set_pid(surfaceFlingerPid);
+
+ actualDisplayFrameStartEvent->set_present_type(toProto(mFramePresentMetadata));
+ actualDisplayFrameStartEvent->set_on_time_finish(mFrameReadyMetadata ==
+ FrameReadyMetadata::OnTimeFinish);
+ actualDisplayFrameStartEvent->set_gpu_composition(mGpuFence != FenceTime::NO_FENCE);
+ actualDisplayFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(mJankType));
+ actualDisplayFrameStartEvent->set_prediction_type(toProto(mPredictionState));
+ });
+
+ // Actual timeline end
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+ packet->set_timestamp(static_cast<uint64_t>(mSurfaceFlingerActuals.presentTime));
+
+ auto* event = packet->set_frame_timeline_event();
+ auto* actualDisplayFrameEndEvent = event->set_frame_end();
+
+ actualDisplayFrameEndEvent->set_cookie(actualTimelineCookie);
+ });
+}
+
+void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid) const {
+ if (mToken == FrameTimelineInfo::INVALID_VSYNC_ID) {
+ // DisplayFrame should not have an invalid token.
+ ALOGE("Cannot trace DisplayFrame with invalid token");
+ return;
+ }
+
+ if (mPredictionState == PredictionState::Valid) {
+ // Expired and unknown predictions have zeroed timestamps. This cannot be used in any
+ // meaningful way in a trace.
+ tracePredictions(surfaceFlingerPid);
+ }
+ traceActuals(surfaceFlingerPid);
+
+ for (auto& surfaceFrame : mSurfaceFrames) {
+ surfaceFrame->trace(mToken);
+ }
+}
+
+float FrameTimeline::computeFps(const std::unordered_set<int32_t>& layerIds) {
+ if (layerIds.empty()) {
+ return 0.0f;
+ }
+
+ std::vector<nsecs_t> presentTimes;
+ {
+ std::scoped_lock lock(mMutex);
+ presentTimes.reserve(mDisplayFrames.size());
+ for (size_t i = 0; i < mDisplayFrames.size(); i++) {
+ const auto& displayFrame = mDisplayFrames[i];
+ if (displayFrame->getActuals().presentTime <= 0) {
+ continue;
+ }
+ for (const auto& surfaceFrame : displayFrame->getSurfaceFrames()) {
+ if (surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented &&
+ layerIds.count(surfaceFrame->getLayerId()) > 0) {
+ // We're looking for DisplayFrames that presents at least one layer from
+ // layerIds, so push the present time and skip looking through the rest of the
+ // SurfaceFrames.
+ presentTimes.push_back(displayFrame->getActuals().presentTime);
+ break;
+ }
+ }
+ }
+ }
+
+ // FPS can't be computed when there's fewer than 2 presented frames.
+ if (presentTimes.size() <= 1) {
+ return 0.0f;
+ }
+
+ nsecs_t priorPresentTime = -1;
+ nsecs_t totalPresentToPresentWalls = 0;
+
+ for (const nsecs_t presentTime : presentTimes) {
+ if (priorPresentTime == -1) {
+ priorPresentTime = presentTime;
+ continue;
+ }
+
+ totalPresentToPresentWalls += (presentTime - priorPresentTime);
+ priorPresentTime = presentTime;
+ }
+
+ if (CC_UNLIKELY(totalPresentToPresentWalls <= 0)) {
+ ALOGW("Invalid total present-to-present duration when computing fps: %" PRId64,
+ totalPresentToPresentWalls);
+ return 0.0f;
+ }
+
+ const constexpr nsecs_t kOneSecond =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
+ // (10^9 nanoseconds / second) * (N present deltas) / (total nanoseconds in N present deltas) =
+ // M frames / second
+ return kOneSecond * static_cast<nsecs_t>((presentTimes.size() - 1)) /
+ static_cast<float>(totalPresentToPresentWalls);
+}
+
+void FrameTimeline::flushPendingPresentFences() {
+ for (size_t i = 0; i < mPendingPresentFences.size(); i++) {
+ const auto& pendingPresentFence = mPendingPresentFences[i];
+ nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID;
+ if (pendingPresentFence.first && pendingPresentFence.first->isValid()) {
+ signalTime = pendingPresentFence.first->getSignalTime();
+ if (signalTime == Fence::SIGNAL_TIME_PENDING) {
+ continue;
+ }
+ }
+ auto& displayFrame = pendingPresentFence.second;
+ displayFrame->onPresent(signalTime, mPreviousPresentTime);
+ displayFrame->trace(mSurfaceFlingerPid);
+ mPreviousPresentTime = signalTime;
+
+ mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i));
+ --i;
+ }
+}
+
+void FrameTimeline::finalizeCurrentDisplayFrame() {
+ while (mDisplayFrames.size() >= mMaxDisplayFrames) {
+ // We maintain only a fixed number of frames' data. Pop older frames
+ mDisplayFrames.pop_front();
+ }
+ mDisplayFrames.push_back(mCurrentDisplayFrame);
+ mCurrentDisplayFrame.reset();
+ mCurrentDisplayFrame = std::make_shared<DisplayFrame>(mTimeStats, mJankClassificationThresholds,
+ &mTraceCookieCounter);
+}
+
+nsecs_t FrameTimeline::DisplayFrame::getBaseTime() const {
+ nsecs_t baseTime =
+ getMinTime(mPredictionState, mSurfaceFlingerPredictions, mSurfaceFlingerActuals);
+ for (const auto& surfaceFrame : mSurfaceFrames) {
+ nsecs_t surfaceFrameBaseTime = surfaceFrame->getBaseTime();
+ if (surfaceFrameBaseTime != 0) {
+ baseTime = std::min(baseTime, surfaceFrameBaseTime);
+ }
+ }
+ return baseTime;
+}
+
+void FrameTimeline::DisplayFrame::dumpJank(std::string& result, nsecs_t baseTime,
+ int displayFrameCount) const {
+ if (mJankType == JankType::None) {
+ // Check if any Surface Frame has been janky
+ bool isJanky = false;
+ for (const auto& surfaceFrame : mSurfaceFrames) {
+ if (surfaceFrame->getJankType() != JankType::None) {
+ isJanky = true;
+ break;
+ }
+ }
+ if (!isJanky) {
+ return;
+ }
+ }
+ StringAppendF(&result, "Display Frame %d", displayFrameCount);
+ dump(result, baseTime);
+}
+
+void FrameTimeline::DisplayFrame::dumpAll(std::string& result, nsecs_t baseTime) const {
+ dump(result, baseTime);
+}
+
+void FrameTimeline::DisplayFrame::dump(std::string& result, nsecs_t baseTime) const {
+ if (mJankType != JankType::None) {
+ // Easily identify a janky Display Frame in the dump
+ StringAppendF(&result, " [*] ");
+ }
+ StringAppendF(&result, "\n");
+ StringAppendF(&result, "Prediction State : %s\n", toString(mPredictionState).c_str());
+ StringAppendF(&result, "Jank Type : %s\n", jankTypeBitmaskToString(mJankType).c_str());
+ StringAppendF(&result, "Present Metadata : %s\n", toString(mFramePresentMetadata).c_str());
+ StringAppendF(&result, "Finish Metadata: %s\n", toString(mFrameReadyMetadata).c_str());
+ StringAppendF(&result, "Start Metadata: %s\n", toString(mFrameStartMetadata).c_str());
+ std::chrono::nanoseconds vsyncPeriod(mRefreshRate.getPeriodNsecs());
+ StringAppendF(&result, "Vsync Period: %10f\n",
+ std::chrono::duration<double, std::milli>(vsyncPeriod).count());
+ nsecs_t presentDelta =
+ mSurfaceFlingerActuals.presentTime - mSurfaceFlingerPredictions.presentTime;
+ std::chrono::nanoseconds presentDeltaNs(std::abs(presentDelta));
+ StringAppendF(&result, "Present delta: %10f\n",
+ std::chrono::duration<double, std::milli>(presentDeltaNs).count());
+ std::chrono::nanoseconds deltaToVsync(std::abs(presentDelta) % mRefreshRate.getPeriodNsecs());
+ StringAppendF(&result, "Present delta %% refreshrate: %10f\n",
+ std::chrono::duration<double, std::milli>(deltaToVsync).count());
+ dumpTable(result, mSurfaceFlingerPredictions, mSurfaceFlingerActuals, "", mPredictionState,
+ baseTime);
+ StringAppendF(&result, "\n");
+ std::string indent = " "; // 4 spaces
+ for (const auto& surfaceFrame : mSurfaceFrames) {
+ surfaceFrame->dump(result, indent, baseTime);
+ }
+ StringAppendF(&result, "\n");
+}
+
+void FrameTimeline::dumpAll(std::string& result) {
+ std::scoped_lock lock(mMutex);
+ StringAppendF(&result, "Number of display frames : %d\n", (int)mDisplayFrames.size());
+ nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : mDisplayFrames[0]->getBaseTime();
+ for (size_t i = 0; i < mDisplayFrames.size(); i++) {
+ StringAppendF(&result, "Display Frame %d", static_cast<int>(i));
+ mDisplayFrames[i]->dumpAll(result, baseTime);
+ }
+}
+
+void FrameTimeline::dumpJank(std::string& result) {
+ std::scoped_lock lock(mMutex);
+ nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : mDisplayFrames[0]->getBaseTime();
+ for (size_t i = 0; i < mDisplayFrames.size(); i++) {
+ mDisplayFrames[i]->dumpJank(result, baseTime, static_cast<int>(i));
+ }
+}
+
+void FrameTimeline::parseArgs(const Vector<String16>& args, std::string& result) {
+ ATRACE_CALL();
+ std::unordered_map<std::string, bool> argsMap;
+ for (size_t i = 0; i < args.size(); i++) {
+ argsMap[std::string(String8(args[i]).c_str())] = true;
+ }
+ if (argsMap.count("-jank")) {
+ dumpJank(result);
+ }
+ if (argsMap.count("-all")) {
+ dumpAll(result);
+ }
+}
+
+void FrameTimeline::setMaxDisplayFrames(uint32_t size) {
+ std::scoped_lock lock(mMutex);
+
+ // The size can either increase or decrease, clear everything, to be consistent
+ mDisplayFrames.clear();
+ mPendingPresentFences.clear();
+ mMaxDisplayFrames = size;
+}
+
+void FrameTimeline::reset() {
+ setMaxDisplayFrames(kDefaultMaxDisplayFrames);
+}
+
+} // namespace impl
+} // namespace android::frametimeline
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
new file mode 100644
index 0000000..0563a53
--- /dev/null
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -0,0 +1,495 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <../Fps.h>
+#include <../TimeStats/TimeStats.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/JankInfo.h>
+#include <perfetto/trace/android/frame_timeline_event.pbzero.h>
+#include <perfetto/tracing.h>
+#include <ui/FenceTime.h>
+#include <utils/RefBase.h>
+#include <utils/String16.h>
+#include <utils/Timers.h>
+#include <utils/Vector.h>
+
+#include <deque>
+#include <mutex>
+
+namespace android::frametimeline {
+
+class FrameTimelineTest;
+
+using namespace std::chrono_literals;
+
+// Metadata indicating how the frame was presented w.r.t expected present time.
+enum class FramePresentMetadata : int8_t {
+ // Frame was presented on time
+ OnTimePresent,
+ // Frame was presented late
+ LatePresent,
+ // Frame was presented early
+ EarlyPresent,
+ // Unknown/initial state
+ UnknownPresent,
+};
+
+// Metadata comparing the frame's actual finish time to the expected deadline.
+enum class FrameReadyMetadata : int8_t {
+ // App/SF finished on time. Early finish is treated as on time since the goal of any component
+ // is to finish before the deadline.
+ OnTimeFinish,
+ // App/SF finished work later than expected
+ LateFinish,
+ // Unknown/initial state
+ UnknownFinish,
+};
+
+// Metadata comparing the frame's actual start time to the expected start time.
+enum class FrameStartMetadata : int8_t {
+ // App/SF started on time
+ OnTimeStart,
+ // App/SF started later than expected
+ LateStart,
+ // App/SF started earlier than expected
+ EarlyStart,
+ // Unknown/initial state
+ UnknownStart,
+};
+
+/*
+ * Collection of timestamps that can be used for both predictions and actual times.
+ */
+struct TimelineItem {
+ TimelineItem(const nsecs_t startTime = 0, const nsecs_t endTime = 0,
+ const nsecs_t presentTime = 0)
+ : startTime(startTime), endTime(endTime), presentTime(presentTime) {}
+
+ nsecs_t startTime;
+ nsecs_t endTime;
+ nsecs_t presentTime;
+
+ bool operator==(const TimelineItem& other) const {
+ return startTime == other.startTime && endTime == other.endTime &&
+ presentTime == other.presentTime;
+ }
+
+ bool operator!=(const TimelineItem& other) const { return !(*this == other); }
+};
+
+struct TokenManagerPrediction {
+ nsecs_t timestamp = 0;
+ TimelineItem predictions;
+};
+
+struct JankClassificationThresholds {
+ // The various thresholds for App and SF. If the actual timestamp falls within the threshold
+ // compared to prediction, we treat it as on time.
+ nsecs_t presentThreshold = std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
+ nsecs_t deadlineThreshold = std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
+ nsecs_t startThreshold = std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
+};
+
+/*
+ * TokenManager generates a running number token for a set of predictions made by VsyncPredictor. It
+ * saves these predictions for a short period of time and returns the predictions for a given token,
+ * if it hasn't expired.
+ */
+class TokenManager {
+public:
+ virtual ~TokenManager() = default;
+
+ // Generates a token for the given set of predictions. Stores the predictions for 120ms and
+ // destroys it later.
+ virtual int64_t generateTokenForPredictions(TimelineItem&& prediction) = 0;
+
+ // Returns the stored predictions for a given token, if the predictions haven't expired.
+ virtual std::optional<TimelineItem> getPredictionsForToken(int64_t token) const = 0;
+};
+
+enum class PredictionState {
+ Valid, // Predictions obtained successfully from the TokenManager
+ Expired, // TokenManager no longer has the predictions
+ None, // Predictions are either not present or didn't come from TokenManager
+};
+
+/*
+ * Trace cookie is used to send start and end timestamps of <Surface/Display>Frames separately
+ * without needing to resend all the other information. We send all info to perfetto, along with a
+ * new cookie, in the start of a frame. For the corresponding end, we just send the same cookie.
+ * This helps in reducing the amount of data emitted by the producer.
+ */
+class TraceCookieCounter {
+public:
+ int64_t getCookieForTracing();
+
+private:
+ // Friend class for testing
+ friend class android::frametimeline::FrameTimelineTest;
+
+ std::atomic<int64_t> mTraceCookie = 0;
+};
+
+class SurfaceFrame {
+public:
+ enum class PresentState {
+ Presented, // Buffer was latched and presented by SurfaceFlinger
+ Dropped, // Buffer was dropped by SurfaceFlinger
+ Unknown, // Initial state, SurfaceFlinger hasn't seen this buffer yet
+ };
+
+ // Only FrameTimeline can construct a SurfaceFrame as it provides Predictions(through
+ // TokenManager), Thresholds and TimeStats pointer.
+ SurfaceFrame(const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
+ int32_t layerId, std::string layerName, std::string debugName,
+ PredictionState predictionState, TimelineItem&& predictions,
+ std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds,
+ TraceCookieCounter* traceCookieCounter, bool isBuffer);
+ ~SurfaceFrame() = default;
+
+ // Returns std::nullopt if the frame hasn't been classified yet.
+ // Used by both SF and FrameTimeline.
+ std::optional<int32_t> getJankType() const;
+
+ // Functions called by SF
+ int64_t getToken() const { return mToken; };
+ int32_t getInputEventId() const { return mInputEventId; };
+ TimelineItem getPredictions() const { return mPredictions; };
+ // Actual timestamps of the app are set individually at different functions.
+ // Start time (if the app provides) and Queue time are accessible after queueing the frame,
+ // whereas Acquire Fence time is available only during latch. Drop time is available at the time
+ // the buffer was dropped.
+ void setActualStartTime(nsecs_t actualStartTime);
+ void setActualQueueTime(nsecs_t actualQueueTime);
+ void setAcquireFenceTime(nsecs_t acquireFenceTime);
+ void setDropTime(nsecs_t dropTime);
+ void setPresentState(PresentState presentState, nsecs_t lastLatchTime = 0);
+ void setRenderRate(Fps renderRate);
+ void setGpuComposition();
+
+ // When a bufferless SurfaceFrame is promoted to a buffer SurfaceFrame, we also have to update
+ // isBuffer.
+ void promoteToBuffer();
+
+ // Functions called by FrameTimeline
+ // BaseTime is the smallest timestamp in this SurfaceFrame.
+ // Used for dumping all timestamps relative to the oldest, making it easy to read.
+ nsecs_t getBaseTime() const;
+ // Sets the actual present time, appropriate metadata and classifies the jank.
+ // displayRefreshRate, displayDeadlineDelta, and displayPresentDelta are propagated from the
+ // display frame.
+ void onPresent(nsecs_t presentTime, int32_t displayFrameJankType, Fps refreshRate,
+ nsecs_t displayDeadlineDelta, nsecs_t displayPresentDelta);
+ // All the timestamps are dumped relative to the baseTime
+ void dump(std::string& result, const std::string& indent, nsecs_t baseTime) const;
+ // Dumps only the layer, token, is buffer, jank metadata, prediction and present states.
+ std::string miniDump() const;
+ // Emits a packet for perfetto tracing. The function body will be executed only if tracing is
+ // enabled. The displayFrameToken is needed to link the SurfaceFrame to the corresponding
+ // DisplayFrame at the trace processor side.
+ void trace(int64_t displayFrameToken) const;
+
+ // Getter functions used only by FrameTimelineTests and SurfaceFrame internally
+ TimelineItem getActuals() const;
+ pid_t getOwnerPid() const { return mOwnerPid; };
+ int32_t getLayerId() const { return mLayerId; };
+ PredictionState getPredictionState() const;
+ PresentState getPresentState() const;
+ FrameReadyMetadata getFrameReadyMetadata() const;
+ FramePresentMetadata getFramePresentMetadata() const;
+ nsecs_t getDropTime() const;
+ bool getIsBuffer() const;
+
+ // For prediction expired frames, this delta is subtracted from the actual end time to get a
+ // start time decent enough to see in traces.
+ // TODO(b/172587309): Remove this when we have actual start times.
+ static constexpr nsecs_t kPredictionExpiredStartTimeDelta =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
+
+private:
+ void tracePredictions(int64_t displayFrameToken) const;
+ void traceActuals(int64_t displayFrameToken) const;
+ void classifyJankLocked(int32_t displayFrameJankType, const Fps& refreshRate,
+ nsecs_t& deadlineDelta) REQUIRES(mMutex);
+
+ const int64_t mToken;
+ const int32_t mInputEventId;
+ const pid_t mOwnerPid;
+ const uid_t mOwnerUid;
+ const std::string mLayerName;
+ const std::string mDebugName;
+ const int32_t mLayerId;
+ PresentState mPresentState GUARDED_BY(mMutex);
+ const PredictionState mPredictionState;
+ const TimelineItem mPredictions;
+ TimelineItem mActuals GUARDED_BY(mMutex);
+ std::shared_ptr<TimeStats> mTimeStats;
+ const JankClassificationThresholds mJankClassificationThresholds;
+ nsecs_t mActualQueueTime GUARDED_BY(mMutex) = 0;
+ nsecs_t mDropTime GUARDED_BY(mMutex) = 0;
+ mutable std::mutex mMutex;
+ // Bitmask for the type of jank
+ int32_t mJankType GUARDED_BY(mMutex) = JankType::None;
+ // Indicates if this frame was composited by the GPU or not
+ bool mGpuComposition GUARDED_BY(mMutex) = false;
+ // Rendering rate for this frame.
+ std::optional<Fps> mRenderRate GUARDED_BY(mMutex);
+ // Enum for the type of present
+ FramePresentMetadata mFramePresentMetadata GUARDED_BY(mMutex) =
+ FramePresentMetadata::UnknownPresent;
+ // Enum for the type of finish
+ FrameReadyMetadata mFrameReadyMetadata GUARDED_BY(mMutex) = FrameReadyMetadata::UnknownFinish;
+ // Time when the previous buffer from the same layer was latched by SF. This is used in checking
+ // for BufferStuffing where the current buffer is expected to be ready but the previous buffer
+ // was latched instead.
+ nsecs_t mLastLatchTime GUARDED_BY(mMutex) = 0;
+ // TraceCookieCounter is used to obtain the cookie for sendig trace packets to perfetto. Using a
+ // reference here because the counter is owned by FrameTimeline, which outlives SurfaceFrame.
+ TraceCookieCounter& mTraceCookieCounter;
+ // Tells if the SurfaceFrame is representing a buffer or a transaction without a
+ // buffer(animations)
+ bool mIsBuffer;
+};
+
+/*
+ * Maintains a history of SurfaceFrames grouped together by the vsync time in which they were
+ * presented
+ */
+class FrameTimeline {
+public:
+ virtual ~FrameTimeline() = default;
+ virtual TokenManager* getTokenManager() = 0;
+
+ // Initializes the Perfetto DataSource that emits DisplayFrame and SurfaceFrame events. Test
+ // classes can avoid double registration by mocking this function.
+ virtual void onBootFinished() = 0;
+
+ // Create a new surface frame, set the predictions based on a token and return it to the caller.
+ // Debug name is the human-readable debugging string for dumpsys.
+ virtual std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
+ const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
+ int32_t layerId, std::string layerName, std::string debugName, bool isBuffer) = 0;
+
+ // Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be
+ // composited into one display frame.
+ virtual void addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame) = 0;
+
+ // The first function called by SF for the current DisplayFrame. Fetches SF predictions based on
+ // the token and sets the actualSfWakeTime for the current DisplayFrame.
+ virtual void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate) = 0;
+
+ // Sets the sfPresentTime and finalizes the current DisplayFrame. Tracks the
+ // given present fence until it's signaled, and updates the present timestamps of all presented
+ // SurfaceFrames in that vsync. If a gpuFence was also provided, its tracked in the
+ // corresponding DisplayFrame.
+ virtual void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence,
+ const std::shared_ptr<FenceTime>& gpuFence) = 0;
+
+ // Args:
+ // -jank : Dumps only the Display Frames that are either janky themselves
+ // or contain janky Surface Frames.
+ // -all : Dumps the entire list of DisplayFrames and the SurfaceFrames contained within
+ virtual void parseArgs(const Vector<String16>& args, std::string& result) = 0;
+
+ // Sets the max number of display frames that can be stored. Called by SF backdoor.
+ virtual void setMaxDisplayFrames(uint32_t size);
+
+ // Computes the historical fps for the provided set of layer IDs
+ // The fps is compted from the linear timeline of present timestamps for DisplayFrames
+ // containing at least one layer ID.
+ virtual float computeFps(const std::unordered_set<int32_t>& layerIds);
+
+ // Restores the max number of display frames to default. Called by SF backdoor.
+ virtual void reset() = 0;
+};
+
+namespace impl {
+
+class TokenManager : public android::frametimeline::TokenManager {
+public:
+ TokenManager() : mCurrentToken(FrameTimelineInfo::INVALID_VSYNC_ID + 1) {}
+ ~TokenManager() = default;
+
+ int64_t generateTokenForPredictions(TimelineItem&& predictions) override;
+ std::optional<TimelineItem> getPredictionsForToken(int64_t token) const override;
+
+private:
+ // Friend class for testing
+ friend class android::frametimeline::FrameTimelineTest;
+
+ void flushTokens(nsecs_t flushTime) REQUIRES(mMutex);
+
+ std::map<int64_t, TokenManagerPrediction> mPredictions GUARDED_BY(mMutex);
+ int64_t mCurrentToken GUARDED_BY(mMutex);
+ mutable std::mutex mMutex;
+ static constexpr nsecs_t kMaxRetentionTime =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(120ms).count();
+};
+
+class FrameTimeline : public android::frametimeline::FrameTimeline {
+public:
+ class FrameTimelineDataSource : public perfetto::DataSource<FrameTimelineDataSource> {
+ void OnSetup(const SetupArgs&) override{};
+ void OnStart(const StartArgs&) override{};
+ void OnStop(const StopArgs&) override{};
+ };
+
+ /*
+ * DisplayFrame should be used only internally within FrameTimeline. All members and methods are
+ * guarded by FrameTimeline's mMutex.
+ */
+ class DisplayFrame {
+ public:
+ DisplayFrame(std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds,
+ TraceCookieCounter* traceCookieCounter);
+ virtual ~DisplayFrame() = default;
+ // Dumpsys interface - dumps only if the DisplayFrame itself is janky or is at least one
+ // SurfaceFrame is janky.
+ void dumpJank(std::string& result, nsecs_t baseTime, int displayFrameCount) const;
+ // Dumpsys interface - dumps all data irrespective of jank
+ void dumpAll(std::string& result, nsecs_t baseTime) const;
+ // Emits a packet for perfetto tracing. The function body will be executed only if tracing
+ // is enabled.
+ void trace(pid_t surfaceFlingerPid) const;
+ // Sets the token, vsyncPeriod, predictions and SF start time.
+ void onSfWakeUp(int64_t token, Fps refreshRate, std::optional<TimelineItem> predictions,
+ nsecs_t wakeUpTime);
+ // Sets the appropriate metadata and classifies the jank.
+ void onPresent(nsecs_t signalTime, nsecs_t previousPresentTime);
+ // Adds the provided SurfaceFrame to the current display frame.
+ void addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame);
+
+ void setPredictions(PredictionState predictionState, TimelineItem predictions);
+ void setActualStartTime(nsecs_t actualStartTime);
+ void setActualEndTime(nsecs_t actualEndTime);
+ void setGpuFence(const std::shared_ptr<FenceTime>& gpuFence);
+
+ // BaseTime is the smallest timestamp in a DisplayFrame.
+ // Used for dumping all timestamps relative to the oldest, making it easy to read.
+ nsecs_t getBaseTime() const;
+
+ // Functions to be used only in testing.
+ TimelineItem getActuals() const { return mSurfaceFlingerActuals; };
+ TimelineItem getPredictions() const { return mSurfaceFlingerPredictions; };
+ FrameStartMetadata getFrameStartMetadata() const { return mFrameStartMetadata; };
+ FramePresentMetadata getFramePresentMetadata() const { return mFramePresentMetadata; };
+ FrameReadyMetadata getFrameReadyMetadata() const { return mFrameReadyMetadata; };
+ int32_t getJankType() const { return mJankType; }
+ const std::vector<std::shared_ptr<SurfaceFrame>>& getSurfaceFrames() const {
+ return mSurfaceFrames;
+ }
+
+ private:
+ void dump(std::string& result, nsecs_t baseTime) const;
+ void tracePredictions(pid_t surfaceFlingerPid) const;
+ void traceActuals(pid_t surfaceFlingerPid) const;
+ void classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync,
+ nsecs_t previousPresentTime);
+
+ int64_t mToken = FrameTimelineInfo::INVALID_VSYNC_ID;
+
+ /* Usage of TimelineItem w.r.t SurfaceFlinger
+ * startTime Time when SurfaceFlinger wakes up to handle transactions and buffer updates
+ * endTime Time when SurfaceFlinger sends a composited frame to Display
+ * presentTime Time when the composited frame was presented on screen
+ */
+ TimelineItem mSurfaceFlingerPredictions;
+ TimelineItem mSurfaceFlingerActuals;
+ std::shared_ptr<TimeStats> mTimeStats;
+ const JankClassificationThresholds mJankClassificationThresholds;
+
+ // Collection of predictions and actual values sent over by Layers
+ std::vector<std::shared_ptr<SurfaceFrame>> mSurfaceFrames;
+
+ PredictionState mPredictionState = PredictionState::None;
+ // Bitmask for the type of jank
+ int32_t mJankType = JankType::None;
+ // A valid gpu fence indicates that the DisplayFrame was composited by the GPU
+ std::shared_ptr<FenceTime> mGpuFence = FenceTime::NO_FENCE;
+ // Enum for the type of present
+ FramePresentMetadata mFramePresentMetadata = FramePresentMetadata::UnknownPresent;
+ // Enum for the type of finish
+ FrameReadyMetadata mFrameReadyMetadata = FrameReadyMetadata::UnknownFinish;
+ // Enum for the type of start
+ FrameStartMetadata mFrameStartMetadata = FrameStartMetadata::UnknownStart;
+ // The refresh rate (vsync period) in nanoseconds as seen by SF during this DisplayFrame's
+ // timeline
+ Fps mRefreshRate;
+ // TraceCookieCounter is used to obtain the cookie for sendig trace packets to perfetto.
+ // Using a reference here because the counter is owned by FrameTimeline, which outlives
+ // DisplayFrame.
+ TraceCookieCounter& mTraceCookieCounter;
+ };
+
+ FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid,
+ JankClassificationThresholds thresholds = {});
+ ~FrameTimeline() = default;
+
+ frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; }
+ std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
+ const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
+ int32_t layerId, std::string layerName, std::string debugName, bool isBuffer) override;
+ void addSurfaceFrame(std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame) override;
+ void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate) override;
+ void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence,
+ const std::shared_ptr<FenceTime>& gpuFence = FenceTime::NO_FENCE) override;
+ void parseArgs(const Vector<String16>& args, std::string& result) override;
+ void setMaxDisplayFrames(uint32_t size) override;
+ float computeFps(const std::unordered_set<int32_t>& layerIds) override;
+ void reset() override;
+
+ // Sets up the perfetto tracing backend and data source.
+ void onBootFinished() override;
+ // Registers the data source with the perfetto backend. Called as part of onBootFinished()
+ // and should not be called manually outside of tests.
+ void registerDataSource();
+
+ static constexpr char kFrameTimelineDataSource[] = "android.surfaceflinger.frametimeline";
+
+private:
+ // Friend class for testing
+ friend class android::frametimeline::FrameTimelineTest;
+
+ void flushPendingPresentFences() REQUIRES(mMutex);
+ void finalizeCurrentDisplayFrame() REQUIRES(mMutex);
+ void dumpAll(std::string& result);
+ void dumpJank(std::string& result);
+
+ // Sliding window of display frames. TODO(b/168072834): compare perf with fixed size array
+ std::deque<std::shared_ptr<DisplayFrame>> mDisplayFrames GUARDED_BY(mMutex);
+ std::vector<std::pair<std::shared_ptr<FenceTime>, std::shared_ptr<DisplayFrame>>>
+ mPendingPresentFences GUARDED_BY(mMutex);
+ std::shared_ptr<DisplayFrame> mCurrentDisplayFrame GUARDED_BY(mMutex);
+ TokenManager mTokenManager;
+ TraceCookieCounter mTraceCookieCounter;
+ mutable std::mutex mMutex;
+ uint32_t mMaxDisplayFrames;
+ std::shared_ptr<TimeStats> mTimeStats;
+ const pid_t mSurfaceFlingerPid;
+ nsecs_t mPreviousPresentTime = 0;
+ const JankClassificationThresholds mJankClassificationThresholds;
+ static constexpr uint32_t kDefaultMaxDisplayFrames = 64;
+ // The initial container size for the vector<SurfaceFrames> inside display frame. Although
+ // this number doesn't represent any bounds on the number of surface frames that can go in a
+ // display frame, this is a good starting size for the vector so that we can avoid the
+ // internal vector resizing that happens with push_back.
+ static constexpr uint32_t kNumSurfaceFramesInitial = 10;
+};
+
+} // namespace impl
+} // namespace android::frametimeline
diff --git a/services/surfaceflinger/FrameTracer/FrameTracer.cpp b/services/surfaceflinger/FrameTracer/FrameTracer.cpp
index b986f38..2dfb9a9 100644
--- a/services/surfaceflinger/FrameTracer/FrameTracer.cpp
+++ b/services/surfaceflinger/FrameTracer/FrameTracer.cpp
@@ -25,7 +25,7 @@
#include "FrameTracer.h"
#include <android-base/stringprintf.h>
-#include <perfetto/trace/clock_snapshot.pbzero.h>
+#include <perfetto/common/builtin_clock.pbzero.h>
#include <algorithm>
#include <mutex>
@@ -34,7 +34,6 @@
namespace android {
-using Clock = perfetto::protos::pbzero::ClockSnapshot::Clock;
void FrameTracer::initialize() {
std::call_once(mInitializationFlag, [this]() {
perfetto::TracingInitArgs args;
@@ -136,7 +135,7 @@
uint64_t bufferID, uint64_t frameNumber, nsecs_t timestamp,
FrameEvent::BufferEventType type, nsecs_t duration) {
auto packet = ctx.NewTracePacket();
- packet->set_timestamp_clock_id(Clock::MONOTONIC);
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
packet->set_timestamp(timestamp);
auto* event = packet->set_graphics_frame_event()->set_buffer_event();
event->set_buffer_id(static_cast<uint32_t>(bufferID));
diff --git a/services/surfaceflinger/FrameTracer/OWNERS b/services/surfaceflinger/FrameTracer/OWNERS
new file mode 100644
index 0000000..e4f5d45
--- /dev/null
+++ b/services/surfaceflinger/FrameTracer/OWNERS
@@ -0,0 +1 @@
+adsrini@google.com
diff --git a/services/surfaceflinger/FrameTracker.cpp b/services/surfaceflinger/FrameTracker.cpp
index 8ad805b..178c531 100644
--- a/services/surfaceflinger/FrameTracker.cpp
+++ b/services/surfaceflinger/FrameTracker.cpp
@@ -62,10 +62,9 @@
mFrameRecords[mOffset].actualPresentTime = presentTime;
}
-void FrameTracker::setActualPresentFence(
- std::shared_ptr<FenceTime>&& readyFence) {
+void FrameTracker::setActualPresentFence(const std::shared_ptr<FenceTime>& readyFence) {
Mutex::Autolock lock(mMutex);
- mFrameRecords[mOffset].actualPresentFence = std::move(readyFence);
+ mFrameRecords[mOffset].actualPresentFence = readyFence;
mNumFences++;
}
diff --git a/services/surfaceflinger/FrameTracker.h b/services/surfaceflinger/FrameTracker.h
index 35382be..bc412ae 100644
--- a/services/surfaceflinger/FrameTracker.h
+++ b/services/surfaceflinger/FrameTracker.h
@@ -66,7 +66,7 @@
// setActualPresentFence sets the fence that is used to get the time
// at which the current frame became visible to the user.
- void setActualPresentFence(std::shared_ptr<FenceTime>&& fence);
+ void setActualPresentFence(const std::shared_ptr<FenceTime>& fence);
// setDisplayRefreshPeriod sets the display refresh period in nanoseconds.
// This is used to compute frame presentation duration statistics relative
diff --git a/services/surfaceflinger/HdrLayerInfoReporter.cpp b/services/surfaceflinger/HdrLayerInfoReporter.cpp
new file mode 100644
index 0000000..c06e300
--- /dev/null
+++ b/services/surfaceflinger/HdrLayerInfoReporter.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "HdrLayerInfoReporter"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <utils/Trace.h>
+
+#include "HdrLayerInfoReporter.h"
+
+namespace android {
+
+void HdrLayerInfoReporter::dispatchHdrLayerInfo(const HdrLayerInfo& info) {
+ ATRACE_CALL();
+ std::vector<sp<gui::IHdrLayerInfoListener>> toInvoke;
+ {
+ std::scoped_lock lock(mMutex);
+ toInvoke.reserve(mListeners.size());
+ for (auto& [key, it] : mListeners) {
+ if (it.lastInfo != info) {
+ it.lastInfo = info;
+ toInvoke.push_back(it.listener);
+ }
+ }
+ }
+
+ for (const auto& listener : toInvoke) {
+ ATRACE_NAME("invoking onHdrLayerInfoChanged");
+ listener->onHdrLayerInfoChanged(info.numberOfHdrLayers, info.maxW, info.maxH, info.flags);
+ }
+}
+
+void HdrLayerInfoReporter::binderDied(const wp<IBinder>& who) {
+ std::scoped_lock lock(mMutex);
+ mListeners.erase(who);
+}
+
+void HdrLayerInfoReporter::addListener(const sp<gui::IHdrLayerInfoListener>& listener) {
+ sp<IBinder> asBinder = IInterface::asBinder(listener);
+ asBinder->linkToDeath(this);
+ std::lock_guard lock(mMutex);
+ mListeners.emplace(wp<IBinder>(asBinder), TrackedListener{listener, HdrLayerInfo{}});
+}
+
+void HdrLayerInfoReporter::removeListener(const sp<gui::IHdrLayerInfoListener>& listener) {
+ std::lock_guard lock(mMutex);
+ mListeners.erase(wp<IBinder>(IInterface::asBinder(listener)));
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/HdrLayerInfoReporter.h b/services/surfaceflinger/HdrLayerInfoReporter.h
new file mode 100644
index 0000000..671395f
--- /dev/null
+++ b/services/surfaceflinger/HdrLayerInfoReporter.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#pragma once
+
+#include <android-base/thread_annotations.h>
+#include <android/gui/IHdrLayerInfoListener.h>
+#include <binder/IBinder.h>
+
+#include <unordered_map>
+
+namespace android {
+
+class HdrLayerInfoReporter final : public IBinder::DeathRecipient {
+public:
+ struct HdrLayerInfo {
+ int32_t numberOfHdrLayers = 0;
+ int32_t maxW = 0;
+ int32_t maxH = 0;
+ int32_t flags = 0;
+
+ bool operator==(const HdrLayerInfo& other) const {
+ return numberOfHdrLayers == other.numberOfHdrLayers && maxW == other.maxW &&
+ maxH == other.maxH && flags == other.flags;
+ }
+
+ bool operator!=(const HdrLayerInfo& other) const { return !(*this == other); }
+ };
+
+ HdrLayerInfoReporter() = default;
+ ~HdrLayerInfoReporter() final = default;
+
+ // Dispatches updated layer fps values for the registered listeners
+ // This method promotes Layer weak pointers and performs layer stack traversals, so mStateLock
+ // must be held when calling this method.
+ void dispatchHdrLayerInfo(const HdrLayerInfo& info) EXCLUDES(mMutex);
+
+ // Override for IBinder::DeathRecipient
+ void binderDied(const wp<IBinder>&) override EXCLUDES(mMutex);
+
+ // Registers an Fps listener that listens to fps updates for the provided layer
+ void addListener(const sp<gui::IHdrLayerInfoListener>& listener) EXCLUDES(mMutex);
+ // Deregisters an Fps listener
+ void removeListener(const sp<gui::IHdrLayerInfoListener>& listener) EXCLUDES(mMutex);
+
+ bool hasListeners() const EXCLUDES(mMutex) {
+ std::scoped_lock lock(mMutex);
+ return !mListeners.empty();
+ }
+
+private:
+ mutable std::mutex mMutex;
+ struct WpHash {
+ size_t operator()(const wp<IBinder>& p) const {
+ return std::hash<IBinder*>()(p.unsafe_get());
+ }
+ };
+
+ struct TrackedListener {
+ sp<gui::IHdrLayerInfoListener> listener;
+ HdrLayerInfo lastInfo;
+ };
+
+ std::unordered_map<wp<IBinder>, TrackedListener, WpHash> mListeners GUARDED_BY(mMutex);
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 3282a5a..4461420 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -25,6 +25,7 @@
#include "Layer.h"
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android/native_window.h>
#include <binder/IPCThreadState.h>
@@ -39,6 +40,7 @@
#include <gui/LayerDebugInfo.h>
#include <gui/Surface.h>
#include <math.h>
+#include <private/android_filesystem_config.h>
#include <renderengine/RenderEngine.h>
#include <stdint.h>
#include <stdlib.h>
@@ -61,18 +63,25 @@
#include "DisplayDevice.h"
#include "DisplayHardware/HWComposer.h"
#include "EffectLayer.h"
+#include "FrameTimeline.h"
#include "FrameTracer/FrameTracer.h"
#include "LayerProtoHelper.h"
#include "LayerRejecter.h"
#include "MonitoredProducer.h"
#include "SurfaceFlinger.h"
#include "TimeStats/TimeStats.h"
+#include "input/InputWindow.h"
#define DEBUG_RESIZE 0
namespace android {
+namespace {
+constexpr int kDumpTableRowLength = 159;
+} // namespace
using base::StringAppendF;
+using namespace android::flag_operators;
+using PresentState = frametimeline::SurfaceFrame::PresentState;
std::atomic<int32_t> Layer::sSequence{1};
@@ -80,31 +89,35 @@
: mFlinger(args.flinger),
mName(args.name),
mClientRef(args.client),
- mWindowType(args.metadata.getInt32(METADATA_WINDOW_TYPE, 0)) {
+ mWindowType(static_cast<InputWindowInfo::Type>(
+ args.metadata.getInt32(METADATA_WINDOW_TYPE, 0))) {
uint32_t layerFlags = 0;
if (args.flags & ISurfaceComposerClient::eHidden) layerFlags |= layer_state_t::eLayerHidden;
if (args.flags & ISurfaceComposerClient::eOpaque) layerFlags |= layer_state_t::eLayerOpaque;
if (args.flags & ISurfaceComposerClient::eSecure) layerFlags |= layer_state_t::eLayerSecure;
+ if (args.flags & ISurfaceComposerClient::eSkipScreenshot)
+ layerFlags |= layer_state_t::eLayerSkipScreenshot;
mCurrentState.active_legacy.w = args.w;
mCurrentState.active_legacy.h = args.h;
mCurrentState.flags = layerFlags;
mCurrentState.active_legacy.transform.set(0, 0);
- mCurrentState.crop_legacy.makeInvalid();
- mCurrentState.requestedCrop_legacy = mCurrentState.crop_legacy;
+ mCurrentState.crop.makeInvalid();
+ mCurrentState.requestedCrop = mCurrentState.crop;
mCurrentState.z = 0;
mCurrentState.color.a = 1.0f;
mCurrentState.layerStack = 0;
mCurrentState.sequence = 0;
mCurrentState.requested_legacy = mCurrentState.active_legacy;
- mCurrentState.active.w = UINT32_MAX;
- mCurrentState.active.h = UINT32_MAX;
- mCurrentState.active.transform.set(0, 0);
+ mCurrentState.width = UINT32_MAX;
+ mCurrentState.height = UINT32_MAX;
+ mCurrentState.transform.set(0, 0);
mCurrentState.frameNumber = 0;
- mCurrentState.transform = 0;
+ mCurrentState.bufferTransform = 0;
mCurrentState.transformToDisplayInverse = false;
mCurrentState.crop.makeInvalid();
- mCurrentState.acquireFence = new Fence(-1);
+ mCurrentState.acquireFence = sp<Fence>::make(-1);
+ mCurrentState.acquireFenceTime = std::make_shared<FenceTime>(mCurrentState.acquireFence);
mCurrentState.dataspace = ui::Dataspace::UNKNOWN;
mCurrentState.hdrMetadata.validTypes = 0;
mCurrentState.surfaceDamageRegion = Region::INVALID_REGION;
@@ -118,6 +131,8 @@
mCurrentState.shadowRadius = 0.f;
mCurrentState.treeHasFrameRateVote = false;
mCurrentState.fixedTransformHint = ui::Transform::ROT_INVALID;
+ mCurrentState.frameTimelineInfo = {};
+ mCurrentState.postTime = -1;
if (args.flags & ISurfaceComposerClient::eNoColorFill) {
// Set an invalid color so there is no color fill.
@@ -136,6 +151,16 @@
mCallingPid = args.callingPid;
mCallingUid = args.callingUid;
+
+ if (mCallingUid == AID_GRAPHICS || mCallingUid == AID_SYSTEM) {
+ // If the system didn't send an ownerUid, use the callingUid for the ownerUid.
+ mOwnerUid = args.metadata.getInt32(METADATA_OWNER_UID, mCallingUid);
+ mOwnerPid = args.metadata.getInt32(METADATA_OWNER_PID, mCallingPid);
+ } else {
+ // A create layer request from a non system request cannot specify the owner uid
+ mOwnerUid = mCallingUid;
+ mOwnerPid = mCallingPid;
+ }
}
void Layer::onFirstRef() {
@@ -177,19 +202,6 @@
*/
void Layer::onLayerDisplayed(const sp<Fence>& /*releaseFence*/) {}
-void Layer::removeRemoteSyncPoints() {
- for (auto& point : mRemoteSyncPoints) {
- point->setTransactionApplied();
- }
- mRemoteSyncPoints.clear();
-
- {
- for (State pendingState : mPendingStates) {
- pendingState.barrierLayer_legacy = nullptr;
- }
- }
-}
-
void Layer::removeRelativeZ(const std::vector<Layer*>& layersInTree) {
if (mCurrentState.zOrderRelativeOf == nullptr) {
return;
@@ -211,21 +223,6 @@
void Layer::removeFromCurrentState() {
mRemovedFromCurrentState = true;
- // Since we are no longer reachable from CurrentState SurfaceFlinger
- // will no longer invoke doTransaction for us, and so we will
- // never finish applying transactions. We signal the sync point
- // now so that another layer will not become indefinitely
- // blocked.
- removeRemoteSyncPoints();
-
- {
- Mutex::Autolock syncLock(mLocalSyncPointMutex);
- for (auto& point : mLocalSyncPoints) {
- point->setFrameAvailable();
- }
- mLocalSyncPoints.clear();
- }
-
mFlinger->markLayerPendingRemovalLocked(this);
}
@@ -464,6 +461,8 @@
compositionState->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
compositionState->alpha = alpha;
compositionState->backgroundBlurRadius = drawingState.backgroundBlurRadius;
+ compositionState->blurRegions = drawingState.blurRegions;
+ compositionState->stretchEffect = getStretchEffect();
}
void Layer::prepareGeometryCompositionState() {
@@ -532,7 +531,10 @@
isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf;
// Force client composition for special cases known only to the front-end.
- if (isHdrY410() || usesRoundedCorners || drawShadows()) {
+ // Rounded corners no longer force client composition, since we may use a
+ // hole punch so that the layer will appear to have rounded corners.
+ if (isHdrY410() || drawShadows() || drawingState.blurRegions.size() > 0 ||
+ compositionState->stretchEffect.hasEffect()) {
compositionState->forceClientComposition = true;
}
}
@@ -615,11 +617,7 @@
compositionengine::LayerFE::LayerSettings layerSettings;
layerSettings.geometry.boundaries = bounds;
- if (targetSettings.useIdentityTransform) {
- layerSettings.geometry.positionTransform = mat4();
- } else {
- layerSettings.geometry.positionTransform = getTransform().asMatrix4();
- }
+ layerSettings.geometry.positionTransform = getTransform().asMatrix4();
if (hasColorTransform()) {
layerSettings.colorTransform = getColorTransform();
@@ -631,14 +629,20 @@
layerSettings.alpha = alpha;
layerSettings.sourceDataspace = getDataSpace();
- layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
+ if (!targetSettings.disableBlurs) {
+ layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
+ layerSettings.blurRegions = getBlurRegions();
+ }
+ layerSettings.stretchEffect = getDrawingState().stretchEffect;
+ // Record the name of the layer for debugging further down the stack.
+ layerSettings.name = getName();
return layerSettings;
}
std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareShadowClientComposition(
- const LayerFE::LayerSettings& casterLayerSettings, const Rect& displayViewport,
+ const LayerFE::LayerSettings& casterLayerSettings, const Rect& layerStackRect,
ui::Dataspace outputDataspace) {
- renderengine::ShadowSettings shadow = getShadowSettings(displayViewport);
+ renderengine::ShadowSettings shadow = getShadowSettings(layerStackRect);
if (shadow.length <= 0.f) {
return {};
}
@@ -662,6 +666,7 @@
shadowLayer.source.buffer.fence = nullptr;
shadowLayer.frameNumber = 0;
shadowLayer.bufferId = 0;
+ shadowLayer.name = getName();
if (shadowLayer.shadow.ambientColor.a <= 0.f && shadowLayer.shadow.spotColor.a <= 0.f) {
return {};
@@ -697,6 +702,7 @@
// If layer is blacked out, force alpha to 1 so that we draw a black color layer.
layerSettings.alpha = blackout ? 1.0f : 0.0f;
+ layerSettings.name = getName();
}
std::vector<compositionengine::LayerFE::LayerSettings> Layer::prepareClientCompositionList(
@@ -743,141 +749,24 @@
}
}
-bool Layer::addSyncPoint(const std::shared_ptr<SyncPoint>& point) {
- if (point->getFrameNumber() <= mCurrentFrameNumber) {
- // Don't bother with a SyncPoint, since we've already latched the
- // relevant frame
- return false;
- }
- if (isRemovedFromCurrentState()) {
- return false;
- }
-
- Mutex::Autolock lock(mLocalSyncPointMutex);
- mLocalSyncPoints.push_back(point);
- return true;
-}
-
// ----------------------------------------------------------------------------
// local state
// ----------------------------------------------------------------------------
bool Layer::isSecure() const {
const State& s(mDrawingState);
- return (s.flags & layer_state_t::eLayerSecure);
+ if (s.flags & layer_state_t::eLayerSecure) {
+ return true;
+ }
+
+ const auto p = mDrawingParent.promote();
+ return (p != nullptr) ? p->isSecure() : false;
}
// ----------------------------------------------------------------------------
// transaction
// ----------------------------------------------------------------------------
-void Layer::pushPendingState() {
- if (!mCurrentState.modified) {
- return;
- }
- ATRACE_CALL();
-
- // If this transaction is waiting on the receipt of a frame, generate a sync
- // point and send it to the remote layer.
- // We don't allow installing sync points after we are removed from the current state
- // as we won't be able to signal our end.
- if (mCurrentState.barrierLayer_legacy != nullptr && !isRemovedFromCurrentState()) {
- sp<Layer> barrierLayer = mCurrentState.barrierLayer_legacy.promote();
- if (barrierLayer == nullptr) {
- ALOGE("[%s] Unable to promote barrier Layer.", getDebugName());
- // If we can't promote the layer we are intended to wait on,
- // then it is expired or otherwise invalid. Allow this transaction
- // to be applied as per normal (no synchronization).
- mCurrentState.barrierLayer_legacy = nullptr;
- } else {
- auto syncPoint = std::make_shared<SyncPoint>(mCurrentState.frameNumber_legacy,
- this,
- barrierLayer);
- if (barrierLayer->addSyncPoint(syncPoint)) {
- std::stringstream ss;
- ss << "Adding sync point " << mCurrentState.frameNumber_legacy;
- ATRACE_NAME(ss.str().c_str());
- mRemoteSyncPoints.push_back(std::move(syncPoint));
- } else {
- // We already missed the frame we're supposed to synchronize
- // on, so go ahead and apply the state update
- mCurrentState.barrierLayer_legacy = nullptr;
- }
- }
-
- // Wake us up to check if the frame has been received
- setTransactionFlags(eTransactionNeeded);
- mFlinger->setTransactionFlags(eTraversalNeeded);
- }
- mPendingStates.push_back(mCurrentState);
- ATRACE_INT(mTransactionName.c_str(), mPendingStates.size());
-}
-
-void Layer::popPendingState(State* stateToCommit) {
- ATRACE_CALL();
- *stateToCommit = mPendingStates[0];
-
- mPendingStates.pop_front();
- ATRACE_INT(mTransactionName.c_str(), mPendingStates.size());
-}
-
-bool Layer::applyPendingStates(State* stateToCommit) {
- bool stateUpdateAvailable = false;
- while (!mPendingStates.empty()) {
- if (mPendingStates[0].barrierLayer_legacy != nullptr) {
- if (mRemoteSyncPoints.empty()) {
- // If we don't have a sync point for this, apply it anyway. It
- // will be visually wrong, but it should keep us from getting
- // into too much trouble.
- ALOGE("[%s] No local sync point found", getDebugName());
- popPendingState(stateToCommit);
- stateUpdateAvailable = true;
- continue;
- }
-
- if (mRemoteSyncPoints.front()->getFrameNumber() !=
- mPendingStates[0].frameNumber_legacy) {
- ALOGE("[%s] Unexpected sync point frame number found", getDebugName());
-
- // Signal our end of the sync point and then dispose of it
- mRemoteSyncPoints.front()->setTransactionApplied();
- mRemoteSyncPoints.pop_front();
- continue;
- }
-
- if (mRemoteSyncPoints.front()->frameIsAvailable()) {
- ATRACE_NAME("frameIsAvailable");
- // Apply the state update
- popPendingState(stateToCommit);
- stateUpdateAvailable = true;
-
- // Signal our end of the sync point and then dispose of it
- mRemoteSyncPoints.front()->setTransactionApplied();
- mRemoteSyncPoints.pop_front();
- } else {
- ATRACE_NAME("!frameIsAvailable");
- mRemoteSyncPoints.front()->checkTimeoutAndLog();
- break;
- }
- } else {
- popPendingState(stateToCommit);
- stateUpdateAvailable = true;
- }
- }
-
- // If we still have pending updates, we need to ensure SurfaceFlinger
- // will keep calling doTransaction, and so we force a traversal.
- // However, our pending states won't clear until a frame is available,
- // and so there is no need to specifically trigger a wakeup.
- if (!mPendingStates.empty()) {
- setTransactionFlags(eTransactionNeeded);
- mFlinger->setTraversalNeeded();
- }
-
- mCurrentState.modified = false;
- return stateUpdateAvailable;
-}
-
uint32_t Layer::doTransactionResize(uint32_t flags, State* stateToCommit) {
const State& s(getDrawingState());
@@ -894,13 +783,12 @@
" requested={ wh={%4u,%4u} }}\n",
this, getName().c_str(), getBufferTransform(), getEffectiveScalingMode(),
stateToCommit->active_legacy.w, stateToCommit->active_legacy.h,
- stateToCommit->crop_legacy.left, stateToCommit->crop_legacy.top,
- stateToCommit->crop_legacy.right, stateToCommit->crop_legacy.bottom,
- stateToCommit->crop_legacy.getWidth(), stateToCommit->crop_legacy.getHeight(),
- stateToCommit->requested_legacy.w, stateToCommit->requested_legacy.h,
- s.active_legacy.w, s.active_legacy.h, s.crop_legacy.left, s.crop_legacy.top,
- s.crop_legacy.right, s.crop_legacy.bottom, s.crop_legacy.getWidth(),
- s.crop_legacy.getHeight(), s.requested_legacy.w, s.requested_legacy.h);
+ stateToCommit->crop.left, stateToCommit->crop.top, stateToCommit->crop.right,
+ stateToCommit->crop.bottom, stateToCommit->crop.getWidth(),
+ stateToCommit->crop.getHeight(), stateToCommit->requested_legacy.w,
+ stateToCommit->requested_legacy.h, s.active_legacy.w, s.active_legacy.h,
+ s.crop.left, s.crop.top, s.crop.right, s.crop.bottom, s.crop.getWidth(),
+ s.crop.getHeight(), s.requested_legacy.w, s.requested_legacy.h);
}
// Don't let Layer::doTransaction update the drawing state
@@ -954,33 +842,19 @@
uint32_t Layer::doTransaction(uint32_t flags) {
ATRACE_CALL();
- if (mLayerDetached) {
- // Ensure BLAST buffer callbacks are processed.
- // detachChildren and mLayerDetached were implemented to avoid geometry updates
- // to layers in the cases of animation. For BufferQueue layers buffers are still
- // consumed as normal. This is useful as otherwise the client could get hung
- // inevitably waiting on a buffer to return. We recreate this semantic for BufferQueue
- // even though it is a little consistent. detachChildren is shortly slated for removal
- // by the hierarchy mirroring work so we don't need to worry about it too much.
- forceSendCallbacks();
- mCurrentState.callbackHandles = {};
- return flags;
- }
-
if (mChildrenChanged) {
flags |= eVisibleRegion;
mChildrenChanged = false;
}
- pushPendingState();
- State c = getCurrentState();
- if (!applyPendingStates(&c)) {
- return flags;
- }
+ // TODO: This is unfortunate.
+ mCurrentStateModified = mCurrentState.modified;
+ mCurrentState.modified = false;
- flags = doTransactionResize(flags, &c);
+ flags = doTransactionResize(flags, &mCurrentState);
const State& s(getDrawingState());
+ State& c(getCurrentState());
if (getActiveGeometry(c) != getActiveGeometry(s)) {
// invalidate and recompute the visible regions if needed
@@ -993,8 +867,7 @@
this->contentDirty = true;
// we may use linear filtering, if the matrix scales us
- const uint8_t type = getActiveTransform(c).getType();
- mNeedsFiltering = (!getActiveTransform(c).preserveRects() || type >= ui::Transform::SCALE);
+ mNeedsFiltering = getActiveTransform(c).needsBilinearFiltering();
}
if (mCurrentState.inputInfoChanged) {
@@ -1002,16 +875,51 @@
mCurrentState.inputInfoChanged = false;
}
+ // Add the callbacks from the drawing state into the current state. This is so when the current
+ // state gets copied to drawing, we don't lose the callback handles that are still in drawing.
+ for (auto& handle : s.callbackHandles) {
+ c.callbackHandles.push_back(handle);
+ }
+
+ // Allow BufferStateLayer to release any unlatched buffers in drawing state.
+ bufferMayChange(c.buffer->getBuffer());
+
// Commit the transaction
commitTransaction(c);
- mPendingStatesSnapshot = mPendingStates;
mCurrentState.callbackHandles = {};
return flags;
}
-void Layer::commitTransaction(const State& stateToCommit) {
+void Layer::commitTransaction(State& stateToCommit) {
+ if (auto& bufferSurfaceFrame = mDrawingState.bufferSurfaceFrameTX;
+ ((mDrawingState.buffer && stateToCommit.buffer &&
+ mDrawingState.buffer->getBuffer() != stateToCommit.buffer->getBuffer()) ||
+ (mDrawingState.buffer && !stateToCommit.buffer) ||
+ (!mDrawingState.buffer && stateToCommit.buffer)) &&
+ bufferSurfaceFrame != nullptr &&
+ bufferSurfaceFrame->getPresentState() != PresentState::Presented) {
+ // If the previous buffer was committed but not latched (refreshPending - happens during
+ // back to back invalidates), it gets silently dropped here. Mark the corresponding
+ // SurfaceFrame as dropped to prevent it from getting stuck in the pending classification
+ // list.
+ addSurfaceFrameDroppedForBuffer(bufferSurfaceFrame);
+ }
mDrawingState = stateToCommit;
+
+ // Set the present state for all bufferlessSurfaceFramesTX to Presented. The
+ // bufferSurfaceFrameTX will be presented in latchBuffer.
+ for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) {
+ if (surfaceFrame->getPresentState() != PresentState::Presented) {
+ // With applyPendingStates, we could end up having presented surfaceframes from previous
+ // states
+ surfaceFrame->setPresentState(PresentState::Presented);
+ mFlinger->mFrameTimeline->addSurfaceFrame(surfaceFrame);
+ }
+ }
+ // Clear the surfaceFrames from the old state now that it has been copied into DrawingState.
+ stateToCommit.bufferSurfaceFrameTX.reset();
+ stateToCommit.bufferlessSurfaceFramesTX.clear();
}
uint32_t Layer::getTransactionFlags(uint32_t flags) {
@@ -1228,7 +1136,8 @@
t.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
if (!allowNonRectPreservingTransforms && !t.preserveRects()) {
- ALOGW("Attempt to set rotation matrix without permission ACCESS_SURFACE_FLINGER ignored");
+ ALOGW("Attempt to set rotation matrix without permission ACCESS_SURFACE_FLINGER nor "
+ "ROTATE_SURFACE_FLINGER ignored");
return false;
}
mCurrentState.sequence++;
@@ -1246,7 +1155,15 @@
return true;
}
-bool Layer::setFlags(uint8_t flags, uint8_t mask) {
+bool Layer::setBlurRegions(const std::vector<BlurRegion>& blurRegions) {
+ mCurrentState.sequence++;
+ mCurrentState.blurRegions = blurRegions;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+bool Layer::setFlags(uint32_t flags, uint32_t mask) {
const uint32_t newFlags = (mCurrentState.flags & ~mask) | (flags & mask);
if (mCurrentState.flags == newFlags) return false;
mCurrentState.sequence++;
@@ -1256,27 +1173,19 @@
return true;
}
-bool Layer::setCrop_legacy(const Rect& crop) {
- if (mCurrentState.requestedCrop_legacy == crop) return false;
+bool Layer::setCrop(const Rect& crop) {
+ if (mCurrentState.requestedCrop == crop) return false;
mCurrentState.sequence++;
- mCurrentState.requestedCrop_legacy = crop;
- mCurrentState.crop_legacy = crop;
+ mCurrentState.requestedCrop = crop;
+ mCurrentState.crop = crop;
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
}
-bool Layer::setOverrideScalingMode(int32_t scalingMode) {
- if (scalingMode == mOverrideScalingMode) return false;
- mOverrideScalingMode = scalingMode;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
bool Layer::setMetadata(const LayerMetadata& data) {
if (!mCurrentState.metadata.merge(data, true /* eraseEmpty */)) return false;
- mCurrentState.sequence++;
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
@@ -1361,6 +1270,35 @@
return true;
}
+bool Layer::setStretchEffect(const StretchEffect& effect) {
+ StretchEffect temp = effect;
+ temp.sanitize();
+ if (mCurrentState.stretchEffect == temp) {
+ return false;
+ }
+ mCurrentState.sequence++;
+ mCurrentState.stretchEffect = temp;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+StretchEffect Layer::getStretchEffect() const {
+ if (mDrawingState.stretchEffect.hasEffect()) {
+ return mDrawingState.stretchEffect;
+ }
+
+ sp<Layer> parent = getParent();
+ if (parent != nullptr) {
+ auto effect = parent->getStretchEffect();
+ if (effect.hasEffect()) {
+ // TODO(b/179047472): Map it? Or do we make the effect be in global space?
+ return effect;
+ }
+ }
+ return StretchEffect{};
+}
+
void Layer::updateTreeHasFrameRateVote() {
const auto traverseTree = [&](const LayerVector::Visitor& visitor) {
auto parent = getParent();
@@ -1373,31 +1311,40 @@
};
// update parents and children about the vote
- // First traverse the tree and count how many layers has votes
+ // First traverse the tree and count how many layers has votes. In addition
+ // activate the layers in Scheduler's LayerHistory for it to check for changes
int layersWithVote = 0;
traverseTree([&layersWithVote](Layer* layer) {
- const auto layerVotedWithDefaultCompatibility = layer->mCurrentState.frameRate.rate > 0 &&
+ const auto layerVotedWithDefaultCompatibility =
+ layer->mCurrentState.frameRate.rate.isValid() &&
layer->mCurrentState.frameRate.type == FrameRateCompatibility::Default;
const auto layerVotedWithNoVote =
layer->mCurrentState.frameRate.type == FrameRateCompatibility::NoVote;
+ const auto layerVotedWithExactCompatibility =
+ layer->mCurrentState.frameRate.type == FrameRateCompatibility::Exact;
// We do not count layers that are ExactOrMultiple for the same reason
// we are allowing touch boost for those layers. See
// RefreshRateConfigs::getBestRefreshRate for more details.
- if (layerVotedWithDefaultCompatibility || layerVotedWithNoVote) {
+ if (layerVotedWithDefaultCompatibility || layerVotedWithNoVote ||
+ layerVotedWithExactCompatibility) {
layersWithVote++;
}
});
// Now update the other layers
bool transactionNeeded = false;
- traverseTree([layersWithVote, &transactionNeeded](Layer* layer) {
- if (layer->mCurrentState.treeHasFrameRateVote != layersWithVote > 0) {
+ traverseTree([layersWithVote, &transactionNeeded, this](Layer* layer) {
+ const bool treeHasFrameRateVote = layersWithVote > 0;
+ if (layer->mCurrentState.treeHasFrameRateVote != treeHasFrameRateVote) {
layer->mCurrentState.sequence++;
- layer->mCurrentState.treeHasFrameRateVote = layersWithVote > 0;
+ layer->mCurrentState.treeHasFrameRateVote = treeHasFrameRateVote;
layer->mCurrentState.modified = true;
layer->setTransactionFlags(eTransactionNeeded);
transactionNeeded = true;
+
+ mFlinger->mScheduler->recordLayerHistory(layer, systemTime(),
+ LayerHistory::LayerUpdateType::SetFrameRate);
}
});
@@ -1414,10 +1361,6 @@
return false;
}
- // Activate the layer in Scheduler's LayerHistory
- mFlinger->mScheduler->recordLayerHistory(this, systemTime(),
- LayerHistory::LayerUpdateType::SetFrameRate);
-
mCurrentState.sequence++;
mCurrentState.frameRate = frameRate;
mCurrentState.modified = true;
@@ -1428,46 +1371,132 @@
return true;
}
+void Layer::setFrameTimelineVsyncForBufferTransaction(const FrameTimelineInfo& info,
+ nsecs_t postTime) {
+ mCurrentState.postTime = postTime;
+
+ // Check if one of the bufferlessSurfaceFramesTX contains the same vsyncId. This can happen if
+ // there are two transactions with the same token, the first one without a buffer and the
+ // second one with a buffer. We promote the bufferlessSurfaceFrame to a bufferSurfaceFrameTX
+ // in that case.
+ auto it = mCurrentState.bufferlessSurfaceFramesTX.find(info.vsyncId);
+ if (it != mCurrentState.bufferlessSurfaceFramesTX.end()) {
+ // Promote the bufferlessSurfaceFrame to a bufferSurfaceFrameTX
+ mCurrentState.bufferSurfaceFrameTX = it->second;
+ mCurrentState.bufferlessSurfaceFramesTX.erase(it);
+ mCurrentState.bufferSurfaceFrameTX->promoteToBuffer();
+ mCurrentState.bufferSurfaceFrameTX->setActualQueueTime(postTime);
+ } else {
+ mCurrentState.bufferSurfaceFrameTX =
+ createSurfaceFrameForBuffer(info, postTime, mTransactionName);
+ }
+}
+
+void Layer::setFrameTimelineVsyncForBufferlessTransaction(const FrameTimelineInfo& info,
+ nsecs_t postTime) {
+ mCurrentState.frameTimelineInfo = info;
+ mCurrentState.postTime = postTime;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+
+ if (const auto& bufferSurfaceFrameTX = mCurrentState.bufferSurfaceFrameTX;
+ bufferSurfaceFrameTX != nullptr) {
+ if (bufferSurfaceFrameTX->getToken() == info.vsyncId) {
+ // BufferSurfaceFrame takes precedence over BufferlessSurfaceFrame. If the same token is
+ // being used for BufferSurfaceFrame, don't create a new one.
+ return;
+ }
+ }
+ // For Transactions without a buffer, we create only one SurfaceFrame per vsyncId. If multiple
+ // transactions use the same vsyncId, we just treat them as one SurfaceFrame (unless they are
+ // targeting different vsyncs).
+ auto it = mCurrentState.bufferlessSurfaceFramesTX.find(info.vsyncId);
+ if (it == mCurrentState.bufferlessSurfaceFramesTX.end()) {
+ auto surfaceFrame = createSurfaceFrameForTransaction(info, postTime);
+ mCurrentState.bufferlessSurfaceFramesTX[info.vsyncId] = surfaceFrame;
+ } else {
+ if (it->second->getPresentState() == PresentState::Presented) {
+ // If the SurfaceFrame was already presented, its safe to overwrite it since it must
+ // have been from previous vsync.
+ it->second = createSurfaceFrameForTransaction(info, postTime);
+ }
+ }
+}
+
+void Layer::addSurfaceFrameDroppedForBuffer(
+ std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame) {
+ surfaceFrame->setDropTime(systemTime());
+ surfaceFrame->setPresentState(PresentState::Dropped);
+ mFlinger->mFrameTimeline->addSurfaceFrame(surfaceFrame);
+}
+
+void Layer::addSurfaceFramePresentedForBuffer(
+ std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame, nsecs_t acquireFenceTime,
+ nsecs_t currentLatchTime) {
+ surfaceFrame->setAcquireFenceTime(acquireFenceTime);
+ surfaceFrame->setPresentState(PresentState::Presented, mLastLatchTime);
+ mFlinger->mFrameTimeline->addSurfaceFrame(surfaceFrame);
+ mLastLatchTime = currentLatchTime;
+}
+
+std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForTransaction(
+ const FrameTimelineInfo& info, nsecs_t postTime) {
+ auto surfaceFrame =
+ mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
+ getSequence(), mName,
+ mTransactionName,
+ /*isBuffer*/ false);
+ // For Transactions, the post time is considered to be both queue and acquire fence time.
+ surfaceFrame->setActualQueueTime(postTime);
+ surfaceFrame->setAcquireFenceTime(postTime);
+ const auto fps = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
+ if (fps) {
+ surfaceFrame->setRenderRate(*fps);
+ }
+ onSurfaceFrameCreated(surfaceFrame);
+ return surfaceFrame;
+}
+
+std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForBuffer(
+ const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName) {
+ auto surfaceFrame =
+ mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
+ getSequence(), mName, debugName,
+ /*isBuffer*/ true);
+ // For buffers, acquire fence time will set during latch.
+ surfaceFrame->setActualQueueTime(queueTime);
+ const auto fps = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
+ if (fps) {
+ surfaceFrame->setRenderRate(*fps);
+ }
+ // TODO(b/178542907): Implement onSurfaceFrameCreated for BQLayer as well.
+ onSurfaceFrameCreated(surfaceFrame);
+ return surfaceFrame;
+}
+
Layer::FrameRate Layer::getFrameRateForLayerTree() const {
const auto frameRate = getDrawingState().frameRate;
- if (frameRate.rate > 0 || frameRate.type == FrameRateCompatibility::NoVote) {
+ if (frameRate.rate.isValid() || frameRate.type == FrameRateCompatibility::NoVote) {
return frameRate;
}
- // This layer doesn't have a frame rate. If one of its ancestors or successors
- // have a vote, return a NoVote for ancestors/successors to set the vote
+ // This layer doesn't have a frame rate. Check if its ancestors have a vote
+ if (sp<Layer> parent = getParent(); parent) {
+ if (const auto parentFrameRate = parent->getFrameRateForLayerTree();
+ parentFrameRate.rate.isValid()) {
+ return parentFrameRate;
+ }
+ }
+
+ // This layer and its ancestors don't have a frame rate. If one of successors
+ // has a vote, return a NoVote for successors to set the vote
if (getDrawingState().treeHasFrameRateVote) {
- return {0, FrameRateCompatibility::NoVote};
+ return {Fps(0.0f), FrameRateCompatibility::NoVote};
}
return frameRate;
}
-void Layer::deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber) {
- ATRACE_CALL();
- if (mLayerDetached) {
- // If the layer is detached, then we don't defer this transaction since we will not
- // commit the pending state while the layer is detached. Adding sync points may cause
- // the barrier layer to wait for the states to be committed before dequeuing a buffer.
- return;
- }
-
- mCurrentState.barrierLayer_legacy = barrierLayer;
- mCurrentState.frameNumber_legacy = frameNumber;
- // We don't set eTransactionNeeded, because just receiving a deferral
- // request without any other state updates shouldn't actually induce a delay
- mCurrentState.modified = true;
- pushPendingState();
- mCurrentState.barrierLayer_legacy = nullptr;
- mCurrentState.frameNumber_legacy = 0;
- mCurrentState.modified = false;
-}
-
-void Layer::deferTransactionUntil_legacy(const sp<IBinder>& barrierHandle, uint64_t frameNumber) {
- sp<Handle> handle = static_cast<Handle*>(barrierHandle.get());
- deferTransactionUntil_legacy(handle->owner.promote(), frameNumber);
-}
-
// ----------------------------------------------------------------------------
// pageflip handling...
// ----------------------------------------------------------------------------
@@ -1534,7 +1563,7 @@
info.mZ = ds.z;
info.mWidth = ds.active_legacy.w;
info.mHeight = ds.active_legacy.h;
- info.mCrop = ds.crop_legacy;
+ info.mCrop = ds.crop;
info.mColor = ds.color;
info.mFlags = ds.flags;
info.mPixelFormat = getPixelFormat();
@@ -1561,15 +1590,13 @@
info.mRefreshPending = isBufferLatched();
info.mIsOpaque = isOpaque(ds);
info.mContentDirty = contentDirty;
+ info.mStretchEffect = getStretchEffect();
return info;
}
void Layer::miniDumpHeader(std::string& result) {
- result.append("-------------------------------");
- result.append("-------------------------------");
- result.append("-------------------------------");
- result.append("-------------------------------");
- result.append("-------------------\n");
+ result.append(kDumpTableRowLength, '-');
+ result.append("\n");
result.append(" Layer name\n");
result.append(" Z | ");
result.append(" Window Type | ");
@@ -1577,12 +1604,9 @@
result.append(" Transform | ");
result.append(" Disp Frame (LTRB) | ");
result.append(" Source Crop (LTRB) | ");
- result.append(" Frame Rate (Explicit) [Focused]\n");
- result.append("-------------------------------");
- result.append("-------------------------------");
- result.append("-------------------------------");
- result.append("-------------------------------");
- result.append("-------------------\n");
+ result.append(" Frame Rate (Explicit) (Seamlessness) [Focused]\n");
+ result.append(kDumpTableRowLength, '-');
+ result.append("\n");
}
std::string Layer::frameRateCompatibilityString(Layer::FrameRateCompatibility compatibility) {
@@ -1593,6 +1617,8 @@
return "ExactOrMultiple";
case FrameRateCompatibility::NoVote:
return "NoVote";
+ case FrameRateCompatibility::Exact:
+ return "Exact";
}
}
@@ -1631,22 +1657,20 @@
const FloatRect& crop = outputLayerState.sourceCrop;
StringAppendF(&result, "%6.1f %6.1f %6.1f %6.1f | ", crop.left, crop.top, crop.right,
crop.bottom);
- if (layerState.frameRate.rate != 0 ||
- layerState.frameRate.type != FrameRateCompatibility::Default) {
- StringAppendF(&result, "% 6.2ffps %15s", layerState.frameRate.rate,
- frameRateCompatibilityString(layerState.frameRate.type).c_str());
+ const auto frameRate = getFrameRateForLayerTree();
+ if (frameRate.rate.isValid() || frameRate.type != FrameRateCompatibility::Default) {
+ StringAppendF(&result, "%s %15s %17s", to_string(frameRate.rate).c_str(),
+ frameRateCompatibilityString(frameRate.type).c_str(),
+ toString(frameRate.seamlessness).c_str());
} else {
- StringAppendF(&result, " ");
+ result.append(41, ' ');
}
const auto focused = isLayerFocusedBasedOnPriority(getFrameRateSelectionPriority());
StringAppendF(&result, " [%s]\n", focused ? "*" : " ");
- result.append("- - - - - - - - - - - - - - - - ");
- result.append("- - - - - - - - - - - - - - - - ");
- result.append("- - - - - - - - - - - - - - - - ");
- result.append("- - - - - - - - - - - - - - - - ");
- result.append("- - - - - - - -\n");
+ result.append(kDumpTableRowLength, '-');
+ result.append("\n");
}
void Layer::dumpFrameStats(std::string& result) const {
@@ -1673,8 +1697,8 @@
}
void Layer::dumpCallingUidPid(std::string& result) const {
- StringAppendF(&result, "Layer %s (%s) pid:%d uid:%d\n", getName().c_str(), getType(),
- mCallingPid, mCallingUid);
+ StringAppendF(&result, "Layer %s (%s) callingPid:%d callingUid:%d ownerUid:%d\n",
+ getName().c_str(), getType(), mCallingPid, mCallingUid, mOwnerUid);
}
void Layer::onDisconnect() {
@@ -1689,7 +1713,7 @@
FrameEventHistoryDelta* outDelta) {
if (newTimestamps) {
mFlinger->mTimeStats->setPostTime(getSequence(), newTimestamps->frameNumber,
- getName().c_str(), newTimestamps->postedTime);
+ getName().c_str(), mOwnerUid, newTimestamps->postedTime);
mFlinger->mTimeStats->setAcquireFence(getSequence(), newTimestamps->frameNumber,
newTimestamps->acquireFence);
}
@@ -1741,36 +1765,6 @@
return removeResult;
}
-void Layer::reparentChildren(const sp<Layer>& newParent) {
- if (attachChildren()) {
- setTransactionFlags(eTransactionNeeded);
- }
-
- for (const sp<Layer>& child : mCurrentChildren) {
- newParent->addChild(child);
- }
- mCurrentChildren.clear();
- updateTreeHasFrameRateVote();
-}
-
-bool Layer::reparentChildren(const sp<IBinder>& newParentHandle) {
- sp<Handle> handle = nullptr;
- sp<Layer> newParent = nullptr;
- if (newParentHandle == nullptr) {
- return false;
- }
- handle = static_cast<Handle*>(newParentHandle.get());
- newParent = handle->owner.promote();
- if (newParent == nullptr) {
- ALOGE("Unable to promote Layer handle");
- return false;
- }
-
- reparentChildren(newParent);
-
- return true;
-}
-
void Layer::setChildrenDrawingParent(const sp<Layer>& newParent) {
for (const sp<Layer>& child : mDrawingChildren) {
child->mDrawingParent = newParent;
@@ -1781,17 +1775,6 @@
}
bool Layer::reparent(const sp<IBinder>& newParentHandle) {
- bool callSetTransactionFlags = false;
-
- // While layers are detached, we allow most operations
- // and simply halt performing the actual transaction. However
- // for reparent != null we would enter the mRemovedFromCurrentState
- // state, regardless of whether doTransaction was called, and
- // so we need to prevent the update here.
- if (mLayerDetached && newParentHandle == nullptr) {
- return false;
- }
-
sp<Layer> newParent;
if (newParentHandle != nullptr) {
auto handle = static_cast<Handle*>(newParentHandle.get());
@@ -1818,52 +1801,13 @@
} else {
onRemovedFromCurrentState();
}
-
- if (mLayerDetached) {
- mLayerDetached = false;
- callSetTransactionFlags = true;
- }
} else {
onRemovedFromCurrentState();
}
- if (callSetTransactionFlags || attachChildren()) {
- setTransactionFlags(eTransactionNeeded);
- }
return true;
}
-bool Layer::detachChildren() {
- for (const sp<Layer>& child : mCurrentChildren) {
- sp<Client> parentClient = mClientRef.promote();
- sp<Client> client(child->mClientRef.promote());
- if (client != nullptr && parentClient != client) {
- child->mLayerDetached = true;
- child->detachChildren();
- child->removeRemoteSyncPoints();
- }
- }
-
- return true;
-}
-
-bool Layer::attachChildren() {
- bool changed = false;
- for (const sp<Layer>& child : mCurrentChildren) {
- sp<Client> parentClient = mClientRef.promote();
- sp<Client> client(child->mClientRef.promote());
- if (client != nullptr && parentClient != client) {
- if (child->mLayerDetached) {
- child->mLayerDetached = false;
- changed = true;
- }
- changed |= child->attachChildren();
- }
- }
-
- return changed;
-}
-
bool Layer::setColorTransform(const mat4& matrix) {
static const mat4 identityMatrix = mat4();
@@ -2130,7 +2074,19 @@
}
int32_t Layer::getBackgroundBlurRadius() const {
- return getDrawingState().backgroundBlurRadius;
+ const auto& p = mDrawingParent.promote();
+
+ half parentAlpha = (p != nullptr) ? p->getAlpha() : 1.0_hf;
+ return parentAlpha * getDrawingState().backgroundBlurRadius;
+}
+
+const std::vector<BlurRegion> Layer::getBlurRegions() const {
+ auto regionsCopy(getDrawingState().blurRegions);
+ int layerAlpha = getAlpha();
+ for (auto& region : regionsCopy) {
+ region.alpha = region.alpha * layerAlpha;
+ }
+ return regionsCopy;
}
Layer::RoundedCornerState Layer::getRoundedCornerState() const {
@@ -2152,17 +2108,17 @@
}
}
const float radius = getDrawingState().cornerRadius;
- return radius > 0 && getCrop(getDrawingState()).isValid()
- ? RoundedCornerState(getCrop(getDrawingState()).toFloatRect(), radius)
+ return radius > 0 && getCroppedBufferSize(getDrawingState()).isValid()
+ ? RoundedCornerState(getCroppedBufferSize(getDrawingState()).toFloatRect(), radius)
: RoundedCornerState();
}
-renderengine::ShadowSettings Layer::getShadowSettings(const Rect& viewport) const {
+renderengine::ShadowSettings Layer::getShadowSettings(const Rect& layerStackRect) const {
renderengine::ShadowSettings state = mFlinger->mDrawingState.globalShadowSettings;
// Shift the spot light x-position to the middle of the display and then
// offset it by casting layer's screen pos.
- state.lightPos.x = (viewport.width() / 2.f) - mScreenBounds.left;
+ state.lightPos.x = (layerStackRect.width() / 2.f) - mScreenBounds.left;
state.lightPos.y -= mScreenBounds.top;
state.length = mEffectiveShadowRadius;
@@ -2202,7 +2158,7 @@
}
LayerProto* Layer::writeToProto(LayersProto& layersProto, uint32_t traceFlags,
- const DisplayDevice* display) const {
+ const DisplayDevice* display) {
LayerProto* layerProto = layersProto.add_layers();
writeToProtoDrawingState(layerProto, traceFlags, display);
writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, traceFlags);
@@ -2223,18 +2179,10 @@
}
void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags,
- const DisplayDevice* display) const {
- ui::Transform transform = getTransform();
+ const DisplayDevice* display) {
+ const ui::Transform transform = getTransform();
if (traceFlags & SurfaceTracing::TRACE_CRITICAL) {
- for (const auto& pendingState : mPendingStatesSnapshot) {
- auto barrierLayer = pendingState.barrierLayer_legacy.promote();
- if (barrierLayer != nullptr) {
- BarrierLayerProto* barrierLayerProto = layerInfo->add_barrier_layer();
- barrierLayerProto->set_id(barrierLayer->sequence);
- barrierLayerProto->set_frame_number(pendingState.frameNumber_legacy);
- }
- }
auto buffer = getBuffer();
if (buffer != nullptr) {
@@ -2252,6 +2200,7 @@
layerInfo->set_effective_scaling_mode(getEffectiveScalingMode());
layerInfo->set_corner_radius(getRoundedCornerState().radius);
+ layerInfo->set_background_blur_radius(getBackgroundBlurRadius());
LayerProtoHelper::writeToProto(transform, layerInfo->mutable_transform());
LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(),
[&]() { return layerInfo->mutable_position(); });
@@ -2279,12 +2228,12 @@
}
void Layer::writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet stateSet,
- uint32_t traceFlags) const {
+ uint32_t traceFlags) {
const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren;
const State& state = useDrawing ? mDrawingState : mCurrentState;
- ui::Transform requestedTransform = state.active_legacy.transform;
+ ui::Transform requestedTransform = state.transform;
if (traceFlags & SurfaceTracing::TRACE_CRITICAL) {
layerInfo->set_id(sequence);
@@ -2313,11 +2262,10 @@
return layerInfo->mutable_requested_position();
});
- LayerProtoHelper::writeSizeToProto(state.active_legacy.w, state.active_legacy.h,
+ LayerProtoHelper::writeSizeToProto(state.width, state.height,
[&]() { return layerInfo->mutable_size(); });
- LayerProtoHelper::writeToProto(state.crop_legacy,
- [&]() { return layerInfo->mutable_crop(); });
+ LayerProtoHelper::writeToProto(state.crop, [&]() { return layerInfo->mutable_crop(); });
layerInfo->set_is_opaque(isOpaque(state));
@@ -2346,10 +2294,19 @@
}
layerInfo->set_is_relative_of(state.isRelativeOf);
+
+ layerInfo->set_owner_uid(mOwnerUid);
}
if (traceFlags & SurfaceTracing::TRACE_INPUT) {
- LayerProtoHelper::writeToProto(state.inputInfo, state.touchableRegionCrop,
+ InputWindowInfo info;
+ if (useDrawing) {
+ info = fillInputInfo({nullptr});
+ } else {
+ info = state.inputInfo;
+ }
+
+ LayerProtoHelper::writeToProto(info, state.touchableRegionCrop,
[&]() { return layerInfo->mutable_input_window_info(); });
}
@@ -2365,14 +2322,116 @@
return mRemovedFromCurrentState;
}
-InputWindowInfo Layer::fillInputInfo() {
+ui::Transform Layer::getInputTransform() const {
+ return getTransform();
+}
+
+Rect Layer::getInputBounds() const {
+ return getCroppedBufferSize(getDrawingState());
+}
+
+void Layer::fillInputFrameInfo(InputWindowInfo& info, const ui::Transform& toPhysicalDisplay) {
+ // Transform layer size to screen space and inset it by surface insets.
+ // If this is a portal window, set the touchableRegion to the layerBounds.
+ Rect layerBounds = info.portalToDisplayId == ADISPLAY_ID_NONE
+ ? getInputBounds()
+ : info.touchableRegion.getBounds();
+ if (!layerBounds.isValid()) {
+ layerBounds = getInputBounds();
+ }
+
+ if (!layerBounds.isValid()) {
+ // If the layer bounds is empty, set the frame to empty and clear the transform
+ info.frameLeft = 0;
+ info.frameTop = 0;
+ info.frameRight = 0;
+ info.frameBottom = 0;
+ info.transform.reset();
+ return;
+ }
+
+ ui::Transform layerToDisplay = getInputTransform();
+ // Transform that takes window coordinates to unrotated display coordinates
+ ui::Transform t = toPhysicalDisplay * layerToDisplay;
+ int32_t xSurfaceInset = info.surfaceInset;
+ int32_t ySurfaceInset = info.surfaceInset;
+ // Bring screenBounds into unrotated space
+ Rect screenBounds = toPhysicalDisplay.transform(Rect{mScreenBounds});
+
+ const float xScale = t.getScaleX();
+ const float yScale = t.getScaleY();
+ if (xScale != 1.0f || yScale != 1.0f) {
+ xSurfaceInset = std::round(xSurfaceInset * xScale);
+ ySurfaceInset = std::round(ySurfaceInset * yScale);
+ }
+
+ // Transform the layer bounds from layer coordinate space to display coordinate space.
+ Rect transformedLayerBounds = t.transform(layerBounds);
+
+ // clamp inset to layer bounds
+ xSurfaceInset = (xSurfaceInset >= 0)
+ ? std::min(xSurfaceInset, transformedLayerBounds.getWidth() / 2)
+ : 0;
+ ySurfaceInset = (ySurfaceInset >= 0)
+ ? std::min(ySurfaceInset, transformedLayerBounds.getHeight() / 2)
+ : 0;
+
+ // inset while protecting from overflow TODO(b/161235021): What is going wrong
+ // in the overflow scenario?
+ {
+ int32_t tmp;
+ if (!__builtin_add_overflow(transformedLayerBounds.left, xSurfaceInset, &tmp))
+ transformedLayerBounds.left = tmp;
+ if (!__builtin_sub_overflow(transformedLayerBounds.right, xSurfaceInset, &tmp))
+ transformedLayerBounds.right = tmp;
+ if (!__builtin_add_overflow(transformedLayerBounds.top, ySurfaceInset, &tmp))
+ transformedLayerBounds.top = tmp;
+ if (!__builtin_sub_overflow(transformedLayerBounds.bottom, ySurfaceInset, &tmp))
+ transformedLayerBounds.bottom = tmp;
+ }
+
+ // Compute the correct transform to send to input. This will allow it to transform the
+ // input coordinates from display space into window space. Therefore, it needs to use the
+ // final layer frame to create the inverse transform. Since surface insets are added later,
+ // along with the overflow, the best way to ensure we get the correct transform is to use
+ // the final frame calculated.
+ // 1. Take the original transform set on the window and get the inverse transform. This is
+ // used to get the final bounds in display space (ignorning the transform). Apply the
+ // inverse transform on the layerBounds to get the untransformed frame (in layer space)
+ // 2. Take the top and left of the untransformed frame to get the real position on screen.
+ // Apply the layer transform on top/left so it includes any scale or rotation. These will
+ // be the new translation values for the transform.
+ // 3. Update the translation of the original transform to the new translation values.
+ // 4. Send the inverse transform to input so the coordinates can be transformed back into
+ // window space.
+ ui::Transform inverseTransform = t.inverse();
+ Rect nonTransformedBounds = inverseTransform.transform(transformedLayerBounds);
+ vec2 translation = t.transform(nonTransformedBounds.left, nonTransformedBounds.top);
+ ui::Transform inputTransform(t);
+ inputTransform.set(translation.x, translation.y);
+ info.transform = inputTransform.inverse();
+
+ // We need to send the layer bounds cropped to the screenbounds since the layer can be cropped.
+ // The frame should be the area the user sees on screen since it's used for occlusion
+ // detection.
+ transformedLayerBounds.intersect(screenBounds, &transformedLayerBounds);
+ info.frameLeft = transformedLayerBounds.left;
+ info.frameTop = transformedLayerBounds.top;
+ info.frameRight = transformedLayerBounds.right;
+ info.frameBottom = transformedLayerBounds.bottom;
+
+ // Position the touchable region relative to frame screen location and restrict it to frame
+ // bounds.
+ info.touchableRegion = inputTransform.transform(info.touchableRegion);
+}
+
+InputWindowInfo Layer::fillInputInfo(const sp<DisplayDevice>& display) {
if (!hasInputInfo()) {
mDrawingState.inputInfo.name = getName();
- mDrawingState.inputInfo.ownerUid = mCallingUid;
- mDrawingState.inputInfo.ownerPid = mCallingPid;
- mDrawingState.inputInfo.inputFeatures =
- InputWindowInfo::INPUT_FEATURE_NO_INPUT_CHANNEL;
- mDrawingState.inputInfo.layoutParamsFlags = InputWindowInfo::FLAG_NOT_TOUCH_MODAL;
+ mDrawingState.inputInfo.ownerUid = mOwnerUid;
+ mDrawingState.inputInfo.ownerPid = mOwnerPid;
+ mDrawingState.inputInfo.inputFeatures = InputWindowInfo::Feature::NO_INPUT_CHANNEL;
+ mDrawingState.inputInfo.flags = InputWindowInfo::Flag::NOT_TOUCH_MODAL;
mDrawingState.inputInfo.displayId = getLayerStack();
}
@@ -2383,52 +2442,14 @@
info.displayId = getLayerStack();
}
- ui::Transform t = getTransform();
- const float xScale = t.sx();
- const float yScale = t.sy();
- int32_t xSurfaceInset = info.surfaceInset;
- int32_t ySurfaceInset = info.surfaceInset;
- if (xScale != 1.0f || yScale != 1.0f) {
- info.windowXScale *= (xScale != 0.0f) ? 1.0f / xScale : 0.0f;
- info.windowYScale *= (yScale != 0.0f) ? 1.0f / yScale : 0.0f;
- info.touchableRegion.scaleSelf(xScale, yScale);
- xSurfaceInset = std::round(xSurfaceInset * xScale);
- ySurfaceInset = std::round(ySurfaceInset * yScale);
+ // Transform that goes from "logical(rotated)" display to physical/unrotated display.
+ // This is for when inputflinger operates in physical display-space.
+ ui::Transform toPhysicalDisplay;
+ if (display) {
+ toPhysicalDisplay = display->getTransform();
}
+ fillInputFrameInfo(info, toPhysicalDisplay);
- // Transform layer size to screen space and inset it by surface insets.
- // If this is a portal window, set the touchableRegion to the layerBounds.
- Rect layerBounds = info.portalToDisplayId == ADISPLAY_ID_NONE
- ? getBufferSize(getDrawingState())
- : info.touchableRegion.getBounds();
- if (!layerBounds.isValid()) {
- layerBounds = getCroppedBufferSize(getDrawingState());
- }
- layerBounds = t.transform(layerBounds);
-
- // clamp inset to layer bounds
- xSurfaceInset = (xSurfaceInset >= 0) ? std::min(xSurfaceInset, layerBounds.getWidth() / 2) : 0;
- ySurfaceInset = (ySurfaceInset >= 0) ? std::min(ySurfaceInset, layerBounds.getHeight() / 2) : 0;
-
- // inset while protecting from overflow TODO(b/161235021): What is going wrong
- // in the overflow scenario?
- {
- int32_t tmp;
- if (!__builtin_add_overflow(layerBounds.left, xSurfaceInset, &tmp)) layerBounds.left = tmp;
- if (!__builtin_sub_overflow(layerBounds.right, xSurfaceInset, &tmp)) layerBounds.right = tmp;
- if (!__builtin_add_overflow(layerBounds.top, ySurfaceInset, &tmp)) layerBounds.top = tmp;
- if (!__builtin_sub_overflow(layerBounds.bottom, ySurfaceInset, &tmp)) layerBounds.bottom = tmp;
- }
-
- // Input coordinate should match the layer bounds.
- info.frameLeft = layerBounds.left;
- info.frameTop = layerBounds.top;
- info.frameRight = layerBounds.right;
- info.frameBottom = layerBounds.bottom;
-
- // Position the touchable region relative to frame screen location and restrict it to frame
- // bounds.
- info.touchableRegion = info.touchableRegion.translate(info.frameLeft, info.frameTop);
// For compatibility reasons we let layers which can receive input
// receive input before they have actually submitted a buffer. Because
// of this we use canReceiveInput instead of isVisible to check the
@@ -2438,16 +2459,19 @@
// InputDispatcher, and obviously if they aren't visible they can't occlude
// anything.
info.visible = hasInputInfo() ? canReceiveInput() : isVisible();
+ info.alpha = getAlpha();
auto cropLayer = mDrawingState.touchableRegionCrop.promote();
if (info.replaceTouchableRegionWithCrop) {
if (cropLayer == nullptr) {
- info.touchableRegion = Region(Rect{mScreenBounds});
+ info.touchableRegion = Region(toPhysicalDisplay.transform(Rect{mScreenBounds}));
} else {
- info.touchableRegion = Region(Rect{cropLayer->mScreenBounds});
+ info.touchableRegion =
+ Region(toPhysicalDisplay.transform(Rect{cropLayer->mScreenBounds}));
}
} else if (cropLayer != nullptr) {
- info.touchableRegion = info.touchableRegion.intersect(Rect{cropLayer->mScreenBounds});
+ info.touchableRegion = info.touchableRegion.intersect(
+ toPhysicalDisplay.transform(Rect{cropLayer->mScreenBounds}));
}
// If the layer is a clone, we need to crop the input region to cloned root to prevent
@@ -2455,7 +2479,7 @@
if (isClone()) {
sp<Layer> clonedRoot = getClonedRoot();
if (clonedRoot != nullptr) {
- Rect rect(clonedRoot->mScreenBounds);
+ Rect rect = toPhysicalDisplay.transform(Rect{clonedRoot->mScreenBounds});
info.touchableRegion = info.touchableRegion.intersect(rect);
}
}
@@ -2582,7 +2606,7 @@
}
// Cloned layers shouldn't handle watch outside since their z order is not determined by
// WM or the client.
- mDrawingState.inputInfo.layoutParamsFlags &= ~InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH;
+ mDrawingState.inputInfo.flags &= ~InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH;
}
void Layer::updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) {
@@ -2631,14 +2655,44 @@
return FrameRateCompatibility::Default;
case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE:
return FrameRateCompatibility::ExactOrMultiple;
+ case ANATIVEWINDOW_FRAME_RATE_EXACT:
+ return FrameRateCompatibility::Exact;
default:
LOG_ALWAYS_FATAL("Invalid frame rate compatibility value %d", compatibility);
return FrameRateCompatibility::Default;
}
}
+scheduler::Seamlessness Layer::FrameRate::convertChangeFrameRateStrategy(int8_t strategy) {
+ switch (strategy) {
+ case ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS:
+ return Seamlessness::OnlySeamless;
+ case ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS:
+ return Seamlessness::SeamedAndSeamless;
+ default:
+ LOG_ALWAYS_FATAL("Invalid change frame sate strategy value %d", strategy);
+ return Seamlessness::Default;
+ }
+}
+
+bool Layer::getPrimaryDisplayOnly() const {
+ const State& s(mDrawingState);
+ if (s.flags & layer_state_t::eLayerSkipScreenshot) {
+ return true;
+ }
+
+ sp<Layer> parent = mDrawingParent.promote();
+ return parent == nullptr ? false : parent->getPrimaryDisplayOnly();
+}
+
// ---------------------------------------------------------------------------
+std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) {
+ return stream << "{rate=" << rate.rate
+ << " type=" << Layer::frameRateCompatibilityString(rate.type)
+ << " seamlessness=" << toString(rate.seamlessness) << "}";
+}
+
}; // namespace android
#if defined(__gl_h_)
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index d071710..688a2c3 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -26,11 +26,13 @@
#include <renderengine/Mesh.h>
#include <renderengine/Texture.h>
#include <sys/types.h>
+#include <ui/BlurRegion.h>
#include <ui/FloatRect.h>
#include <ui/FrameStats.h>
#include <ui/GraphicBuffer.h>
#include <ui/PixelFormat.h>
#include <ui/Region.h>
+#include <ui/StretchEffect.h>
#include <ui/Transform.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>
@@ -45,19 +47,21 @@
#include "ClientCache.h"
#include "DisplayHardware/ComposerHal.h"
#include "DisplayHardware/HWComposer.h"
+#include "Fps.h"
#include "FrameTracker.h"
#include "LayerVector.h"
#include "MonitoredProducer.h"
#include "RenderArea.h"
+#include "Scheduler/LayerInfo.h"
+#include "Scheduler/Seamlessness.h"
#include "SurfaceFlinger.h"
-#include "TransactionCompletedThread.h"
+#include "SurfaceTracing.h"
+#include "TransactionCallbackInvoker.h"
using namespace android::surfaceflinger;
namespace android {
-// ---------------------------------------------------------------------------
-
class Client;
class Colorizer;
class DisplayDevice;
@@ -74,7 +78,9 @@
class SurfaceInterceptor;
}
-// ---------------------------------------------------------------------------
+namespace frametimeline {
+class SurfaceFrame;
+} // namespace frametimeline
struct LayerCreationArgs {
LayerCreationArgs(SurfaceFlinger*, sp<Client>, std::string name, uint32_t w, uint32_t h,
@@ -107,14 +113,6 @@
static constexpr int32_t PRIORITY_NOT_FOCUSED_WITH_MODE = 2;
public:
- mutable bool contentDirty{false};
- Region surfaceDamageRegion;
-
- // Layer serial number. This gives layers an explicit ordering, so we
- // have a stable sort order when their layer stack and Z-order are
- // the same.
- int32_t sequence{sSequence++};
-
enum { // flags for doTransaction()
eDontUpdateGeometryState = 0x00000001,
eVisibleRegion = 0x00000002,
@@ -144,37 +142,8 @@
float radius = 0.0f;
};
- // FrameRateCompatibility specifies how we should interpret the frame rate associated with
- // the layer.
- enum class FrameRateCompatibility {
- Default, // Layer didn't specify any specific handling strategy
-
- ExactOrMultiple, // Layer needs the exact frame rate (or a multiple of it) to present the
- // content properly. Any other value will result in a pull down.
-
- NoVote, // Layer doesn't have any requirements for the refresh rate and
- // should not be considered when the display refresh rate is determined.
- };
-
- // Encapsulates the frame rate and compatibility of the layer. This information will be used
- // when the display refresh rate is determined.
- struct FrameRate {
- float rate;
- FrameRateCompatibility type;
-
- FrameRate() : rate(0), type(FrameRateCompatibility::Default) {}
- FrameRate(float rate, FrameRateCompatibility type) : rate(rate), type(type) {}
-
- bool operator==(const FrameRate& other) const {
- return rate == other.rate && type == other.type;
- }
-
- bool operator!=(const FrameRate& other) const { return !(*this == other); }
-
- // Convert an ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* value to a
- // Layer::FrameRateCompatibility. Logs fatal if the compatibility value is invalid.
- static FrameRateCompatibility convertCompatibility(int8_t compatibility);
- };
+ using FrameRate = scheduler::LayerInfo::FrameRate;
+ using FrameRateCompatibility = scheduler::LayerInfo::FrameRateCompatibility;
struct State {
Geometry active_legacy;
@@ -188,19 +157,14 @@
// to achieve mirroring.
uint32_t layerStack;
- uint8_t flags;
+ uint32_t flags;
uint8_t reserved[2];
int32_t sequence; // changes when visible regions can change
bool modified;
// Crop is expressed in layer space coordinate.
- Rect crop_legacy;
- Rect requestedCrop_legacy;
-
- // If set, defers this state update until the identified Layer
- // receives a frame with the given frameNumber
- wp<Layer> barrierLayer_legacy;
- uint64_t frameNumber_legacy;
+ Rect crop;
+ Rect requestedCrop;
// the transparentRegion hint is a bit special, it's latched only
// when we receive a buffer -- this is because it's "content"
@@ -230,17 +194,19 @@
// The fields below this point are only used by BufferStateLayer
uint64_t frameNumber;
- Geometry active;
+ uint32_t width;
+ uint32_t height;
+ ui::Transform transform;
- uint32_t transform;
+ uint32_t bufferTransform;
bool transformToDisplayInverse;
- Rect crop;
Region transparentRegionHint;
- sp<GraphicBuffer> buffer;
+ std::shared_ptr<renderengine::ExternalTexture> buffer;
client_cache_t clientCacheId;
sp<Fence> acquireFence;
+ std::shared_ptr<FenceTime> acquireFenceTime;
HdrMetadata hdrMetadata;
Region surfaceDamageRegion;
int32_t api;
@@ -258,12 +224,16 @@
// recent callback handle.
std::deque<sp<CallbackHandle>> callbackHandles;
bool colorSpaceAgnostic;
- nsecs_t desiredPresentTime = -1;
+ nsecs_t desiredPresentTime = 0;
+ bool isAutoTimestamp = true;
// Length of the cast shadow. If the radius is > 0, a shadow of length shadowRadius will
// be rendered around the layer.
float shadowRadius;
+ // Layer regions that are made of custom materials, like frosted glass
+ std::vector<BlurRegion> blurRegions;
+
// Priority of the layer assigned by Window Manager.
int32_t frameRateSelectionPriority;
@@ -280,19 +250,82 @@
// a buffer of a different size. ui::Transform::ROT_INVALID means the
// a fixed transform hint is not set.
ui::Transform::RotationFlags fixedTransformHint;
+
+ // The vsync info that was used to start the transaction
+ FrameTimelineInfo frameTimelineInfo;
+
+ // When the transaction was posted
+ nsecs_t postTime;
+
+ sp<ITransactionCompletedListener> releaseBufferListener;
+ // SurfaceFrame that tracks the timeline of Transactions that contain a Buffer. Only one
+ // such SurfaceFrame exists because only one buffer can be presented on the layer per vsync.
+ // If multiple buffers are queued, the prior ones will be dropped, along with the
+ // SurfaceFrame that's tracking them.
+ std::shared_ptr<frametimeline::SurfaceFrame> bufferSurfaceFrameTX;
+ // A map of token(frametimelineVsyncId) to the SurfaceFrame that's tracking a transaction
+ // that contains the token. Only one SurfaceFrame exisits for transactions that share the
+ // same token, unless they are presented in different vsyncs.
+ std::unordered_map<int64_t, std::shared_ptr<frametimeline::SurfaceFrame>>
+ bufferlessSurfaceFramesTX;
+ // An arbitrary threshold for the number of BufferlessSurfaceFrames in the state. Used to
+ // trigger a warning if the number of SurfaceFrames crosses the threshold.
+ static constexpr uint32_t kStateSurfaceFramesThreshold = 25;
+
+ // Stretch effect to apply to this layer
+ StretchEffect stretchEffect;
+ };
+
+ /*
+ * Trivial class, used to ensure that mFlinger->onLayerDestroyed(mLayer)
+ * is called.
+ */
+ class LayerCleaner {
+ sp<SurfaceFlinger> mFlinger;
+ sp<Layer> mLayer;
+
+ protected:
+ ~LayerCleaner() {
+ // destroy client resources
+ mFlinger->onHandleDestroyed(mLayer);
+ }
+
+ public:
+ LayerCleaner(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer)
+ : mFlinger(flinger), mLayer(layer) {}
+ };
+
+ /*
+ * The layer handle is just a BBinder object passed to the client
+ * (remote process) -- we don't keep any reference on our side such that
+ * the dtor is called when the remote side let go of its reference.
+ *
+ * LayerCleaner ensures that mFlinger->onLayerDestroyed() is called for
+ * this layer when the handle is destroyed.
+ */
+ class Handle : public BBinder, public LayerCleaner {
+ public:
+ Handle(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer)
+ : LayerCleaner(flinger, layer), owner(layer) {}
+
+ wp<Layer> owner;
};
explicit Layer(const LayerCreationArgs& args);
virtual ~Layer();
- void onFirstRef() override;
+ static bool isLayerFocusedBasedOnPriority(int32_t priority);
+ static void miniDumpHeader(std::string& result);
+ static std::string frameRateCompatibilityString(FrameRateCompatibility compatibility);
- int getWindowType() const { return mWindowType; }
+ // Provide unique string for each class type in the Layer hierarchy
+ virtual const char* getType() const = 0;
- void setPrimaryDisplayOnly() { mPrimaryDisplayOnly = true; }
- bool getPrimaryDisplayOnly() const { return mPrimaryDisplayOnly; }
+ // true if this layer is visible, false otherwise
+ virtual bool isVisible() const = 0;
- // ------------------------------------------------------------------------
+ virtual sp<Layer> createClone() = 0;
+
// Geometry setting functions.
//
// The following group of functions are used to specify the layers
@@ -305,7 +338,6 @@
//
// The first set of geometry functions are controlled by the scaling mode, described
// in window.h. The scaling mode may be set by the client, as it submits buffers.
- // This value may be overriden through SurfaceControl, with setOverrideScalingMode.
//
// Put simply, if our scaling mode is SCALING_MODE_FREEZE, then
// matrix updates will not be applied while a resize is pending
@@ -336,7 +368,7 @@
// space for top-level layers.
virtual bool setPosition(float x, float y);
// Buffer space
- virtual bool setCrop_legacy(const Rect& crop);
+ virtual bool setCrop(const Rect& crop);
// TODO(b/38182121): Could we eliminate the various latching modes by
// using the layer hierarchy?
@@ -356,22 +388,14 @@
// When non-zero, everything below this layer will be blurred by backgroundBlurRadius, which
// is specified in pixels.
virtual bool setBackgroundBlurRadius(int backgroundBlurRadius);
+ virtual bool setBlurRegions(const std::vector<BlurRegion>& effectRegions);
virtual bool setTransparentRegionHint(const Region& transparent);
- virtual bool setFlags(uint8_t flags, uint8_t mask);
+ virtual bool setFlags(uint32_t flags, uint32_t mask);
virtual bool setLayerStack(uint32_t layerStack);
virtual uint32_t getLayerStack() const;
- virtual void deferTransactionUntil_legacy(const sp<IBinder>& barrierHandle,
- uint64_t frameNumber);
- virtual void deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber);
- virtual bool setOverrideScalingMode(int32_t overrideScalingMode);
virtual bool setMetadata(const LayerMetadata& data);
- bool reparentChildren(const sp<IBinder>& newParentHandle);
- void reparentChildren(const sp<Layer>& newParent);
- virtual void setChildrenDrawingParent(const sp<Layer>& layer);
+ virtual void setChildrenDrawingParent(const sp<Layer>&);
virtual bool reparent(const sp<IBinder>& newParentHandle);
- virtual bool detachChildren();
- bool attachChildren();
- bool isLayerDetached() const { return mLayerDetached; }
virtual bool setColorTransform(const mat4& matrix);
virtual mat4 getColorTransform() const;
virtual bool hasColorTransform() const;
@@ -380,11 +404,13 @@
// Used only to set BufferStateLayer state
virtual bool setTransform(uint32_t /*transform*/) { return false; };
virtual bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/) { return false; };
- virtual bool setCrop(const Rect& /*crop*/) { return false; };
- virtual bool setFrame(const Rect& /*frame*/) { return false; };
- virtual bool setBuffer(const sp<GraphicBuffer>& /*buffer*/, const sp<Fence>& /*acquireFence*/,
- nsecs_t /*postTime*/, nsecs_t /*desiredPresentTime*/,
- const client_cache_t& /*clientCacheId*/) {
+ virtual bool setBuffer(const std::shared_ptr<renderengine::ExternalTexture>& /*buffer*/,
+ const sp<Fence>& /*acquireFence*/, nsecs_t /*postTime*/,
+ nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
+ const client_cache_t& /*clientCacheId*/, uint64_t /* frameNumber */,
+ std::optional<nsecs_t> /* dequeueTime */,
+ const FrameTimelineInfo& /*info*/,
+ const sp<ITransactionCompletedListener>& /* releaseBufferListener */) {
return false;
};
virtual bool setAcquireFence(const sp<Fence>& /*fence*/) { return false; };
@@ -397,30 +423,20 @@
const std::vector<sp<CallbackHandle>>& /*handles*/) {
return false;
};
- virtual void forceSendCallbacks() {}
virtual bool addFrameEvent(const sp<Fence>& /*acquireFence*/, nsecs_t /*postedTime*/,
nsecs_t /*requestedPresentTime*/) {
return false;
}
virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace);
virtual bool setColorSpaceAgnostic(const bool agnostic);
- bool setShadowRadius(float shadowRadius);
virtual bool setFrameRateSelectionPriority(int32_t priority);
virtual bool setFixedTransformHint(ui::Transform::RotationFlags fixedTransformHint);
+ virtual void setAutoRefresh(bool /* autoRefresh */) {}
// If the variable is not set on the layer, it traverses up the tree to inherit the frame
// rate priority from its parent.
virtual int32_t getFrameRateSelectionPriority() const;
- static bool isLayerFocusedBasedOnPriority(int32_t priority);
-
virtual ui::Dataspace getDataSpace() const { return ui::Dataspace::UNKNOWN; }
- // Before color management is introduced, contents on Android have to be
- // desaturated in order to match what they appears like visually.
- // With color management, these contents will appear desaturated, thus
- // needed to be saturated so that they match what they are designed for
- // visually.
- bool isLegacyDataSpace() const;
-
virtual sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const;
virtual compositionengine::LayerFECompositionState* editCompositionState();
@@ -429,49 +445,7 @@
// one empty rect.
virtual void useSurfaceDamage() {}
virtual void useEmptyDamage() {}
-
- uint32_t getTransactionFlags() const { return mTransactionFlags; }
- uint32_t getTransactionFlags(uint32_t flags);
- uint32_t setTransactionFlags(uint32_t flags);
-
- // Deprecated, please use compositionengine::Output::belongsInOutput()
- // instead.
- // TODO(lpique): Move the remaining callers (screencap) to the new function.
- bool belongsToDisplay(uint32_t layerStack, bool isPrimaryDisplay) const {
- return getLayerStack() == layerStack && (!mPrimaryDisplayOnly || isPrimaryDisplay);
- }
-
- FloatRect getBounds(const Region& activeTransparentRegion) const;
- FloatRect getBounds() const;
-
- // Compute bounds for the layer and cache the results.
- void computeBounds(FloatRect parentBounds, ui::Transform parentTransform, float shadowRadius);
-
- // Returns the buffer scale transform if a scaling mode is set.
- ui::Transform getBufferScaleTransform() const;
-
- // Get effective layer transform, taking into account all its parent transform with any
- // scaling if the parent scaling more is not NATIVE_WINDOW_SCALING_MODE_FREEZE.
- ui::Transform getTransformWithScale(const ui::Transform& bufferScaleTransform) const;
-
- // Returns the bounds of the layer without any buffer scaling.
- FloatRect getBoundsPreScaling(const ui::Transform& bufferScaleTransform) const;
-
- int32_t getSequence() const { return sequence; }
-
- // For tracing.
- // TODO: Replace with raw buffer id from buffer metadata when that becomes available.
- // GraphicBuffer::getId() does not provide a reliable global identifier. Since the traces
- // creates its tracks by buffer id and has no way of associating a buffer back to the process
- // that created it, the current implementation is only sufficient for cases where a buffer is
- // only used within a single layer.
- uint64_t getCurrentBufferId() const { return getBuffer() ? getBuffer()->getId() : 0; }
-
- // -----------------------------------------------------------------------
- // Virtuals
-
- // Provide unique string for each class type in the Layer hierarchy
- virtual const char* getType() const = 0;
+ Region getVisibleRegion(const DisplayDevice*) const;
/*
* isOpaque - true if this surface is opaque
@@ -483,31 +457,12 @@
virtual bool isOpaque(const Layer::State&) const { return false; }
/*
- * isSecure - true if this surface is secure, that is if it prevents
- * screenshots or VNC servers.
- */
- bool isSecure() const;
-
- /*
- * isVisible - true if this layer is visible, false otherwise
- */
- virtual bool isVisible() const = 0;
-
- /*
- * isHiddenByPolicy - true if this layer has been forced invisible.
- * just because this is false, doesn't mean isVisible() is true.
- * For example if this layer has no active buffer, it may not be hidden by
- * policy, but it still can not be visible.
- */
- bool isHiddenByPolicy() const;
-
- /*
* Returns whether this layer can receive input.
*/
virtual bool canReceiveInput() const;
/*
- * isProtected - true if the layer may contain protected content in the
+ * isProtected - true if the layer may contain protected contents in the
* GRALLOC_USAGE_PROTECTED sense.
*/
virtual bool isProtected() const { return false; }
@@ -527,22 +482,6 @@
// to avoid grabbing the lock again to avoid deadlock
virtual bool isCreatedFromMainThread() const { return false; }
- bool isRemovedFromCurrentState() const;
-
- LayerProto* writeToProto(LayersProto& layersProto, uint32_t traceFlags,
- const DisplayDevice*) const;
-
- // Write states that are modified by the main thread. This includes drawing
- // state as well as buffer data. This should be called in the main or tracing
- // thread.
- void writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags,
- const DisplayDevice*) const;
- // Write drawing or current state. If writing current state, the caller should hold the
- // external mStateLock. If writing drawing state, this function should be called on the
- // main or tracing thread.
- void writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet stateSet,
- uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const;
-
virtual Geometry getActiveGeometry(const Layer::State& s) const { return s.active_legacy; }
virtual uint32_t getActiveWidth(const Layer::State& s) const { return s.active_legacy.w; }
virtual uint32_t getActiveHeight(const Layer::State& s) const { return s.active_legacy.h; }
@@ -552,8 +491,9 @@
virtual Region getActiveTransparentRegion(const Layer::State& s) const {
return s.activeTransparentRegion_legacy;
}
- virtual Rect getCrop(const Layer::State& s) const { return s.crop_legacy; }
+ virtual Rect getCrop(const Layer::State& s) const { return s.crop; }
virtual bool needsFiltering(const DisplayDevice*) const { return false; }
+
// True if this layer requires filtering
// This method is distinct from needsFiltering() in how the filter
// requirement is computed. needsFiltering() compares displayFrame and crop,
@@ -567,65 +507,16 @@
return false;
}
- // This layer is not a clone, but it's the parent to the cloned hierarchy. The
- // variable mClonedChild represents the top layer that will be cloned so this
- // layer will be the parent of mClonedChild.
- // The layers in the cloned hierarchy will match the lifetime of the real layers. That is
- // if the real layer is destroyed, then the clone layer will also be destroyed.
- sp<Layer> mClonedChild;
-
- virtual sp<Layer> createClone() = 0;
- void updateMirrorInfo();
virtual void updateCloneBufferInfo(){};
-protected:
- sp<compositionengine::LayerFE> asLayerFE() const;
- sp<Layer> getClonedFrom() { return mClonedFrom != nullptr ? mClonedFrom.promote() : nullptr; }
- bool isClone() { return mClonedFrom != nullptr; }
- bool isClonedFromAlive() { return getClonedFrom() != nullptr; }
-
- virtual void setInitialValuesForClone(const sp<Layer>& clonedFrom);
-
- void updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
- void updateClonedChildren(const sp<Layer>& mirrorRoot,
- std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
- void updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
- void addChildToDrawing(const sp<Layer>& layer);
- void updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
- virtual std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
- compositionengine::LayerFE::ClientCompositionTargetSettings&);
- virtual std::optional<compositionengine::LayerFE::LayerSettings> prepareShadowClientComposition(
- const LayerFE::LayerSettings& layerSettings, const Rect& displayViewport,
- ui::Dataspace outputDataspace);
- // Modifies the passed in layer settings to clear the contents. If the blackout flag is set,
- // the settings clears the content with a solid black fill.
- void prepareClearClientComposition(LayerFE::LayerSettings& layerSettings, bool blackout) const;
-
-public:
- /*
- * compositionengine::LayerFE overrides
- */
- const compositionengine::LayerFECompositionState* getCompositionState() const override;
- bool onPreComposition(nsecs_t) override;
- void prepareCompositionState(compositionengine::LayerFE::StateSubset subset) override;
- std::vector<compositionengine::LayerFE::LayerSettings> prepareClientCompositionList(
- compositionengine::LayerFE::ClientCompositionTargetSettings&) override;
- void onLayerDisplayed(const sp<Fence>& releaseFence) override;
- const char* getDebugName() const override;
-
-protected:
- void prepareBasicGeometryCompositionState();
- void prepareGeometryCompositionState();
- virtual void preparePerFrameCompositionState();
- void prepareCursorCompositionState();
-
-public:
virtual void setDefaultBufferSize(uint32_t /*w*/, uint32_t /*h*/) {}
virtual bool isHdrY410() const { return false; }
virtual bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const { return false; }
+ virtual uint64_t getHeadFrameNumber(nsecs_t /* expectedPresentTime */) const { return 0; }
+
/*
* called after composition.
* returns true if the layer latched a new buffer this frame.
@@ -642,11 +533,6 @@
virtual void finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& /*glDoneFence*/,
const CompositorTiming& /*compositorTiming*/) {}
- /*
- * doTransaction - process the transaction. This is a good place to figure
- * out which attributes of the surface have changed.
- */
- uint32_t doTransaction(uint32_t transactionFlags);
/*
* latchBuffer - called each time the screen is redrawn and returns whether
@@ -664,6 +550,171 @@
virtual void latchAndReleaseBuffer() {}
/*
+ * returns the rectangle that crops the content of the layer and scales it
+ * to the layer's size.
+ */
+ virtual Rect getBufferCrop() const { return Rect(); }
+
+ /*
+ * Returns the transform applied to the buffer.
+ */
+ virtual uint32_t getBufferTransform() const { return 0; }
+
+ virtual sp<GraphicBuffer> getBuffer() const { return nullptr; }
+
+ virtual ui::Transform::RotationFlags getTransformHint() const { return ui::Transform::ROT_0; }
+
+ /*
+ * Returns if a frame is ready
+ */
+ virtual bool hasReadyFrame() const { return false; }
+
+ virtual int32_t getQueuedFrameCount() const { return 0; }
+
+ /**
+ * Returns active buffer size in the correct orientation. Buffer size is determined by undoing
+ * any buffer transformations. If the layer has no buffer then return INVALID_RECT.
+ */
+ virtual Rect getBufferSize(const Layer::State&) const { return Rect::INVALID_RECT; }
+
+ /**
+ * Returns the source bounds. If the bounds are not defined, it is inferred from the
+ * buffer size. Failing that, the bounds are determined from the passed in parent bounds.
+ * For the root layer, this is the display viewport size.
+ */
+ virtual FloatRect computeSourceBounds(const FloatRect& parentBounds) const {
+ return parentBounds;
+ }
+ virtual FrameRate getFrameRateForLayerTree() const;
+
+ virtual std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool /*forceFlush*/) {
+ return {};
+ }
+
+ virtual bool getTransformToDisplayInverse() const { return false; }
+
+ // Returns how rounded corners should be drawn for this layer.
+ // This will traverse the hierarchy until it reaches its root, finding topmost rounded
+ // corner definition and converting it into current layer's coordinates.
+ // As of now, only 1 corner radius per display list is supported. Subsequent ones will be
+ // ignored.
+ virtual RoundedCornerState getRoundedCornerState() const;
+
+ bool hasRoundedCorners() const override { return getRoundedCornerState().radius > .0f; }
+
+ virtual PixelFormat getPixelFormat() const { return PIXEL_FORMAT_NONE; }
+ /**
+ * Return whether this layer needs an input info. For most layer types
+ * this is only true if they explicitly set an input-info but BufferLayer
+ * overrides this so we can generate input-info for Buffered layers that don't
+ * have them (for input occlusion detection checks).
+ */
+ virtual bool needsInputInfo() const { return hasInputInfo(); }
+
+ // Implements RefBase.
+ void onFirstRef() override;
+
+ // implements compositionengine::LayerFE
+ const compositionengine::LayerFECompositionState* getCompositionState() const override;
+ bool onPreComposition(nsecs_t) override;
+ void prepareCompositionState(compositionengine::LayerFE::StateSubset subset) override;
+ std::vector<compositionengine::LayerFE::LayerSettings> prepareClientCompositionList(
+ compositionengine::LayerFE::ClientCompositionTargetSettings&) override;
+ void onLayerDisplayed(const sp<Fence>& releaseFence) override;
+ const char* getDebugName() const override;
+
+ bool setShadowRadius(float shadowRadius);
+
+ // Before color management is introduced, contents on Android have to be
+ // desaturated in order to match what they appears like visually.
+ // With color management, these contents will appear desaturated, thus
+ // needed to be saturated so that they match what they are designed for
+ // visually.
+ bool isLegacyDataSpace() const;
+
+ uint32_t getTransactionFlags() const { return mTransactionFlags; }
+ uint32_t getTransactionFlags(uint32_t flags);
+ uint32_t setTransactionFlags(uint32_t flags);
+
+ // Deprecated, please use compositionengine::Output::belongsInOutput()
+ // instead.
+ // TODO(lpique): Move the remaining callers (screencap) to the new function.
+ bool belongsToDisplay(uint32_t layerStack) const { return getLayerStack() == layerStack; }
+
+ FloatRect getBounds(const Region& activeTransparentRegion) const;
+ FloatRect getBounds() const;
+
+ // Compute bounds for the layer and cache the results.
+ void computeBounds(FloatRect parentBounds, ui::Transform parentTransform, float shadowRadius);
+
+ // Returns the buffer scale transform if a scaling mode is set.
+ ui::Transform getBufferScaleTransform() const;
+
+ // Get effective layer transform, taking into account all its parent transform with any
+ // scaling if the parent scaling more is not NATIVE_WINDOW_SCALING_MODE_FREEZE.
+ ui::Transform getTransformWithScale(const ui::Transform& bufferScaleTransform) const;
+
+ // Returns the bounds of the layer without any buffer scaling.
+ FloatRect getBoundsPreScaling(const ui::Transform& bufferScaleTransform) const;
+
+ int32_t getSequence() const override { return sequence; }
+
+ // For tracing.
+ // TODO: Replace with raw buffer id from buffer metadata when that becomes available.
+ // GraphicBuffer::getId() does not provide a reliable global identifier. Since the traces
+ // creates its tracks by buffer id and has no way of associating a buffer back to the process
+ // that created it, the current implementation is only sufficient for cases where a buffer is
+ // only used within a single layer.
+ uint64_t getCurrentBufferId() const { return getBuffer() ? getBuffer()->getId() : 0; }
+
+ /*
+ * isSecure - true if this surface is secure, that is if it prevents
+ * screenshots or VNC servers. A surface can be set to be secure by the
+ * application, being secure doesn't mean the surface has DRM contents.
+ */
+ bool isSecure() const;
+
+ /*
+ * isHiddenByPolicy - true if this layer has been forced invisible.
+ * just because this is false, doesn't mean isVisible() is true.
+ * For example if this layer has no active buffer, it may not be hidden by
+ * policy, but it still can not be visible.
+ */
+ bool isHiddenByPolicy() const;
+
+ bool isRemovedFromCurrentState() const;
+
+ LayerProto* writeToProto(LayersProto& layersProto, uint32_t traceFlags, const DisplayDevice*);
+
+ // Write states that are modified by the main thread. This includes drawing
+ // state as well as buffer data. This should be called in the main or tracing
+ // thread.
+ void writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags, const DisplayDevice*);
+ // Write drawing or current state. If writing current state, the caller should hold the
+ // external mStateLock. If writing drawing state, this function should be called on the
+ // main or tracing thread.
+ void writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet,
+ uint32_t traceFlags = SurfaceTracing::TRACE_ALL);
+
+ InputWindowInfo::Type getWindowType() const { return mWindowType; }
+
+ bool getPrimaryDisplayOnly() const;
+
+ void updateMirrorInfo();
+
+ /*
+ * doTransaction - process the transaction. This is a good place to figure
+ * out which attributes of the surface have changed.
+ */
+ virtual uint32_t doTransaction(uint32_t transactionFlags);
+
+ /*
+ * Called before updating the drawing state buffer. Used by BufferStateLayer to release any
+ * unlatched buffers in the drawing state.
+ */
+ virtual void bufferMayChange(const sp<GraphicBuffer>& /* newBuffer */){};
+
+ /*
* Remove relative z for the layer if its relative parent is not part of the
* provided layer tree.
*/
@@ -690,36 +741,12 @@
*/
void updateTransformHint(ui::Transform::RotationFlags);
- /*
- * returns the rectangle that crops the content of the layer and scales it
- * to the layer's size.
- */
- virtual Rect getBufferCrop() const { return Rect(); }
-
- /*
- * Returns the transform applied to the buffer.
- */
- virtual uint32_t getBufferTransform() const { return 0; }
-
- virtual sp<GraphicBuffer> getBuffer() const { return nullptr; }
-
- virtual ui::Transform::RotationFlags getTransformHint() const { return ui::Transform::ROT_0; }
-
- /*
- * Returns if a frame is ready
- */
- virtual bool hasReadyFrame() const { return false; }
-
- virtual int32_t getQueuedFrameCount() const { return 0; }
-
- // -----------------------------------------------------------------------
inline const State& getDrawingState() const { return mDrawingState; }
inline const State& getCurrentState() const { return mCurrentState; }
inline State& getCurrentState() { return mCurrentState; }
LayerDebugInfo getLayerDebugInfo(const DisplayDevice*) const;
- static void miniDumpHeader(std::string& result);
void miniDump(std::string& result, const DisplayDevice&) const;
void dumpFrameStats(std::string& result) const;
void dumpFrameEvents(std::string& result);
@@ -727,17 +754,10 @@
void clearFrameStats();
void logFrameStats();
void getFrameStats(FrameStats* outStats) const;
-
- virtual std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool /*forceFlush*/) {
- return {};
- }
-
void onDisconnect();
void addAndGetFrameTimestamps(const NewFrameEventsEntry* newEntry,
FrameEventHistoryDelta* outDelta);
- virtual bool getTransformToDisplayInverse() const { return false; }
-
ui::Transform getTransform() const;
// Returns the Alpha of the Surface, accounting for the Alpha
@@ -754,14 +774,7 @@
// is ready to acquire a buffer.
ui::Transform::RotationFlags getFixedTransformHint() const;
- // Returns how rounded corners should be drawn for this layer.
- // This will traverse the hierarchy until it reaches its root, finding topmost rounded
- // corner definition and converting it into current layer's coordinates.
- // As of now, only 1 corner radius per display list is supported. Subsequent ones will be
- // ignored.
- virtual RoundedCornerState getRoundedCornerState() const;
-
- renderengine::ShadowSettings getShadowSettings(const Rect& viewport) const;
+ renderengine::ShadowSettings getShadowSettings(const Rect& layerStackRect) const;
/**
* Traverse this layer and it's hierarchy of children directly. Unlike traverseInZOrder
@@ -771,17 +784,15 @@
* the scene state, but it's also more efficient than traverseInZOrder and so useful for
* book-keeping.
*/
- void traverse(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor);
- void traverseInReverseZOrder(LayerVector::StateSet stateSet,
- const LayerVector::Visitor& visitor);
- void traverseInZOrder(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor);
+ void traverse(LayerVector::StateSet, const LayerVector::Visitor&);
+ void traverseInReverseZOrder(LayerVector::StateSet, const LayerVector::Visitor&);
+ void traverseInZOrder(LayerVector::StateSet, const LayerVector::Visitor&);
/**
* Traverse only children in z order, ignoring relative layers that are not children of the
* parent.
*/
- void traverseChildrenInZOrder(LayerVector::StateSet stateSet,
- const LayerVector::Visitor& visitor);
+ void traverseChildrenInZOrder(LayerVector::StateSet, const LayerVector::Visitor&);
size_t getChildrenCount() const;
@@ -793,7 +804,7 @@
// the current state, but should not be called anywhere else!
LayerVector& getCurrentChildren() { return mCurrentChildren; }
- void addChild(const sp<Layer>& layer);
+ void addChild(const sp<Layer>&);
// Returns index if removed, or negative value otherwise
// for symmetry with Vector::remove
ssize_t removeChild(const sp<Layer>& layer);
@@ -807,23 +818,7 @@
// Copy the current list of children to the drawing state. Called by
// SurfaceFlinger to complete a transaction.
void commitChildList();
- int32_t getZ(LayerVector::StateSet stateSet) const;
- virtual void pushPendingState();
-
- /**
- * Returns active buffer size in the correct orientation. Buffer size is determined by undoing
- * any buffer transformations. If the layer has no buffer then return INVALID_RECT.
- */
- virtual Rect getBufferSize(const Layer::State&) const { return Rect::INVALID_RECT; }
-
- /**
- * Returns the source bounds. If the bounds are not defined, it is inferred from the
- * buffer size. Failing that, the bounds are determined from the passed in parent bounds.
- * For the root layer, this is the display viewport size.
- */
- virtual FloatRect computeSourceBounds(const FloatRect& parentBounds) const {
- return parentBounds;
- }
+ int32_t getZ(LayerVector::StateSet) const;
/**
* Returns the cropped buffer size or the layer crop if the layer has no buffer. Return
@@ -833,40 +828,112 @@
*/
Rect getCroppedBufferSize(const Layer::State& s) const;
- bool setFrameRate(FrameRate frameRate);
- virtual FrameRate getFrameRateForLayerTree() const;
- static std::string frameRateCompatibilityString(FrameRateCompatibility compatibility);
+ bool setFrameRate(FrameRate);
+
+ virtual void setFrameTimelineInfoForBuffer(const FrameTimelineInfo& /*info*/) {}
+ void setFrameTimelineVsyncForBufferTransaction(const FrameTimelineInfo& info, nsecs_t postTime);
+ void setFrameTimelineVsyncForBufferlessTransaction(const FrameTimelineInfo& info,
+ nsecs_t postTime);
+
+ void addSurfaceFrameDroppedForBuffer(
+ std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame);
+ void addSurfaceFramePresentedForBuffer(
+ std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame, nsecs_t acquireFenceTime,
+ nsecs_t currentLatchTime);
+
+ std::shared_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForTransaction(
+ const FrameTimelineInfo& info, nsecs_t postTime);
+ std::shared_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForBuffer(
+ const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName);
+
+ // Creates a new handle each time, so we only expect
+ // this to be called once.
+ sp<IBinder> getHandle();
+ const std::string& getName() const { return mName; }
+ bool getPremultipledAlpha() const;
+ void setInputInfo(const InputWindowInfo& info);
+
+ InputWindowInfo fillInputInfo(const sp<DisplayDevice>& display);
+ /**
+ * Returns whether this layer has an explicitly set input-info.
+ */
+ bool hasInputInfo() const;
+
+ virtual uid_t getOwnerUid() const { return mOwnerUid; }
+
+ pid_t getOwnerPid() { return mOwnerPid; }
+
+ // This layer is not a clone, but it's the parent to the cloned hierarchy. The
+ // variable mClonedChild represents the top layer that will be cloned so this
+ // layer will be the parent of mClonedChild.
+ // The layers in the cloned hierarchy will match the lifetime of the real layers. That is
+ // if the real layer is destroyed, then the clone layer will also be destroyed.
+ sp<Layer> mClonedChild;
+
+ mutable bool contentDirty{false};
+ Region surfaceDamageRegion;
+
+ // Layer serial number. This gives layers an explicit ordering, so we
+ // have a stable sort order when their layer stack and Z-order are
+ // the same.
+ int32_t sequence{sSequence++};
+
+ bool mPendingHWCDestroy{false};
+
+ bool backpressureEnabled() { return mDrawingState.flags & layer_state_t::eEnableBackpressure; }
+
+ bool setStretchEffect(const StretchEffect& effect);
+ StretchEffect getStretchEffect() const;
+
+ virtual std::atomic<int32_t>* getPendingBufferCounter() { return nullptr; }
+ virtual std::string getPendingBufferCounterName() { return ""; }
protected:
- // constant
- sp<SurfaceFlinger> mFlinger;
- /*
- * Trivial class, used to ensure that mFlinger->onLayerDestroyed(mLayer)
- * is called.
- */
- class LayerCleaner {
- sp<SurfaceFlinger> mFlinger;
- sp<Layer> mLayer;
-
- protected:
- ~LayerCleaner() {
- // destroy client resources
- mFlinger->onHandleDestroyed(mLayer);
- }
-
- public:
- LayerCleaner(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer)
- : mFlinger(flinger), mLayer(layer) {}
- };
-
friend class impl::SurfaceInterceptor;
// For unit tests
friend class TestableSurfaceFlinger;
+ friend class FpsReporterTest;
friend class RefreshRateSelectionTest;
friend class SetFrameRateTest;
+ friend class TransactionFrameTracerTest;
+ friend class TransactionSurfaceFrameTest;
- virtual void commitTransaction(const State& stateToCommit);
+ virtual void setInitialValuesForClone(const sp<Layer>& clonedFrom);
+ virtual std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
+ compositionengine::LayerFE::ClientCompositionTargetSettings&);
+ virtual std::optional<compositionengine::LayerFE::LayerSettings> prepareShadowClientComposition(
+ const LayerFE::LayerSettings&, const Rect& layerStackRect,
+ ui::Dataspace outputDataspace);
+ virtual void preparePerFrameCompositionState();
+ virtual void commitTransaction(State& stateToCommit);
+ virtual uint32_t doTransactionResize(uint32_t flags, Layer::State* stateToCommit);
+ virtual void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>&) {}
+
+ // Returns mCurrentScaling mode (originating from the
+ // Client) or mOverrideScalingMode mode (originating from
+ // the Surface Controller) if set.
+ virtual uint32_t getEffectiveScalingMode() const { return 0; }
+
+ sp<compositionengine::LayerFE> asLayerFE() const;
+ sp<Layer> getClonedFrom() { return mClonedFrom != nullptr ? mClonedFrom.promote() : nullptr; }
+ bool isClone() { return mClonedFrom != nullptr; }
+ bool isClonedFromAlive() { return getClonedFrom() != nullptr; }
+
+ void updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
+ void updateClonedChildren(const sp<Layer>& mirrorRoot,
+ std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
+ void updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
+ void addChildToDrawing(const sp<Layer>&);
+ void updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
+
+ // Modifies the passed in layer settings to clear the contents. If the blackout flag is set,
+ // the settings clears the content with a solid black fill.
+ void prepareClearClientComposition(LayerFE::LayerSettings&, bool blackout) const;
+
+ void prepareBasicGeometryCompositionState();
+ void prepareGeometryCompositionState();
+ void prepareCursorCompositionState();
uint32_t getEffectiveUsage(uint32_t usage) const;
@@ -875,154 +942,29 @@
* crop coordinates, transforming them into layer space.
*/
void setupRoundedCornersCropCoordinates(Rect win, const FloatRect& roundedCornersCrop) const;
- void setParent(const sp<Layer>& layer);
- LayerVector makeTraversalList(LayerVector::StateSet stateSet, bool* outSkipRelativeZUsers);
+ void setParent(const sp<Layer>&);
+ LayerVector makeTraversalList(LayerVector::StateSet, bool* outSkipRelativeZUsers);
void addZOrderRelative(const wp<Layer>& relative);
void removeZOrderRelative(const wp<Layer>& relative);
-
- class SyncPoint {
- public:
- explicit SyncPoint(uint64_t frameNumber,
- wp<Layer> requestedSyncLayer,
- wp<Layer> barrierLayer_legacy)
- : mFrameNumber(frameNumber),
- mFrameIsAvailable(false),
- mTransactionIsApplied(false),
- mRequestedSyncLayer(requestedSyncLayer),
- mBarrierLayer_legacy(barrierLayer_legacy) {}
- uint64_t getFrameNumber() const { return mFrameNumber; }
-
- bool frameIsAvailable() const { return mFrameIsAvailable; }
-
- void setFrameAvailable() { mFrameIsAvailable = true; }
-
- bool transactionIsApplied() const { return mTransactionIsApplied; }
-
- void setTransactionApplied() { mTransactionIsApplied = true; }
-
- sp<Layer> getRequestedSyncLayer() { return mRequestedSyncLayer.promote(); }
-
- sp<Layer> getBarrierLayer() const { return mBarrierLayer_legacy.promote(); }
-
- bool isTimeout() const {
- using namespace std::chrono_literals;
- static constexpr std::chrono::nanoseconds TIMEOUT_THRESHOLD = 1s;
-
- return std::chrono::steady_clock::now() - mCreateTimeStamp > TIMEOUT_THRESHOLD;
- }
-
- void checkTimeoutAndLog() {
- using namespace std::chrono_literals;
- static constexpr std::chrono::nanoseconds LOG_PERIOD = 1s;
-
- if (!frameIsAvailable() && isTimeout()) {
- const auto now = std::chrono::steady_clock::now();
- if (now - mLastLogTime > LOG_PERIOD) {
- mLastLogTime = now;
- sp<Layer> requestedSyncLayer = getRequestedSyncLayer();
- sp<Layer> barrierLayer = getBarrierLayer();
- ALOGW("[%s] sync point %" PRIu64 " wait timeout %lld for %s",
- requestedSyncLayer ? requestedSyncLayer->getDebugName() : "Removed",
- mFrameNumber, (now - mCreateTimeStamp).count(),
- barrierLayer ? barrierLayer->getDebugName() : "Removed");
- }
- }
- }
- private:
- const uint64_t mFrameNumber;
- std::atomic<bool> mFrameIsAvailable;
- std::atomic<bool> mTransactionIsApplied;
- wp<Layer> mRequestedSyncLayer;
- wp<Layer> mBarrierLayer_legacy;
- const std::chrono::time_point<std::chrono::steady_clock> mCreateTimeStamp =
- std::chrono::steady_clock::now();
- std::chrono::time_point<std::chrono::steady_clock> mLastLogTime;
- };
-
- // SyncPoints which will be signaled when the correct frame is at the head
- // of the queue and dropped after the frame has been latched. Protected by
- // mLocalSyncPointMutex.
- Mutex mLocalSyncPointMutex;
- std::list<std::shared_ptr<SyncPoint>> mLocalSyncPoints;
-
- // SyncPoints which will be signaled and then dropped when the transaction
- // is applied
- std::list<std::shared_ptr<SyncPoint>> mRemoteSyncPoints;
-
- // Returns false if the relevant frame has already been latched
- bool addSyncPoint(const std::shared_ptr<SyncPoint>& point);
-
- void popPendingState(State* stateToCommit);
- virtual bool applyPendingStates(State* stateToCommit);
- virtual uint32_t doTransactionResize(uint32_t flags, Layer::State* stateToCommit);
-
- // Returns mCurrentScaling mode (originating from the
- // Client) or mOverrideScalingMode mode (originating from
- // the Surface Controller) if set.
- virtual uint32_t getEffectiveScalingMode() const { return 0; }
-
-public:
- /*
- * The layer handle is just a BBinder object passed to the client
- * (remote process) -- we don't keep any reference on our side such that
- * the dtor is called when the remote side let go of its reference.
- *
- * LayerCleaner ensures that mFlinger->onLayerDestroyed() is called for
- * this layer when the handle is destroyed.
- */
- class Handle : public BBinder, public LayerCleaner {
- public:
- Handle(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer)
- : LayerCleaner(flinger, layer), owner(layer) {}
-
- wp<Layer> owner;
- };
-
- // Creates a new handle each time, so we only expect
- // this to be called once.
- sp<IBinder> getHandle();
- const std::string& getName() const { return mName; }
- virtual void notifyAvailableFrames(nsecs_t /*expectedPresentTime*/) {}
- virtual PixelFormat getPixelFormat() const { return PIXEL_FORMAT_NONE; }
- bool getPremultipledAlpha() const;
-
- bool mPendingHWCDestroy{false};
- void setInputInfo(const InputWindowInfo& info);
-
- InputWindowInfo fillInputInfo();
- /**
- * Returns whether this layer has an explicitly set input-info.
- */
- bool hasInputInfo() const;
- /**
- * Return whether this layer needs an input info. For most layer types
- * this is only true if they explicitly set an input-info but BufferLayer
- * overrides this so we can generate input-info for Buffered layers that don't
- * have them (for input occlusion detection checks).
- */
- virtual bool needsInputInfo() const { return hasInputInfo(); }
-
-protected:
compositionengine::OutputLayer* findOutputLayerForDisplay(const DisplayDevice*) const;
+ bool usingRelativeZ(LayerVector::StateSet) const;
- bool usingRelativeZ(LayerVector::StateSet stateSet) const;
+ virtual ui::Transform getInputTransform() const;
+ virtual Rect getInputBounds() const;
+
+ // constant
+ sp<SurfaceFlinger> mFlinger;
bool mPremultipliedAlpha{true};
const std::string mName;
const std::string mTransactionName{"TX - " + mName};
- bool mPrimaryDisplayOnly = false;
-
// These are only accessed by the main thread or the tracing thread.
State mDrawingState;
- // Store a copy of the pending state so that the drawing thread can access the
- // states without a lock.
- std::deque<State> mPendingStatesSnapshot;
// these are protected by an external lock (mStateLock)
State mCurrentState;
std::atomic<uint32_t> mTransactionFlags{0};
- std::deque<State> mPendingStates;
// Timestamp history for UIAutomation. Thread safe.
FrameTracker mFrameTracker;
@@ -1041,7 +983,6 @@
bool mIsActiveBufferUpdatedForGpu = true;
// We encode unset as -1.
- int32_t mOverrideScalingMode{-1};
std::atomic<uint64_t> mCurrentFrameNumber{0};
// Whether filtering is needed b/c of the drawingstate
bool mNeedsFiltering{false};
@@ -1068,38 +1009,64 @@
wp<Layer> mDrawingParent;
// Can only be accessed with the SF state lock held.
- bool mLayerDetached{false};
- // Can only be accessed with the SF state lock held.
bool mChildrenChanged{false};
// Window types from WindowManager.LayoutParams
- const int mWindowType;
+ const InputWindowInfo::Type mWindowType;
+
+ // The owner of the layer. If created from a non system process, it will be the calling uid.
+ // If created from a system process, the value can be passed in.
+ uid_t mOwnerUid;
+
+ // The owner pid of the layer. If created from a non system process, it will be the calling pid.
+ // If created from a system process, the value can be passed in.
+ pid_t mOwnerPid;
+
+ // Keeps track of the time SF latched the last buffer from this layer.
+ // Used in buffer stuffing analysis in FrameTimeline.
+ nsecs_t mLastLatchTime = 0;
+
+ mutable bool mCurrentStateModified = false;
private:
virtual void setTransformHint(ui::Transform::RotationFlags) {}
+ // Returns true if the layer can draw shadows on its border.
+ virtual bool canDrawShadows() const { return true; }
+
Hwc2::IComposerClient::Composition getCompositionType(const DisplayDevice&) const;
- Region getVisibleRegion(const DisplayDevice*) const;
/**
* Returns an unsorted vector of all layers that are part of this tree.
* That includes the current layer and all its descendants.
*/
- std::vector<Layer*> getLayersInTree(LayerVector::StateSet stateSet);
+ std::vector<Layer*> getLayersInTree(LayerVector::StateSet);
/**
* Traverses layers that are part of this tree in the correct z order.
* layersInTree must be sorted before calling this method.
*/
void traverseChildrenInZOrderInner(const std::vector<Layer*>& layersInTree,
- LayerVector::StateSet stateSet,
- const LayerVector::Visitor& visitor);
- LayerVector makeChildrenTraversalList(LayerVector::StateSet stateSet,
+ LayerVector::StateSet, const LayerVector::Visitor&);
+ LayerVector makeChildrenTraversalList(LayerVector::StateSet,
const std::vector<Layer*>& layersInTree);
void updateTreeHasFrameRateVote();
+ void setZOrderRelativeOf(const wp<Layer>& relativeOf);
+
+ // Find the root of the cloned hierarchy, this means the first non cloned parent.
+ // This will return null if first non cloned parent is not found.
+ sp<Layer> getClonedRoot();
+
+ // Finds the top most layer in the hierarchy. This will find the root Layer where the parent is
+ // null.
+ sp<Layer> getRootLayer();
+
+ // Fills in the frame and transform info for the InputWindowInfo
+ void fillInputFrameInfo(InputWindowInfo& info, const ui::Transform& toPhysicalDisplay);
// Cached properties computed from drawing state
- // Effective transform taking into account parent transforms and any parent scaling.
+ // Effective transform taking into account parent transforms and any parent scaling, which is
+ // a transform from the current layer coordinate space to display(screen) coordinate space.
ui::Transform mEffectiveTransform;
// Bounds of the layer before any transformation is applied and before it has been cropped
@@ -1113,12 +1080,8 @@
// Layer bounds in screen space.
FloatRect mScreenBounds;
- void setZOrderRelativeOf(const wp<Layer>& relativeOf);
-
bool mGetHandleCalled = false;
- void removeRemoteSyncPoints();
-
// Tracks the process and user id of the caller when creating this layer
// to help debugging.
pid_t mCallingPid;
@@ -1135,16 +1098,10 @@
// shadow radius is the set shadow radius, otherwise its the parent's shadow radius.
float mEffectiveShadowRadius = 0.f;
- // Returns true if the layer can draw shadows on its border.
- virtual bool canDrawShadows() const { return true; }
-
- // Find the root of the cloned hierarchy, this means the first non cloned parent.
- // This will return null if first non cloned parent is not found.
- sp<Layer> getClonedRoot();
-
- // Finds the top most layer in the hierarchy. This will find the root Layer where the parent is
- // null.
- sp<Layer> getRootLayer();
+ // A list of regions on this layer that should have blurs.
+ const std::vector<BlurRegion> getBlurRegions() const;
};
+std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate);
+
} // namespace android
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 0fe1421..b1db6d3 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -17,6 +17,7 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
#include "LayerProtoHelper.h"
@@ -131,8 +132,12 @@
}
InputWindowInfoProto* proto = getInputWindowInfoProto();
- proto->set_layout_params_flags(inputInfo.layoutParamsFlags);
- proto->set_layout_params_type(inputInfo.layoutParamsType);
+ proto->set_layout_params_flags(inputInfo.flags.get());
+ using U = std::underlying_type_t<InputWindowInfo::Type>;
+ // TODO(b/129481165): This static assert can be safely removed once conversion warnings
+ // are re-enabled.
+ static_assert(std::is_same_v<U, int32_t>);
+ proto->set_layout_params_type(static_cast<U>(inputInfo.type));
LayerProtoHelper::writeToProto({inputInfo.frameLeft, inputInfo.frameTop, inputInfo.frameRight,
inputInfo.frameBottom},
@@ -142,13 +147,11 @@
proto->set_surface_inset(inputInfo.surfaceInset);
proto->set_visible(inputInfo.visible);
- proto->set_can_receive_keys(inputInfo.canReceiveKeys);
- proto->set_has_focus(inputInfo.hasFocus);
+ proto->set_focusable(inputInfo.focusable);
proto->set_has_wallpaper(inputInfo.hasWallpaper);
proto->set_global_scale_factor(inputInfo.globalScaleFactor);
- proto->set_window_x_scale(inputInfo.windowXScale);
- proto->set_window_y_scale(inputInfo.windowYScale);
+ LayerProtoHelper::writeToProto(inputInfo.transform, proto->mutable_transform());
proto->set_replace_touchable_region_with_crop(inputInfo.replaceTouchableRegionWithCrop);
auto cropLayer = touchableRegionBounds.promote();
if (cropLayer != nullptr) {
@@ -171,4 +174,4 @@
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/LayerRejecter.cpp b/services/surfaceflinger/LayerRejecter.cpp
index e6c8654..1c0263b 100644
--- a/services/surfaceflinger/LayerRejecter.cpp
+++ b/services/surfaceflinger/LayerRejecter.cpp
@@ -29,13 +29,12 @@
LayerRejecter::LayerRejecter(Layer::State& front, Layer::State& current,
bool& recomputeVisibleRegions, bool stickySet, const std::string& name,
- int32_t overrideScalingMode, bool transformToDisplayInverse)
+ bool transformToDisplayInverse)
: mFront(front),
mCurrent(current),
mRecomputeVisibleRegions(recomputeVisibleRegions),
mStickyTransformSet(stickySet),
mName(name),
- mOverrideScalingMode(overrideScalingMode),
mTransformToDisplayInverse(transformToDisplayInverse) {}
bool LayerRejecter::reject(const sp<GraphicBuffer>& buf, const BufferItem& item) {
@@ -59,7 +58,7 @@
}
}
- int actualScalingMode = mOverrideScalingMode >= 0 ? mOverrideScalingMode : item.mScalingMode;
+ int actualScalingMode = item.mScalingMode;
bool isFixedSize = actualScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE;
if (mFront.active_legacy != mFront.requested_legacy) {
if (isFixedSize ||
@@ -81,24 +80,23 @@
// recompute visible region
mRecomputeVisibleRegions = true;
- if (mFront.crop_legacy != mFront.requestedCrop_legacy) {
- mFront.crop_legacy = mFront.requestedCrop_legacy;
- mCurrent.crop_legacy = mFront.requestedCrop_legacy;
+ if (mFront.crop != mFront.requestedCrop) {
+ mFront.crop = mFront.requestedCrop;
+ mCurrent.crop = mFront.requestedCrop;
mRecomputeVisibleRegions = true;
}
}
ALOGD_IF(DEBUG_RESIZE,
"[%s] latchBuffer/reject: buffer (%ux%u, tr=%02x), scalingMode=%d\n"
- " drawing={ active_legacy ={ wh={%4u,%4u} crop_legacy={%4d,%4d,%4d,%4d} "
+ " drawing={ active_legacy ={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} "
"(%4d,%4d) "
"}\n"
" requested_legacy={ wh={%4u,%4u} }}\n",
mName.c_str(), bufWidth, bufHeight, item.mTransform, item.mScalingMode,
- mFront.active_legacy.w, mFront.active_legacy.h, mFront.crop_legacy.left,
- mFront.crop_legacy.top, mFront.crop_legacy.right, mFront.crop_legacy.bottom,
- mFront.crop_legacy.getWidth(), mFront.crop_legacy.getHeight(),
- mFront.requested_legacy.w, mFront.requested_legacy.h);
+ mFront.active_legacy.w, mFront.active_legacy.h, mFront.crop.left, mFront.crop.top,
+ mFront.crop.right, mFront.crop.bottom, mFront.crop.getWidth(),
+ mFront.crop.getHeight(), mFront.requested_legacy.w, mFront.requested_legacy.h);
}
if (!isFixedSize && !mStickyTransformSet) {
diff --git a/services/surfaceflinger/LayerRejecter.h b/services/surfaceflinger/LayerRejecter.h
index fb5c750..4981f45 100644
--- a/services/surfaceflinger/LayerRejecter.h
+++ b/services/surfaceflinger/LayerRejecter.h
@@ -24,7 +24,7 @@
class LayerRejecter : public BufferLayerConsumer::BufferRejecter {
public:
LayerRejecter(Layer::State& front, Layer::State& current, bool& recomputeVisibleRegions,
- bool stickySet, const std::string& name, int32_t overrideScalingMode,
+ bool stickySet, const std::string& name,
bool transformToDisplayInverse);
virtual bool reject(const sp<GraphicBuffer>&, const BufferItem&);
@@ -35,7 +35,6 @@
bool& mRecomputeVisibleRegions;
const bool mStickyTransformSet;
const std::string& mName;
- const int32_t mOverrideScalingMode;
const bool mTransformToDisplayInverse;
};
diff --git a/services/surfaceflinger/LayerRenderArea.cpp b/services/surfaceflinger/LayerRenderArea.cpp
new file mode 100644
index 0000000..e84508f
--- /dev/null
+++ b/services/surfaceflinger/LayerRenderArea.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2020 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 <ui/GraphicTypes.h>
+#include <ui/Transform.h>
+
+#include "ContainerLayer.h"
+#include "DisplayDevice.h"
+#include "Layer.h"
+#include "LayerRenderArea.h"
+#include "SurfaceFlinger.h"
+
+namespace android {
+namespace {
+
+struct ReparentForDrawing {
+ const sp<Layer>& oldParent;
+
+ ReparentForDrawing(const sp<Layer>& oldParent, const sp<Layer>& newParent,
+ const Rect& drawingBounds)
+ : oldParent(oldParent) {
+ // Compute and cache the bounds for the new parent layer.
+ newParent->computeBounds(drawingBounds.toFloatRect(), ui::Transform(),
+ 0.f /* shadowRadius */);
+ oldParent->setChildrenDrawingParent(newParent);
+ }
+ ~ReparentForDrawing() { oldParent->setChildrenDrawingParent(oldParent); }
+};
+
+} // namespace
+
+LayerRenderArea::LayerRenderArea(SurfaceFlinger& flinger, sp<Layer> layer, const Rect& crop,
+ ui::Size reqSize, ui::Dataspace reqDataSpace, bool childrenOnly,
+ const Rect& layerStackRect, bool allowSecureLayers)
+ : RenderArea(reqSize, CaptureFill::CLEAR, reqDataSpace, layerStackRect, allowSecureLayers),
+ mLayer(std::move(layer)),
+ mCrop(crop),
+ mFlinger(flinger),
+ mChildrenOnly(childrenOnly) {}
+
+const ui::Transform& LayerRenderArea::getTransform() const {
+ return mTransform;
+}
+
+Rect LayerRenderArea::getBounds() const {
+ return mLayer->getBufferSize(mLayer->getDrawingState());
+}
+
+int LayerRenderArea::getHeight() const {
+ return mLayer->getBufferSize(mLayer->getDrawingState()).getHeight();
+}
+
+int LayerRenderArea::getWidth() const {
+ return mLayer->getBufferSize(mLayer->getDrawingState()).getWidth();
+}
+
+bool LayerRenderArea::isSecure() const {
+ return mAllowSecureLayers;
+}
+
+bool LayerRenderArea::needsFiltering() const {
+ return mNeedsFiltering;
+}
+
+sp<const DisplayDevice> LayerRenderArea::getDisplayDevice() const {
+ return nullptr;
+}
+
+Rect LayerRenderArea::getSourceCrop() const {
+ if (mCrop.isEmpty()) {
+ return getBounds();
+ } else {
+ return mCrop;
+ }
+}
+
+void LayerRenderArea::render(std::function<void()> drawLayers) {
+ using namespace std::string_literals;
+
+ const Rect sourceCrop = getSourceCrop();
+ // no need to check rotation because there is none
+ mNeedsFiltering = sourceCrop.width() != getReqWidth() || sourceCrop.height() != getReqHeight();
+
+ if (!mChildrenOnly) {
+ mTransform = mLayer->getTransform().inverse();
+ drawLayers();
+ } else {
+ uint32_t w = static_cast<uint32_t>(getWidth());
+ uint32_t h = static_cast<uint32_t>(getHeight());
+ // In the "childrenOnly" case we reparent the children to a screenshot
+ // layer which has no properties set and which does not draw.
+ sp<ContainerLayer> screenshotParentLayer = mFlinger.getFactory().createContainerLayer(
+ {&mFlinger, nullptr, "Screenshot Parent"s, w, h, 0, LayerMetadata()});
+
+ ReparentForDrawing reparent(mLayer, screenshotParentLayer, sourceCrop);
+ drawLayers();
+ }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/LayerRenderArea.h b/services/surfaceflinger/LayerRenderArea.h
new file mode 100644
index 0000000..6a90694
--- /dev/null
+++ b/services/surfaceflinger/LayerRenderArea.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <ui/GraphicTypes.h>
+#include <ui/Transform.h>
+#include <utils/StrongPointer.h>
+
+#include "RenderArea.h"
+
+namespace android {
+
+class DisplayDevice;
+class Layer;
+class SurfaceFlinger;
+
+class LayerRenderArea : public RenderArea {
+public:
+ LayerRenderArea(SurfaceFlinger& flinger, sp<Layer> layer, const Rect& crop, ui::Size reqSize,
+ ui::Dataspace reqDataSpace, bool childrenOnly, const Rect& layerStackRect,
+ bool allowSecureLayers);
+
+ const ui::Transform& getTransform() const override;
+ Rect getBounds() const override;
+ int getHeight() const override;
+ int getWidth() const override;
+ bool isSecure() const override;
+ bool needsFiltering() const override;
+ sp<const DisplayDevice> getDisplayDevice() const override;
+ Rect getSourceCrop() const override;
+
+ void render(std::function<void()> drawLayers) override;
+
+private:
+ const sp<Layer> mLayer;
+ const Rect mCrop;
+
+ ui::Transform mTransform;
+ bool mNeedsFiltering = false;
+
+ SurfaceFlinger& mFlinger;
+ const bool mChildrenOnly;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Promise.h b/services/surfaceflinger/Promise.h
deleted file mode 100644
index a80d441..0000000
--- a/services/surfaceflinger/Promise.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 2020 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.
- */
-
-#pragma once
-
-#include <future>
-#include <type_traits>
-#include <utility>
-
-namespace android::promise {
-namespace impl {
-
-template <typename T>
-struct FutureResult {
- using Type = T;
-};
-
-template <typename T>
-struct FutureResult<std::future<T>> {
- using Type = T;
-};
-
-} // namespace impl
-
-template <typename T>
-using FutureResult = typename impl::FutureResult<T>::Type;
-
-template <typename... Args>
-inline auto defer(Args... args) {
- return std::async(std::launch::deferred, std::forward<Args>(args)...);
-}
-
-template <typename T>
-inline std::future<T> yield(T&& v) {
- return defer([](T&& v) { return std::forward<T>(v); }, std::forward<T>(v));
-}
-
-template <typename T>
-struct Chain {
- Chain(std::future<T>&& f) : future(std::move(f)) {}
- operator std::future<T>&&() && { return std::move(future); }
-
- T get() && { return future.get(); }
-
- template <typename F, typename R = std::invoke_result_t<F, T>>
- auto then(F&& op) && -> Chain<FutureResult<R>> {
- return defer(
- [](auto&& f, F&& op) {
- R r = op(f.get());
- if constexpr (std::is_same_v<R, FutureResult<R>>) {
- return r;
- } else {
- return r.get();
- }
- },
- std::move(future), std::forward<F>(op));
- }
-
- std::future<T> future;
-};
-
-template <typename T>
-inline Chain<T> chain(std::future<T>&& f) {
- return std::move(f);
-}
-
-} // namespace android::promise
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index f602412..a9fd16c 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -17,6 +17,9 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
+
+#include <algorithm>
#include "RefreshRateOverlay.h"
#include "Client.h"
@@ -106,52 +109,86 @@
drawSegment(Segment::Buttom, left, color, buffer, pixels);
}
-sp<GraphicBuffer> RefreshRateOverlay::SevenSegmentDrawer::drawNumber(int number,
- const half4& color) {
- if (number < 0 || number > 1000) return nullptr;
+std::vector<sp<GraphicBuffer>> RefreshRateOverlay::SevenSegmentDrawer::drawNumber(
+ int number, const half4& color, bool showSpinner) {
+ if (number < 0 || number > 1000) return {};
const auto hundreds = number / 100;
const auto tens = (number / 10) % 10;
const auto ones = number % 10;
- sp<GraphicBuffer> buffer =
- new GraphicBuffer(BUFFER_WIDTH, BUFFER_HEIGHT, HAL_PIXEL_FORMAT_RGBA_8888, 1,
- GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER |
- GRALLOC_USAGE_HW_TEXTURE,
- "RefreshRateOverlayBuffer");
- uint8_t* pixels;
- buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
- // Clear buffer content
- drawRect(Rect(BUFFER_WIDTH, BUFFER_HEIGHT), half4(0), buffer, pixels);
- int left = 0;
- if (hundreds != 0) {
- drawDigit(hundreds, left, color, buffer, pixels);
+ std::vector<sp<GraphicBuffer>> buffers;
+ const auto loopCount = showSpinner ? 6 : 1;
+ for (int i = 0; i < loopCount; i++) {
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(BUFFER_WIDTH, BUFFER_HEIGHT, HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER |
+ GRALLOC_USAGE_HW_TEXTURE,
+ "RefreshRateOverlayBuffer");
+ const status_t bufferStatus = buffer->initCheck();
+ LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "RefreshRateOverlay: Buffer failed to allocate: %d",
+ bufferStatus);
+ uint8_t* pixels;
+ buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
+ // Clear buffer content
+ drawRect(Rect(BUFFER_WIDTH, BUFFER_HEIGHT), half4(0), buffer, pixels);
+ int left = 0;
+ if (hundreds != 0) {
+ drawDigit(hundreds, left, color, buffer, pixels);
+ }
left += DIGIT_WIDTH + DIGIT_SPACE;
- }
- if (tens != 0) {
- drawDigit(tens, left, color, buffer, pixels);
+ if (tens != 0) {
+ drawDigit(tens, left, color, buffer, pixels);
+ }
left += DIGIT_WIDTH + DIGIT_SPACE;
- }
- drawDigit(ones, left, color, buffer, pixels);
- buffer->unlock();
- return buffer;
+ drawDigit(ones, left, color, buffer, pixels);
+ left += DIGIT_WIDTH + DIGIT_SPACE;
+
+ if (showSpinner) {
+ switch (i) {
+ case 0:
+ drawSegment(Segment::Upper, left, color, buffer, pixels);
+ break;
+ case 1:
+ drawSegment(Segment::UpperRight, left, color, buffer, pixels);
+ break;
+ case 2:
+ drawSegment(Segment::LowerRight, left, color, buffer, pixels);
+ break;
+ case 3:
+ drawSegment(Segment::Buttom, left, color, buffer, pixels);
+ break;
+ case 4:
+ drawSegment(Segment::LowerLeft, left, color, buffer, pixels);
+ break;
+ case 5:
+ drawSegment(Segment::UpperLeft, left, color, buffer, pixels);
+ break;
+ }
+ }
+
+ buffer->unlock();
+ buffers.emplace_back(buffer);
+ }
+ return buffers;
}
-RefreshRateOverlay::RefreshRateOverlay(SurfaceFlinger& flinger)
- : mFlinger(flinger), mClient(new Client(&mFlinger)) {
+RefreshRateOverlay::RefreshRateOverlay(SurfaceFlinger& flinger, bool showSpinner)
+ : mFlinger(flinger), mClient(new Client(&mFlinger)), mShowSpinner(showSpinner) {
createLayer();
- primeCache();
+ reset();
}
bool RefreshRateOverlay::createLayer() {
+ int32_t layerId;
const status_t ret =
mFlinger.createLayer(String8("RefreshRateOverlay"), mClient,
SevenSegmentDrawer::getWidth(), SevenSegmentDrawer::getHeight(),
PIXEL_FORMAT_RGBA_8888,
ISurfaceComposerClient::eFXSurfaceBufferState, LayerMetadata(),
- &mIBinder, &mGbp, nullptr);
+ &mIBinder, &mGbp, nullptr, &layerId);
if (ret) {
ALOGE("failed to create buffer state layer");
return false;
@@ -159,7 +196,7 @@
Mutex::Autolock _l(mFlinger.mStateLock);
mLayer = mClient->getLayerUser(mIBinder);
- mLayer->setFrameRate(Layer::FrameRate(0, Layer::FrameRateCompatibility::NoVote));
+ mLayer->setFrameRate(Layer::FrameRate(Fps(0.0f), Layer::FrameRateCompatibility::NoVote));
// setting Layer's Z requires resorting layersSortedByZ
ssize_t idx = mFlinger.mCurrentState.layersSortedByZ.indexOf(mLayer);
@@ -171,51 +208,84 @@
return true;
}
-void RefreshRateOverlay::primeCache() {
- auto& allRefreshRates = mFlinger.mRefreshRateConfigs->getAllRefreshRates();
- if (allRefreshRates.size() == 1) {
- auto fps = allRefreshRates.begin()->second->getFps();
- half4 color = {LOW_FPS_COLOR, ALPHA};
- mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color));
- return;
- }
-
- std::vector<uint32_t> supportedFps;
- supportedFps.reserve(allRefreshRates.size());
- for (auto& [ignored, refreshRate] : allRefreshRates) {
- supportedFps.push_back(refreshRate->getFps());
- }
-
- std::sort(supportedFps.begin(), supportedFps.end());
- const auto mLowFps = supportedFps[0];
- const auto mHighFps = supportedFps[supportedFps.size() - 1];
- for (auto fps : supportedFps) {
- const auto fpsScale = float(fps - mLowFps) / (mHighFps - mLowFps);
+const std::vector<std::shared_ptr<renderengine::ExternalTexture>>&
+RefreshRateOverlay::getOrCreateBuffers(uint32_t fps) {
+ if (mBufferCache.find(fps) == mBufferCache.end()) {
+ // Ensure the range is > 0, so we don't divide by 0.
+ const auto rangeLength = std::max(1u, mHighFps - mLowFps);
+ // Clip values outside the range [mLowFps, mHighFps]. The current fps may be outside
+ // of this range if the display has changed its set of supported refresh rates.
+ fps = std::max(fps, mLowFps);
+ fps = std::min(fps, mHighFps);
+ const auto fpsScale = static_cast<float>(fps - mLowFps) / rangeLength;
half4 color;
color.r = HIGH_FPS_COLOR.r * fpsScale + LOW_FPS_COLOR.r * (1 - fpsScale);
color.g = HIGH_FPS_COLOR.g * fpsScale + LOW_FPS_COLOR.g * (1 - fpsScale);
color.b = HIGH_FPS_COLOR.b * fpsScale + LOW_FPS_COLOR.b * (1 - fpsScale);
color.a = ALPHA;
- mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color));
+ auto buffers = SevenSegmentDrawer::drawNumber(fps, color, mShowSpinner);
+ std::vector<std::shared_ptr<renderengine::ExternalTexture>> textures;
+ std::transform(buffers.begin(), buffers.end(), std::back_inserter(textures),
+ [&](const auto& buffer) -> std::shared_ptr<renderengine::ExternalTexture> {
+ return std::make_shared<
+ renderengine::ExternalTexture>(buffer,
+ mFlinger.getRenderEngine(),
+ renderengine::ExternalTexture::
+ Usage::READABLE);
+ });
+ mBufferCache.emplace(fps, textures);
}
+
+ return mBufferCache[fps];
}
void RefreshRateOverlay::setViewport(ui::Size viewport) {
- Rect frame(viewport.width >> 3, viewport.height >> 5);
+ Rect frame((3 * viewport.width) >> 4, viewport.height >> 5);
frame.offsetBy(viewport.width >> 5, viewport.height >> 4);
- mLayer->setFrame(frame);
+
+ layer_state_t::matrix22_t matrix;
+ matrix.dsdx = frame.getWidth() / static_cast<float>(SevenSegmentDrawer::getWidth());
+ matrix.dtdx = 0;
+ matrix.dtdy = 0;
+ matrix.dsdy = frame.getHeight() / static_cast<float>(SevenSegmentDrawer::getHeight());
+ mLayer->setMatrix(matrix, true);
+ mLayer->setPosition(frame.left, frame.top);
+ mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
+}
+
+void RefreshRateOverlay::changeRefreshRate(const Fps& fps) {
+ mCurrentFps = fps.getIntValue();
+ auto buffer = getOrCreateBuffers(*mCurrentFps)[mFrame];
+ mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {},
+ mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */),
+ std::nullopt /* dequeueTime */, FrameTimelineInfo{},
+ nullptr /* releaseBufferListener */);
mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
}
-void RefreshRateOverlay::changeRefreshRate(const RefreshRate& refreshRate) {
- auto buffer = mBufferCache[refreshRate.getFps()];
- mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {});
+void RefreshRateOverlay::onInvalidate() {
+ if (!mCurrentFps.has_value()) return;
+
+ const auto& buffers = getOrCreateBuffers(*mCurrentFps);
+ mFrame = (mFrame + 1) % buffers.size();
+ auto buffer = buffers[mFrame];
+ mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {},
+ mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */),
+ std::nullopt /* dequeueTime */, FrameTimelineInfo{},
+ nullptr /* releaseBufferListener */);
mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
}
+void RefreshRateOverlay::reset() {
+ mBufferCache.clear();
+ const auto range = mFlinger.mRefreshRateConfigs->getSupportedRefreshRateRange();
+ mLowFps = range.min.getIntValue();
+ mHighFps = range.max.getIntValue();
+}
+
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index 35c8020..aa8329c 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -16,14 +16,15 @@
#pragma once
-#include <unordered_map>
-
#include <math/vec4.h>
+#include <renderengine/RenderEngine.h>
#include <ui/Rect.h>
#include <ui/Size.h>
#include <utils/StrongPointer.h>
-#include "Scheduler/RefreshRateConfigs.h"
+#include <unordered_map>
+
+#include "Fps.h"
namespace android {
@@ -34,19 +35,20 @@
class Layer;
class SurfaceFlinger;
-using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
-
class RefreshRateOverlay {
public:
- explicit RefreshRateOverlay(SurfaceFlinger&);
+ RefreshRateOverlay(SurfaceFlinger&, bool showSpinner);
void setViewport(ui::Size);
- void changeRefreshRate(const RefreshRate&);
+ void changeRefreshRate(const Fps&);
+ void onInvalidate();
+ void reset();
private:
class SevenSegmentDrawer {
public:
- static sp<GraphicBuffer> drawNumber(int number, const half4& color);
+ static std::vector<sp<GraphicBuffer>> drawNumber(int number, const half4& color,
+ bool showSpinner);
static uint32_t getHeight() { return BUFFER_HEIGHT; }
static uint32_t getWidth() { return BUFFER_WIDTH; }
@@ -65,11 +67,12 @@
static constexpr uint32_t DIGIT_SPACE = 16;
static constexpr uint32_t BUFFER_HEIGHT = DIGIT_HEIGHT;
static constexpr uint32_t BUFFER_WIDTH =
- 3 * DIGIT_WIDTH + 2 * DIGIT_SPACE; // Digit|Space|Digit|Space|Digit
+ 4 * DIGIT_WIDTH + 3 * DIGIT_SPACE; // Digit|Space|Digit|Space|Digit|Space|Spinner
};
bool createLayer();
- void primeCache();
+ const std::vector<std::shared_ptr<renderengine::ExternalTexture>>& getOrCreateBuffers(
+ uint32_t fps);
SurfaceFlinger& mFlinger;
const sp<Client> mClient;
@@ -77,11 +80,19 @@
sp<IBinder> mIBinder;
sp<IGraphicBufferProducer> mGbp;
- std::unordered_map<int, sp<GraphicBuffer>> mBufferCache;
-
+ std::unordered_map<int, std::vector<std::shared_ptr<renderengine::ExternalTexture>>>
+ mBufferCache;
+ std::optional<int> mCurrentFps;
+ int mFrame = 0;
static constexpr float ALPHA = 0.8f;
const half3 LOW_FPS_COLOR = half3(1.0f, 0.0f, 0.0f);
const half3 HIGH_FPS_COLOR = half3(0.0f, 1.0f, 0.0f);
+
+ const bool mShowSpinner;
+
+ // Interpolate the colors between these values.
+ uint32_t mLowFps;
+ uint32_t mHighFps;
};
} // namespace android
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 27353d8..00090d9 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -17,6 +17,7 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
//#define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -28,15 +29,18 @@
#include <compositionengine/Display.h>
#include <compositionengine/impl/OutputCompositionState.h>
#include <cutils/properties.h>
+#include <ftl/future.h>
#include <gui/IRegionSamplingListener.h>
+#include <gui/SyncScreenCaptureListener.h>
#include <ui/DisplayStatInfo.h>
#include <utils/Trace.h>
#include <string>
#include "DisplayDevice.h"
+#include "DisplayRenderArea.h"
#include "Layer.h"
-#include "Scheduler/DispSync.h"
+#include "Scheduler/VsyncController.h"
#include "SurfaceFlinger.h"
namespace android {
@@ -59,7 +63,7 @@
constexpr auto timeForRegionSampling = 5000000ns;
constexpr auto maxRegionSamplingSkips = 10;
-constexpr auto defaultRegionSamplingOffset = -3ms;
+constexpr auto defaultRegionSamplingWorkDuration = 3ms;
constexpr auto defaultRegionSamplingPeriod = 100ms;
constexpr auto defaultRegionSamplingTimerTimeout = 100ms;
// TODO: (b/127403193) duration to string conversion could probably be constexpr
@@ -71,9 +75,9 @@
RegionSamplingThread::EnvironmentTimingTunables::EnvironmentTimingTunables() {
char value[PROPERTY_VALUE_MAX] = {};
- property_get("debug.sf.region_sampling_offset_ns", value,
- toNsString(defaultRegionSamplingOffset).c_str());
- int const samplingOffsetNsRaw = atoi(value);
+ property_get("debug.sf.region_sampling_duration_ns", value,
+ toNsString(defaultRegionSamplingWorkDuration).c_str());
+ int const samplingDurationNsRaw = atoi(value);
property_get("debug.sf.region_sampling_period_ns", value,
toNsString(defaultRegionSamplingPeriod).c_str());
@@ -85,22 +89,26 @@
if ((samplingPeriodNsRaw < 0) || (samplingTimerTimeoutNsRaw < 0)) {
ALOGW("User-specified sampling tuning options nonsensical. Using defaults");
- mSamplingOffset = defaultRegionSamplingOffset;
+ mSamplingDuration = defaultRegionSamplingWorkDuration;
mSamplingPeriod = defaultRegionSamplingPeriod;
mSamplingTimerTimeout = defaultRegionSamplingTimerTimeout;
} else {
- mSamplingOffset = std::chrono::nanoseconds(samplingOffsetNsRaw);
+ mSamplingDuration = std::chrono::nanoseconds(samplingDurationNsRaw);
mSamplingPeriod = std::chrono::nanoseconds(samplingPeriodNsRaw);
mSamplingTimerTimeout = std::chrono::nanoseconds(samplingTimerTimeoutNsRaw);
}
}
-struct SamplingOffsetCallback : DispSync::Callback {
+struct SamplingOffsetCallback : VSyncSource::Callback {
SamplingOffsetCallback(RegionSamplingThread& samplingThread, Scheduler& scheduler,
- std::chrono::nanoseconds targetSamplingOffset)
+ std::chrono::nanoseconds targetSamplingWorkDuration)
: mRegionSamplingThread(samplingThread),
- mScheduler(scheduler),
- mTargetSamplingOffset(targetSamplingOffset) {}
+ mTargetSamplingWorkDuration(targetSamplingWorkDuration),
+ mVSyncSource(scheduler.makePrimaryDispSyncSource("SamplingThreadDispSyncListener", 0ns,
+ 0ns,
+ /*traceVsync=*/false)) {
+ mVSyncSource->setCallback(this);
+ }
~SamplingOffsetCallback() { stopVsyncListener(); }
@@ -112,8 +120,7 @@
if (mVsyncListening) return;
mPhaseIntervalSetting = Phase::ZERO;
- mScheduler.getPrimaryDispSync().addEventListener("SamplingThreadDispSyncListener", 0, this,
- mLastCallbackTime);
+ mVSyncSource->setVSyncEnabled(true);
mVsyncListening = true;
}
@@ -126,23 +133,24 @@
void stopVsyncListenerLocked() /*REQUIRES(mMutex)*/ {
if (!mVsyncListening) return;
- mScheduler.getPrimaryDispSync().removeEventListener(this, &mLastCallbackTime);
+ mVSyncSource->setVSyncEnabled(false);
mVsyncListening = false;
}
- void onDispSyncEvent(nsecs_t /*when*/, nsecs_t /*expectedVSyncTimestamp*/) final {
+ void onVSyncEvent(nsecs_t /*when*/, nsecs_t /*expectedVSyncTimestamp*/,
+ nsecs_t /*deadlineTimestamp*/) final {
std::unique_lock<decltype(mMutex)> lock(mMutex);
if (mPhaseIntervalSetting == Phase::ZERO) {
ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase));
mPhaseIntervalSetting = Phase::SAMPLING;
- mScheduler.getPrimaryDispSync().changePhaseOffset(this, mTargetSamplingOffset.count());
+ mVSyncSource->setDuration(mTargetSamplingWorkDuration, 0ns);
return;
}
if (mPhaseIntervalSetting == Phase::SAMPLING) {
mPhaseIntervalSetting = Phase::ZERO;
- mScheduler.getPrimaryDispSync().changePhaseOffset(this, 0);
+ mVSyncSource->setDuration(0ns, 0ns);
stopVsyncListenerLocked();
lock.unlock();
mRegionSamplingThread.notifySamplingOffset();
@@ -151,16 +159,15 @@
}
RegionSamplingThread& mRegionSamplingThread;
- Scheduler& mScheduler;
- const std::chrono::nanoseconds mTargetSamplingOffset;
+ const std::chrono::nanoseconds mTargetSamplingWorkDuration;
mutable std::mutex mMutex;
- nsecs_t mLastCallbackTime = 0;
enum class Phase {
ZERO,
SAMPLING
} mPhaseIntervalSetting /*GUARDED_BY(mMutex) macro doesnt work with unique_lock?*/
= Phase::ZERO;
bool mVsyncListening /*GUARDED_BY(mMutex)*/ = false;
+ std::unique_ptr<VSyncSource> mVSyncSource;
};
RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler,
@@ -168,20 +175,22 @@
: mFlinger(flinger),
mScheduler(scheduler),
mTunables(tunables),
- mIdleTimer(std::chrono::duration_cast<std::chrono::milliseconds>(
- mTunables.mSamplingTimerTimeout),
- [] {}, [this] { checkForStaleLuma(); }),
+ mIdleTimer(
+ "RegSampIdle",
+ std::chrono::duration_cast<std::chrono::milliseconds>(
+ mTunables.mSamplingTimerTimeout),
+ [] {}, [this] { checkForStaleLuma(); }),
mPhaseCallback(std::make_unique<SamplingOffsetCallback>(*this, mScheduler,
- tunables.mSamplingOffset)),
+ tunables.mSamplingDuration)),
lastSampleTime(0ns) {
mThread = std::thread([this]() { threadMain(); });
- pthread_setname_np(mThread.native_handle(), "RegionSamplingThread");
+ pthread_setname_np(mThread.native_handle(), "RegionSampling");
mIdleTimer.start();
}
RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler)
: RegionSamplingThread(flinger, scheduler,
- TimingTunables{defaultRegionSamplingOffset,
+ TimingTunables{defaultRegionSamplingWorkDuration,
defaultRegionSamplingPeriod,
defaultRegionSamplingTimerTimeout}) {}
@@ -242,8 +251,7 @@
// If there is relatively little time left for surfaceflinger
// until the next vsync deadline, defer this sampling work
// to a later frame, when hopefully there will be more time.
- DisplayStatInfo stats;
- mScheduler.getDisplayStatInfo(&stats);
+ const DisplayStatInfo stats = mScheduler.getDisplayStatInfo(systemTime());
if (std::chrono::nanoseconds(stats.vsyncTime) - now < timeForRegionSampling) {
ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForQuietFrame));
mDiscardedFrames++;
@@ -336,8 +344,20 @@
return;
}
- const auto device = mFlinger.getDefaultDisplayDevice();
- const auto orientation = ui::Transform::toRotationFlags(device->getOrientation());
+ wp<const DisplayDevice> displayWeak;
+
+ ui::LayerStack layerStack;
+ ui::Transform::RotationFlags orientation;
+ ui::Size displaySize;
+
+ {
+ // TODO(b/159112860): Don't keep sp<DisplayDevice> outside of SF main thread
+ const sp<const DisplayDevice> display = mFlinger.getDefaultDisplayDevice();
+ displayWeak = display;
+ layerStack = display->getLayerStack();
+ orientation = ui::Transform::toRotationFlags(display->getOrientation());
+ displaySize = display->getSize();
+ }
std::vector<RegionSamplingThread::Descriptor> descriptors;
Region sampleRegion;
@@ -346,20 +366,18 @@
descriptors.emplace_back(descriptor);
}
- const Rect sampledArea = sampleRegion.bounds();
-
auto dx = 0;
auto dy = 0;
switch (orientation) {
case ui::Transform::ROT_90:
- dx = device->getWidth();
+ dx = displaySize.getWidth();
break;
case ui::Transform::ROT_180:
- dx = device->getWidth();
- dy = device->getHeight();
+ dx = displaySize.getWidth();
+ dy = displaySize.getHeight();
break;
case ui::Transform::ROT_270:
- dy = device->getHeight();
+ dy = displaySize.getHeight();
break;
default:
break;
@@ -368,8 +386,14 @@
ui::Transform t(orientation);
auto screencapRegion = t.transform(sampleRegion);
screencapRegion = screencapRegion.translate(dx, dy);
- DisplayRenderArea renderArea(device, screencapRegion.bounds(), sampledArea.getWidth(),
- sampledArea.getHeight(), ui::Dataspace::V0_SRGB, orientation);
+
+ const Rect sampledBounds = sampleRegion.bounds();
+
+ SurfaceFlinger::RenderAreaFuture renderAreaFuture = ftl::defer([=] {
+ return DisplayRenderArea::create(displayWeak, screencapRegion.bounds(),
+ sampledBounds.getSize(), ui::Dataspace::V0_SRGB,
+ orientation);
+ });
std::unordered_set<sp<IRegionSamplingListener>, SpHash<IRegionSamplingListener>> listeners;
@@ -393,9 +417,9 @@
constexpr bool roundOutwards = true;
Rect transformed = transform.transform(bounds, roundOutwards);
- // If this layer doesn't intersect with the larger sampledArea, skip capturing it
+ // If this layer doesn't intersect with the larger sampledBounds, skip capturing it
Rect ignore;
- if (!transformed.intersect(sampledArea, &ignore)) return;
+ if (!transformed.intersect(sampledBounds, &ignore)) return;
// If the layer doesn't intersect a sampling area, skip capturing it
bool intersectsAnyArea = false;
@@ -411,22 +435,31 @@
bounds.top, bounds.right, bounds.bottom);
visitor(layer);
};
- mFlinger.traverseLayersInDisplay(device, filterVisitor);
+ mFlinger.traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, filterVisitor);
};
- sp<GraphicBuffer> buffer = nullptr;
- if (mCachedBuffer && mCachedBuffer->getWidth() == sampledArea.getWidth() &&
- mCachedBuffer->getHeight() == sampledArea.getHeight()) {
+ std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr;
+ if (mCachedBuffer && mCachedBuffer->getBuffer()->getWidth() == sampledBounds.getWidth() &&
+ mCachedBuffer->getBuffer()->getHeight() == sampledBounds.getHeight()) {
buffer = mCachedBuffer;
} else {
- const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER;
- buffer = new GraphicBuffer(sampledArea.getWidth(), sampledArea.getHeight(),
- PIXEL_FORMAT_RGBA_8888, 1, usage, "RegionSamplingThread");
+ const uint32_t usage =
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
+ sp<GraphicBuffer> graphicBuffer =
+ new GraphicBuffer(sampledBounds.getWidth(), sampledBounds.getHeight(),
+ PIXEL_FORMAT_RGBA_8888, 1, usage, "RegionSamplingThread");
+ const status_t bufferStatus = graphicBuffer->initCheck();
+ LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "captureSample: Buffer failed to allocate: %d",
+ bufferStatus);
+ buffer = std::make_shared<
+ renderengine::ExternalTexture>(graphicBuffer, mFlinger.getRenderEngine(),
+ renderengine::ExternalTexture::Usage::WRITEABLE);
}
- bool ignored;
- mFlinger.captureScreenCommon(renderArea, traverseLayers, buffer, false /* identityTransform */,
- true /* regionSampling */, ignored);
+ const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
+ mFlinger.captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer,
+ true /* regionSampling */, false /* grayscale */, captureListener);
+ ScreenCaptureResults captureResults = captureListener->waitForResults();
std::vector<Descriptor> activeDescriptors;
for (const auto& descriptor : descriptors) {
@@ -436,8 +469,8 @@
}
ALOGV("Sampling %zu descriptors", activeDescriptors.size());
- std::vector<float> lumas =
- sampleBuffer(buffer, sampledArea.leftTop(), activeDescriptors, orientation);
+ std::vector<float> lumas = sampleBuffer(buffer->getBuffer(), sampledBounds.leftTop(),
+ activeDescriptors, orientation);
if (lumas.size() != activeDescriptors.size()) {
ALOGW("collected %zu median luma values for %zu descriptors", lumas.size(),
activeDescriptors.size());
@@ -448,10 +481,6 @@
activeDescriptors[d].listener->onSampleCollected(lumas[d]);
}
- // Extend the lifetime of mCachedBuffer from the previous frame to here to ensure that:
- // 1) The region sampling thread is the last owner of the buffer, and the freeing of the buffer
- // happens in this thread, as opposed to the main thread.
- // 2) The listener(s) receive their notifications prior to freeing the buffer.
mCachedBuffer = buffer;
ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::noWorkNeeded));
}
@@ -475,4 +504,4 @@
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h
index b9b7a3c..86632db 100644
--- a/services/surfaceflinger/RegionSamplingThread.h
+++ b/services/surfaceflinger/RegionSamplingThread.h
@@ -16,17 +16,19 @@
#pragma once
+#include <android-base/thread_annotations.h>
+#include <binder/IBinder.h>
+#include <renderengine/ExternalTexture.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/Rect.h>
+#include <utils/StrongPointer.h>
+
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <unordered_map>
-#include <android-base/thread_annotations.h>
-#include <binder/IBinder.h>
-#include <ui/GraphicBuffer.h>
-#include <ui/Rect.h>
-#include <utils/StrongPointer.h>
#include "Scheduler/OneShotTimer.h"
namespace android {
@@ -43,10 +45,10 @@
class RegionSamplingThread : public IBinder::DeathRecipient {
public:
struct TimingTunables {
- // debug.sf.sampling_offset_ns
- // When asynchronously collecting sample, the offset, from zero phase in the vsync timeline
- // at which the sampling should start.
- std::chrono::nanoseconds mSamplingOffset;
+ // debug.sf.sampling_duration_ns
+ // When asynchronously collecting sample, the duration, at which the sampling should start
+ // before a vsync
+ std::chrono::nanoseconds mSamplingDuration;
// debug.sf.sampling_period_ns
// This is the maximum amount of time the luma recieving client
// should have to wait for a new luma value after a frame is updated. The inverse of this is
@@ -122,7 +124,8 @@
std::mutex mSamplingMutex;
std::unordered_map<wp<IBinder>, Descriptor, WpHash> mDescriptors GUARDED_BY(mSamplingMutex);
- sp<GraphicBuffer> mCachedBuffer GUARDED_BY(mSamplingMutex) = nullptr;
+ std::shared_ptr<renderengine::ExternalTexture> mCachedBuffer GUARDED_BY(mSamplingMutex) =
+ nullptr;
};
} // namespace android
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index 6b0455a..c9f7f46 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -23,15 +23,15 @@
static float getCaptureFillValue(CaptureFill captureFill);
- RenderArea(uint32_t reqWidth, uint32_t reqHeight, CaptureFill captureFill,
- ui::Dataspace reqDataSpace, const Rect& displayViewport,
+ RenderArea(ui::Size reqSize, CaptureFill captureFill, ui::Dataspace reqDataSpace,
+ const Rect& layerStackRect, bool allowSecureLayers = false,
RotationFlags rotation = ui::Transform::ROT_0)
- : mReqWidth(reqWidth),
- mReqHeight(reqHeight),
+ : mAllowSecureLayers(allowSecureLayers),
+ mReqSize(reqSize),
mReqDataSpace(reqDataSpace),
mCaptureFill(captureFill),
mRotationFlags(rotation),
- mDisplayViewport(displayViewport) {}
+ mLayerStackSpaceRect(layerStackRect) {}
virtual ~RenderArea() = default;
@@ -70,8 +70,8 @@
RotationFlags getRotationFlags() const { return mRotationFlags; }
// Returns the size of the physical render area.
- int getReqWidth() const { return static_cast<int>(mReqWidth); }
- int getReqHeight() const { return static_cast<int>(mReqHeight); }
+ int getReqWidth() const { return mReqSize.width; }
+ int getReqHeight() const { return mReqSize.height; }
// Returns the composition data space of the render area.
ui::Dataspace getReqDataSpace() const { return mReqDataSpace; }
@@ -83,15 +83,17 @@
virtual sp<const DisplayDevice> getDisplayDevice() const = 0;
// Returns the source display viewport.
- const Rect& getDisplayViewport() const { return mDisplayViewport; }
+ const Rect& getLayerStackSpaceRect() const { return mLayerStackSpaceRect; }
+
+protected:
+ const bool mAllowSecureLayers;
private:
- const uint32_t mReqWidth;
- const uint32_t mReqHeight;
+ const ui::Size mReqSize;
const ui::Dataspace mReqDataSpace;
const CaptureFill mCaptureFill;
const RotationFlags mRotationFlags;
- const Rect mDisplayViewport;
+ const Rect mLayerStackSpaceRect;
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
deleted file mode 100644
index 46112f5..0000000
--- a/services/surfaceflinger/Scheduler/DispSync.cpp
+++ /dev/null
@@ -1,877 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-//#define LOG_NDEBUG 0
-
-// This is needed for stdint.h to define INT64_MAX in C++
-#define __STDC_LIMIT_MACROS
-
-#include <math.h>
-
-#include <algorithm>
-
-#include <android-base/stringprintf.h>
-#include <cutils/properties.h>
-#include <log/log.h>
-#include <utils/Thread.h>
-#include <utils/Trace.h>
-
-#include <ui/FenceTime.h>
-
-#include "DispSync.h"
-#include "EventLog/EventLog.h"
-#include "SurfaceFlinger.h"
-
-using android::base::StringAppendF;
-using std::max;
-using std::min;
-
-namespace android {
-
-DispSync::~DispSync() = default;
-DispSync::Callback::~Callback() = default;
-
-namespace impl {
-
-// Setting this to true adds a zero-phase tracer for correlating with hardware
-// vsync events
-static const bool kEnableZeroPhaseTracer = false;
-
-// This is the threshold used to determine when hardware vsync events are
-// needed to re-synchronize the software vsync model with the hardware. The
-// error metric used is the mean of the squared difference between each
-// present time and the nearest software-predicted vsync.
-static const nsecs_t kErrorThreshold = 160000000000; // 400 usec squared
-
-#undef LOG_TAG
-#define LOG_TAG "DispSyncThread"
-class DispSyncThread : public Thread {
-public:
- DispSyncThread(const char* name, bool showTraceDetailedInfo)
- : mName(name),
- mStop(false),
- mModelLocked("DispSync:ModelLocked", false),
- mPeriod(0),
- mPhase(0),
- mReferenceTime(0),
- mWakeupLatency(0),
- mFrameNumber(0),
- mTraceDetailedInfo(showTraceDetailedInfo) {}
-
- virtual ~DispSyncThread() {}
-
- void updateModel(nsecs_t period, nsecs_t phase, nsecs_t referenceTime) {
- if (mTraceDetailedInfo) ATRACE_CALL();
- Mutex::Autolock lock(mMutex);
-
- mPhase = phase;
- const bool referenceTimeChanged = mReferenceTime != referenceTime;
- mReferenceTime = referenceTime;
- if (mPeriod != 0 && mPeriod != period && mReferenceTime != 0) {
- // Inflate the reference time to be the most recent predicted
- // vsync before the current time.
- const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- const nsecs_t baseTime = now - mReferenceTime;
- const nsecs_t numOldPeriods = baseTime / mPeriod;
- mReferenceTime = mReferenceTime + (numOldPeriods)*mPeriod;
- }
- mPeriod = period;
- if (!mModelLocked && referenceTimeChanged) {
- for (auto& eventListener : mEventListeners) {
- eventListener.mLastEventTime = mReferenceTime + mPhase + eventListener.mPhase;
- // If mLastEventTime is after mReferenceTime (can happen when positive phase offsets
- // are used) we treat it as like it happened in previous period.
- if (eventListener.mLastEventTime > mReferenceTime) {
- eventListener.mLastEventTime -= mPeriod;
- }
- }
- }
- if (mTraceDetailedInfo) {
- ATRACE_INT64("DispSync:Period", mPeriod);
- ATRACE_INT64("DispSync:Phase", mPhase + mPeriod / 2);
- ATRACE_INT64("DispSync:Reference Time", mReferenceTime);
- }
- ALOGV("[%s] updateModel: mPeriod = %" PRId64 ", mPhase = %" PRId64
- " mReferenceTime = %" PRId64,
- mName, ns2us(mPeriod), ns2us(mPhase), ns2us(mReferenceTime));
- mCond.signal();
- }
-
- void stop() {
- if (mTraceDetailedInfo) ATRACE_CALL();
- Mutex::Autolock lock(mMutex);
- mStop = true;
- mCond.signal();
- }
-
- void lockModel() {
- Mutex::Autolock lock(mMutex);
- mModelLocked = true;
- }
-
- void unlockModel() {
- Mutex::Autolock lock(mMutex);
- mModelLocked = false;
- }
-
- virtual bool threadLoop() {
- status_t err;
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-
- while (true) {
- std::vector<CallbackInvocation> callbackInvocations;
-
- nsecs_t targetTime = 0;
-
- { // Scope for lock
- Mutex::Autolock lock(mMutex);
-
- if (mTraceDetailedInfo) {
- ATRACE_INT64("DispSync:Frame", mFrameNumber);
- }
- ALOGV("[%s] Frame %" PRId64, mName, mFrameNumber);
- ++mFrameNumber;
-
- if (mStop) {
- return false;
- }
-
- if (mPeriod == 0) {
- err = mCond.wait(mMutex);
- if (err != NO_ERROR) {
- ALOGE("error waiting for new events: %s (%d)", strerror(-err), err);
- return false;
- }
- continue;
- }
-
- targetTime = computeNextEventTimeLocked(now);
-
- bool isWakeup = false;
-
- if (now < targetTime) {
- if (mTraceDetailedInfo) ATRACE_NAME("DispSync waiting");
-
- if (targetTime == INT64_MAX) {
- ALOGV("[%s] Waiting forever", mName);
- err = mCond.wait(mMutex);
- } else {
- ALOGV("[%s] Waiting until %" PRId64, mName, ns2us(targetTime));
- err = mCond.waitRelative(mMutex, targetTime - now);
- }
-
- if (err == TIMED_OUT) {
- isWakeup = true;
- } else if (err != NO_ERROR) {
- ALOGE("error waiting for next event: %s (%d)", strerror(-err), err);
- return false;
- }
- }
-
- now = systemTime(SYSTEM_TIME_MONOTONIC);
-
- // Don't correct by more than 1.5 ms
- static const nsecs_t kMaxWakeupLatency = us2ns(1500);
-
- if (isWakeup) {
- mWakeupLatency = ((mWakeupLatency * 63) + (now - targetTime)) / 64;
- mWakeupLatency = min(mWakeupLatency, kMaxWakeupLatency);
- if (mTraceDetailedInfo) {
- ATRACE_INT64("DispSync:WakeupLat", now - targetTime);
- ATRACE_INT64("DispSync:AvgWakeupLat", mWakeupLatency);
- }
- }
-
- callbackInvocations =
- gatherCallbackInvocationsLocked(now, computeNextRefreshLocked(0, now));
- }
-
- if (callbackInvocations.size() > 0) {
- fireCallbackInvocations(callbackInvocations);
- }
- }
-
- return false;
- }
-
- status_t addEventListener(const char* name, nsecs_t phase, DispSync::Callback* callback,
- nsecs_t lastCallbackTime) {
- if (mTraceDetailedInfo) ATRACE_CALL();
- Mutex::Autolock lock(mMutex);
-
- for (size_t i = 0; i < mEventListeners.size(); i++) {
- if (mEventListeners[i].mCallback == callback) {
- return BAD_VALUE;
- }
- }
-
- EventListener listener;
- listener.mName = name;
- listener.mPhase = phase;
- listener.mCallback = callback;
-
- // We want to allow the firstmost future event to fire without
- // allowing any past events to fire. To do this extrapolate from
- // mReferenceTime the most recent hardware vsync, and pin the
- // last event time there.
- const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- if (mPeriod != 0) {
- const nsecs_t baseTime = now - mReferenceTime;
- const nsecs_t numPeriodsSinceReference = baseTime / mPeriod;
- const nsecs_t predictedReference = mReferenceTime + numPeriodsSinceReference * mPeriod;
- const nsecs_t phaseCorrection = mPhase + listener.mPhase;
- const nsecs_t predictedLastEventTime = predictedReference + phaseCorrection;
- if (predictedLastEventTime >= now) {
- // Make sure that the last event time does not exceed the current time.
- // If it would, then back the last event time by a period.
- listener.mLastEventTime = predictedLastEventTime - mPeriod;
- } else {
- listener.mLastEventTime = predictedLastEventTime;
- }
- } else {
- listener.mLastEventTime = now + mPhase - mWakeupLatency;
- }
-
- if (lastCallbackTime <= 0) {
- // If there is no prior callback time, try to infer one based on the
- // logical last event time.
- listener.mLastCallbackTime = listener.mLastEventTime + mWakeupLatency;
- } else {
- listener.mLastCallbackTime = lastCallbackTime;
- }
-
- mEventListeners.push_back(listener);
-
- mCond.signal();
-
- return NO_ERROR;
- }
-
- status_t removeEventListener(DispSync::Callback* callback, nsecs_t* outLastCallback) {
- if (mTraceDetailedInfo) ATRACE_CALL();
- Mutex::Autolock lock(mMutex);
-
- for (std::vector<EventListener>::iterator it = mEventListeners.begin();
- it != mEventListeners.end(); ++it) {
- if (it->mCallback == callback) {
- *outLastCallback = it->mLastCallbackTime;
- mEventListeners.erase(it);
- mCond.signal();
- return NO_ERROR;
- }
- }
-
- return BAD_VALUE;
- }
-
- status_t changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) {
- if (mTraceDetailedInfo) ATRACE_CALL();
- Mutex::Autolock lock(mMutex);
-
- for (auto& eventListener : mEventListeners) {
- if (eventListener.mCallback == callback) {
- const nsecs_t oldPhase = eventListener.mPhase;
- eventListener.mPhase = phase;
-
- // Pretend that the last time this event was handled at the same frame but with the
- // new offset to allow for a seamless offset change without double-firing or
- // skipping.
- nsecs_t diff = oldPhase - phase;
- eventListener.mLastEventTime -= diff;
- eventListener.mLastCallbackTime -= diff;
- mCond.signal();
- return NO_ERROR;
- }
- }
- return BAD_VALUE;
- }
-
- nsecs_t computeNextRefresh(int periodOffset, nsecs_t now) const {
- Mutex::Autolock lock(mMutex);
- return computeNextRefreshLocked(periodOffset, now);
- }
-
-private:
- struct EventListener {
- const char* mName;
- nsecs_t mPhase;
- nsecs_t mLastEventTime;
- nsecs_t mLastCallbackTime;
- DispSync::Callback* mCallback;
- };
-
- struct CallbackInvocation {
- DispSync::Callback* mCallback;
- nsecs_t mEventTime;
- nsecs_t mExpectedVSyncTime;
- };
-
- nsecs_t computeNextEventTimeLocked(nsecs_t now) {
- if (mTraceDetailedInfo) ATRACE_CALL();
- ALOGV("[%s] computeNextEventTimeLocked", mName);
- nsecs_t nextEventTime = INT64_MAX;
- for (size_t i = 0; i < mEventListeners.size(); i++) {
- nsecs_t t = computeListenerNextEventTimeLocked(mEventListeners[i], now);
-
- if (t < nextEventTime) {
- nextEventTime = t;
- }
- }
-
- ALOGV("[%s] nextEventTime = %" PRId64, mName, ns2us(nextEventTime));
- return nextEventTime;
- }
-
- // Check that the duration is close enough in length to a period without
- // falling into double-rate vsyncs.
- bool isCloseToPeriod(nsecs_t duration) {
- // Ratio of 3/5 is arbitrary, but it must be greater than 1/2.
- return duration < (3 * mPeriod) / 5;
- }
-
- std::vector<CallbackInvocation> gatherCallbackInvocationsLocked(nsecs_t now,
- nsecs_t expectedVSyncTime) {
- if (mTraceDetailedInfo) ATRACE_CALL();
- ALOGV("[%s] gatherCallbackInvocationsLocked @ %" PRId64, mName, ns2us(now));
-
- std::vector<CallbackInvocation> callbackInvocations;
- nsecs_t onePeriodAgo = now - mPeriod;
-
- for (auto& eventListener : mEventListeners) {
- nsecs_t t = computeListenerNextEventTimeLocked(eventListener, onePeriodAgo);
-
- if (t < now) {
- if (isCloseToPeriod(now - eventListener.mLastCallbackTime)) {
- eventListener.mLastEventTime = t;
- ALOGV("[%s] [%s] Skipping event due to model error", mName,
- eventListener.mName);
- continue;
- }
-
- CallbackInvocation ci;
- ci.mCallback = eventListener.mCallback;
- ci.mEventTime = t;
- ci.mExpectedVSyncTime = expectedVSyncTime;
- if (eventListener.mPhase < 0) {
- ci.mExpectedVSyncTime += mPeriod;
- }
- ALOGV("[%s] [%s] Preparing to fire, latency: %" PRId64, mName, eventListener.mName,
- t - eventListener.mLastEventTime);
- callbackInvocations.push_back(ci);
- eventListener.mLastEventTime = t;
- eventListener.mLastCallbackTime = now;
- }
- }
-
- return callbackInvocations;
- }
-
- nsecs_t computeListenerNextEventTimeLocked(const EventListener& listener, nsecs_t baseTime) {
- if (mTraceDetailedInfo) ATRACE_CALL();
- ALOGV("[%s] [%s] computeListenerNextEventTimeLocked(%" PRId64 ")", mName, listener.mName,
- ns2us(baseTime));
-
- nsecs_t lastEventTime = listener.mLastEventTime + mWakeupLatency;
- ALOGV("[%s] lastEventTime: %" PRId64, mName, ns2us(lastEventTime));
- if (baseTime < lastEventTime) {
- baseTime = lastEventTime;
- ALOGV("[%s] Clamping baseTime to lastEventTime -> %" PRId64, mName, ns2us(baseTime));
- }
-
- baseTime -= mReferenceTime;
- ALOGV("[%s] Relative baseTime = %" PRId64, mName, ns2us(baseTime));
- nsecs_t phase = mPhase + listener.mPhase;
- ALOGV("[%s] Phase = %" PRId64, mName, ns2us(phase));
- baseTime -= phase;
- ALOGV("[%s] baseTime - phase = %" PRId64, mName, ns2us(baseTime));
-
- // If our previous time is before the reference (because the reference
- // has since been updated), the division by mPeriod will truncate
- // towards zero instead of computing the floor. Since in all cases
- // before the reference we want the next time to be effectively now, we
- // set baseTime to -mPeriod so that numPeriods will be -1.
- // When we add 1 and the phase, we will be at the correct event time for
- // this period.
- if (baseTime < 0) {
- ALOGV("[%s] Correcting negative baseTime", mName);
- baseTime = -mPeriod;
- }
-
- nsecs_t numPeriods = baseTime / mPeriod;
- ALOGV("[%s] numPeriods = %" PRId64, mName, numPeriods);
- nsecs_t t = (numPeriods + 1) * mPeriod + phase;
- ALOGV("[%s] t = %" PRId64, mName, ns2us(t));
- t += mReferenceTime;
- ALOGV("[%s] Absolute t = %" PRId64, mName, ns2us(t));
-
- // Check that it's been slightly more than half a period since the last
- // event so that we don't accidentally fall into double-rate vsyncs
- if (isCloseToPeriod(t - listener.mLastEventTime)) {
- t += mPeriod;
- ALOGV("[%s] Modifying t -> %" PRId64, mName, ns2us(t));
- }
-
- t -= mWakeupLatency;
- ALOGV("[%s] Corrected for wakeup latency -> %" PRId64, mName, ns2us(t));
-
- return t;
- }
-
- void fireCallbackInvocations(const std::vector<CallbackInvocation>& callbacks) {
- if (mTraceDetailedInfo) ATRACE_CALL();
- for (size_t i = 0; i < callbacks.size(); i++) {
- callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime,
- callbacks[i].mExpectedVSyncTime);
- }
- }
-
- nsecs_t computeNextRefreshLocked(int periodOffset, nsecs_t now) const {
- nsecs_t phase = mReferenceTime + mPhase;
- if (mPeriod == 0) {
- return 0;
- }
- return (((now - phase) / mPeriod) + periodOffset + 1) * mPeriod + phase;
- }
-
- const char* const mName;
-
- bool mStop;
- TracedOrdinal<bool> mModelLocked;
-
- nsecs_t mPeriod;
- nsecs_t mPhase;
- nsecs_t mReferenceTime;
- nsecs_t mWakeupLatency;
-
- int64_t mFrameNumber;
-
- std::vector<EventListener> mEventListeners;
-
- mutable Mutex mMutex;
- Condition mCond;
-
- // Flag to turn on logging in systrace.
- const bool mTraceDetailedInfo;
-};
-
-#undef LOG_TAG
-#define LOG_TAG "DispSync"
-
-class ZeroPhaseTracer : public DispSync::Callback {
-public:
- ZeroPhaseTracer() : mParity("ZERO_PHASE_VSYNC", false) {}
-
- virtual void onDispSyncEvent(nsecs_t /*when*/, nsecs_t /*expectedVSyncTimestamp*/) {
- mParity = !mParity;
- }
-
-private:
- TracedOrdinal<bool> mParity;
-};
-
-DispSync::DispSync(const char* name, bool hasSyncFramework)
- : mName(name), mIgnorePresentFences(!hasSyncFramework) {
- // This flag offers the ability to turn on systrace logging from the shell.
- char value[PROPERTY_VALUE_MAX];
- property_get("debug.sf.dispsync_trace_detailed_info", value, "0");
- mTraceDetailedInfo = atoi(value);
-
- mThread = new DispSyncThread(name, mTraceDetailedInfo);
- mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
-
- // set DispSync to SCHED_FIFO to minimize jitter
- struct sched_param param = {0};
- param.sched_priority = 2;
- if (sched_setscheduler(mThread->getTid(), SCHED_FIFO, ¶m) != 0) {
- ALOGE("Couldn't set SCHED_FIFO for DispSyncThread");
- }
-
- beginResync();
-
- if (mTraceDetailedInfo && kEnableZeroPhaseTracer) {
- mZeroPhaseTracer = std::make_unique<ZeroPhaseTracer>();
- addEventListener("ZeroPhaseTracer", 0, mZeroPhaseTracer.get(), 0);
- }
-}
-
-DispSync::~DispSync() {
- mThread->stop();
- mThread->requestExitAndWait();
-}
-
-void DispSync::reset() {
- Mutex::Autolock lock(mMutex);
- resetLocked();
-}
-
-void DispSync::resetLocked() {
- mPhase = 0;
- const size_t lastSampleIdx = (mFirstResyncSample + mNumResyncSamples - 1) % MAX_RESYNC_SAMPLES;
- // Keep the most recent sample, when we resync to hardware we'll overwrite this
- // with a more accurate signal
- if (mResyncSamples[lastSampleIdx] != 0) {
- mReferenceTime = mResyncSamples[lastSampleIdx];
- }
- mModelUpdated = false;
- for (size_t i = 0; i < MAX_RESYNC_SAMPLES; i++) {
- mResyncSamples[i] = 0;
- }
- mNumResyncSamples = 0;
- mFirstResyncSample = 0;
- mNumResyncSamplesSincePresent = 0;
- mThread->unlockModel();
- resetErrorLocked();
-}
-
-bool DispSync::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) {
- Mutex::Autolock lock(mMutex);
-
- if (mIgnorePresentFences) {
- return true;
- }
-
- mPresentFences[mPresentSampleOffset] = fenceTime;
- mPresentSampleOffset = (mPresentSampleOffset + 1) % NUM_PRESENT_SAMPLES;
- mNumResyncSamplesSincePresent = 0;
-
- updateErrorLocked();
-
- return !mModelUpdated || mError > kErrorThreshold;
-}
-
-void DispSync::beginResync() {
- Mutex::Autolock lock(mMutex);
- ALOGV("[%s] beginResync", mName);
- resetLocked();
-}
-
-bool DispSync::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> /*hwcVsyncPeriod*/,
- bool* periodFlushed) {
- Mutex::Autolock lock(mMutex);
-
- ALOGV("[%s] addResyncSample(%" PRId64 ")", mName, ns2us(timestamp));
-
- *periodFlushed = false;
- const size_t idx = (mFirstResyncSample + mNumResyncSamples) % MAX_RESYNC_SAMPLES;
- mResyncSamples[idx] = timestamp;
- if (mNumResyncSamples == 0) {
- mPhase = 0;
- ALOGV("[%s] First resync sample: mPeriod = %" PRId64 ", mPhase = 0, "
- "mReferenceTime = %" PRId64,
- mName, ns2us(mPeriod), ns2us(timestamp));
- } else if (mPendingPeriod > 0) {
- // mNumResyncSamples > 0, so priorIdx won't overflow
- const size_t priorIdx = (mFirstResyncSample + mNumResyncSamples - 1) % MAX_RESYNC_SAMPLES;
- const nsecs_t lastTimestamp = mResyncSamples[priorIdx];
-
- const nsecs_t observedVsync = std::abs(timestamp - lastTimestamp);
- if (std::abs(observedVsync - mPendingPeriod) <= std::abs(observedVsync - mIntendedPeriod)) {
- // Either the observed vsync is closer to the pending period, (and
- // thus we detected a period change), or the period change will
- // no-op. In either case, reset the model and flush the pending
- // period.
- resetLocked();
- mIntendedPeriod = mPendingPeriod;
- mPeriod = mPendingPeriod;
- mPendingPeriod = 0;
- if (mTraceDetailedInfo) {
- ATRACE_INT("DispSync:PendingPeriod", mPendingPeriod);
- ATRACE_INT("DispSync:IntendedPeriod", mIntendedPeriod);
- }
- *periodFlushed = true;
- }
- }
- // Always update the reference time with the most recent timestamp.
- mReferenceTime = timestamp;
- mThread->updateModel(mPeriod, mPhase, mReferenceTime);
-
- if (mNumResyncSamples < MAX_RESYNC_SAMPLES) {
- mNumResyncSamples++;
- } else {
- mFirstResyncSample = (mFirstResyncSample + 1) % MAX_RESYNC_SAMPLES;
- }
-
- updateModelLocked();
-
- if (mNumResyncSamplesSincePresent++ > MAX_RESYNC_SAMPLES_WITHOUT_PRESENT) {
- resetErrorLocked();
- }
-
- if (mIgnorePresentFences) {
- // If we're ignoring the present fences we have no way to know whether
- // or not we're synchronized with the HW vsyncs, so we just request
- // that the HW vsync events be turned on.
- return true;
- }
-
- // Check against kErrorThreshold / 2 to add some hysteresis before having to
- // resync again
- bool modelLocked = mModelUpdated && mError < (kErrorThreshold / 2) && mPendingPeriod == 0;
- ALOGV("[%s] addResyncSample returning %s", mName, modelLocked ? "locked" : "unlocked");
- if (modelLocked) {
- *periodFlushed = true;
- mThread->lockModel();
- }
- return !modelLocked;
-}
-
-void DispSync::endResync() {
- mThread->lockModel();
-}
-
-status_t DispSync::addEventListener(const char* name, nsecs_t phase, Callback* callback,
- nsecs_t lastCallbackTime) {
- Mutex::Autolock lock(mMutex);
- return mThread->addEventListener(name, phase, callback, lastCallbackTime);
-}
-
-status_t DispSync::removeEventListener(Callback* callback, nsecs_t* outLastCallbackTime) {
- Mutex::Autolock lock(mMutex);
- return mThread->removeEventListener(callback, outLastCallbackTime);
-}
-
-status_t DispSync::changePhaseOffset(Callback* callback, nsecs_t phase) {
- Mutex::Autolock lock(mMutex);
- return mThread->changePhaseOffset(callback, phase);
-}
-
-void DispSync::setPeriod(nsecs_t period) {
- Mutex::Autolock lock(mMutex);
-
- const bool pendingPeriodShouldChange =
- period != mIntendedPeriod || (period == mIntendedPeriod && mPendingPeriod != 0);
-
- if (pendingPeriodShouldChange) {
- mPendingPeriod = period;
- }
- if (mTraceDetailedInfo) {
- ATRACE_INT("DispSync:IntendedPeriod", mIntendedPeriod);
- ATRACE_INT("DispSync:PendingPeriod", mPendingPeriod);
- }
-}
-
-nsecs_t DispSync::getPeriod() {
- // lock mutex as mPeriod changes multiple times in updateModelLocked
- Mutex::Autolock lock(mMutex);
- return mPeriod;
-}
-
-void DispSync::updateModelLocked() {
- ALOGV("[%s] updateModelLocked %zu", mName, mNumResyncSamples);
- if (mNumResyncSamples >= MIN_RESYNC_SAMPLES_FOR_UPDATE) {
- ALOGV("[%s] Computing...", mName);
- nsecs_t durationSum = 0;
- nsecs_t minDuration = INT64_MAX;
- nsecs_t maxDuration = 0;
- // We skip the first 2 samples because the first vsync duration on some
- // devices may be much more inaccurate than on other devices, e.g. due
- // to delays in ramping up from a power collapse. By doing so this
- // actually increases the accuracy of the DispSync model even though
- // we're effectively relying on fewer sample points.
- static constexpr size_t numSamplesSkipped = 2;
- for (size_t i = numSamplesSkipped; i < mNumResyncSamples; i++) {
- size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
- size_t prev = (idx + MAX_RESYNC_SAMPLES - 1) % MAX_RESYNC_SAMPLES;
- nsecs_t duration = mResyncSamples[idx] - mResyncSamples[prev];
- durationSum += duration;
- minDuration = min(minDuration, duration);
- maxDuration = max(maxDuration, duration);
- }
-
- // Exclude the min and max from the average
- durationSum -= minDuration + maxDuration;
- mPeriod = durationSum / (mNumResyncSamples - numSamplesSkipped - 2);
-
- ALOGV("[%s] mPeriod = %" PRId64, mName, ns2us(mPeriod));
-
- double sampleAvgX = 0;
- double sampleAvgY = 0;
- double scale = 2.0 * M_PI / double(mPeriod);
- for (size_t i = numSamplesSkipped; i < mNumResyncSamples; i++) {
- size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
- nsecs_t sample = mResyncSamples[idx] - mReferenceTime;
- double samplePhase = double(sample % mPeriod) * scale;
- sampleAvgX += cos(samplePhase);
- sampleAvgY += sin(samplePhase);
- }
-
- sampleAvgX /= double(mNumResyncSamples - numSamplesSkipped);
- sampleAvgY /= double(mNumResyncSamples - numSamplesSkipped);
-
- mPhase = nsecs_t(atan2(sampleAvgY, sampleAvgX) / scale);
-
- ALOGV("[%s] mPhase = %" PRId64, mName, ns2us(mPhase));
-
- if (mPhase < -(mPeriod / 2)) {
- mPhase += mPeriod;
- ALOGV("[%s] Adjusting mPhase -> %" PRId64, mName, ns2us(mPhase));
- }
-
- mThread->updateModel(mPeriod, mPhase, mReferenceTime);
- mModelUpdated = true;
- }
-}
-
-void DispSync::updateErrorLocked() {
- if (!mModelUpdated) {
- return;
- }
-
- int numErrSamples = 0;
- nsecs_t sqErrSum = 0;
-
- for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
- // Only check for the cached value of signal time to avoid unecessary
- // syscalls. It is the responsibility of the DispSync owner to
- // call getSignalTime() periodically so the cache is updated when the
- // fence signals.
- nsecs_t time = mPresentFences[i]->getCachedSignalTime();
- if (time == Fence::SIGNAL_TIME_PENDING || time == Fence::SIGNAL_TIME_INVALID) {
- continue;
- }
-
- nsecs_t sample = time - mReferenceTime;
- if (sample <= mPhase) {
- continue;
- }
-
- nsecs_t sampleErr = (sample - mPhase) % mPeriod;
- if (sampleErr > mPeriod / 2) {
- sampleErr -= mPeriod;
- }
- sqErrSum += sampleErr * sampleErr;
- numErrSamples++;
- }
-
- if (numErrSamples > 0) {
- mError = sqErrSum / numErrSamples;
- mZeroErrSamplesCount = 0;
- } else {
- mError = 0;
- // Use mod ACCEPTABLE_ZERO_ERR_SAMPLES_COUNT to avoid log spam.
- mZeroErrSamplesCount++;
- ALOGE_IF((mZeroErrSamplesCount % ACCEPTABLE_ZERO_ERR_SAMPLES_COUNT) == 0,
- "No present times for model error.");
- }
-
- if (mTraceDetailedInfo) {
- ATRACE_INT64("DispSync:Error", mError);
- }
-}
-
-void DispSync::resetErrorLocked() {
- mPresentSampleOffset = 0;
- mError = 0;
- mZeroErrSamplesCount = 0;
- if (mTraceDetailedInfo) {
- ATRACE_INT64("DispSync:Error", mError);
- }
- for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
- mPresentFences[i] = FenceTime::NO_FENCE;
- }
-}
-
-nsecs_t DispSync::computeNextRefresh(int periodOffset, nsecs_t now) const {
- Mutex::Autolock lock(mMutex);
- nsecs_t phase = mReferenceTime + mPhase;
- if (mPeriod == 0) {
- return 0;
- }
- return (((now - phase) / mPeriod) + periodOffset + 1) * mPeriod + phase;
-}
-
-void DispSync::setIgnorePresentFences(bool ignore) {
- Mutex::Autolock lock(mMutex);
- if (mIgnorePresentFences != ignore) {
- mIgnorePresentFences = ignore;
- resetLocked();
- }
-}
-
-void DispSync::dump(std::string& result) const {
- Mutex::Autolock lock(mMutex);
- StringAppendF(&result, "present fences are %s\n", mIgnorePresentFences ? "ignored" : "used");
- StringAppendF(&result, "mPeriod: %" PRId64 " ns (%.3f fps)\n", mPeriod, 1000000000.0 / mPeriod);
- StringAppendF(&result, "mPhase: %" PRId64 " ns\n", mPhase);
- StringAppendF(&result, "mError: %" PRId64 " ns (sqrt=%.1f)\n", mError, sqrt(mError));
- StringAppendF(&result, "mNumResyncSamplesSincePresent: %d (limit %d)\n",
- mNumResyncSamplesSincePresent, MAX_RESYNC_SAMPLES_WITHOUT_PRESENT);
- StringAppendF(&result, "mNumResyncSamples: %zd (max %d)\n", mNumResyncSamples,
- MAX_RESYNC_SAMPLES);
-
- result.append("mResyncSamples:\n");
- nsecs_t previous = -1;
- for (size_t i = 0; i < mNumResyncSamples; i++) {
- size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
- nsecs_t sampleTime = mResyncSamples[idx];
- if (i == 0) {
- StringAppendF(&result, " %" PRId64 "\n", sampleTime);
- } else {
- StringAppendF(&result, " %" PRId64 " (+%" PRId64 ")\n", sampleTime,
- sampleTime - previous);
- }
- previous = sampleTime;
- }
-
- StringAppendF(&result, "mPresentFences [%d]:\n", NUM_PRESENT_SAMPLES);
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- previous = Fence::SIGNAL_TIME_INVALID;
- for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
- size_t idx = (i + mPresentSampleOffset) % NUM_PRESENT_SAMPLES;
- nsecs_t presentTime = mPresentFences[idx]->getSignalTime();
- if (presentTime == Fence::SIGNAL_TIME_PENDING) {
- StringAppendF(&result, " [unsignaled fence]\n");
- } else if (presentTime == Fence::SIGNAL_TIME_INVALID) {
- StringAppendF(&result, " [invalid fence]\n");
- } else if (previous == Fence::SIGNAL_TIME_PENDING ||
- previous == Fence::SIGNAL_TIME_INVALID) {
- StringAppendF(&result, " %" PRId64 " (%.3f ms ago)\n", presentTime,
- (now - presentTime) / 1000000.0);
- } else {
- StringAppendF(&result, " %" PRId64 " (+%" PRId64 " / %.3f) (%.3f ms ago)\n",
- presentTime, presentTime - previous,
- (presentTime - previous) / (double)mPeriod,
- (now - presentTime) / 1000000.0);
- }
- previous = presentTime;
- }
-
- StringAppendF(&result, "current monotonic time: %" PRId64 "\n", now);
-}
-
-nsecs_t DispSync::expectedPresentTime(nsecs_t now) {
- // The HWC doesn't currently have a way to report additional latency.
- // Assume that whatever we submit now will appear right after the flip.
- // For a smart panel this might be 1. This is expressed in frames,
- // rather than time, because we expect to have a constant frame delay
- // regardless of the refresh rate.
- const uint32_t hwcLatency = 0;
-
- // Ask DispSync when the next refresh will be (CLOCK_MONOTONIC).
- return mThread->computeNextRefresh(hwcLatency, now);
-}
-
-} // namespace impl
-
-} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/DispSync.h b/services/surfaceflinger/Scheduler/DispSync.h
deleted file mode 100644
index 6fb5654..0000000
--- a/services/surfaceflinger/Scheduler/DispSync.h
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * Copyright (C) 2012 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.
- */
-
-#pragma once
-
-#include <stddef.h>
-
-#include <utils/Mutex.h>
-#include <utils/RefBase.h>
-#include <utils/Timers.h>
-
-#include <ui/FenceTime.h>
-
-#include <memory>
-
-namespace android {
-
-class FenceTime;
-
-class DispSync {
-public:
- class Callback {
- public:
- Callback() = default;
- virtual ~Callback();
- virtual void onDispSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) = 0;
-
- protected:
- Callback(Callback const&) = delete;
- Callback& operator=(Callback const&) = delete;
- };
-
- DispSync() = default;
- virtual ~DispSync();
-
- virtual void reset() = 0;
- virtual bool addPresentFence(const std::shared_ptr<FenceTime>&) = 0;
- virtual void beginResync() = 0;
- virtual bool addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
- bool* periodFlushed) = 0;
- virtual void endResync() = 0;
- virtual void setPeriod(nsecs_t period) = 0;
- virtual nsecs_t getPeriod() = 0;
- virtual status_t addEventListener(const char* name, nsecs_t phase, Callback* callback,
- nsecs_t lastCallbackTime) = 0;
- virtual status_t removeEventListener(Callback* callback, nsecs_t* outLastCallback) = 0;
- virtual status_t changePhaseOffset(Callback* callback, nsecs_t phase) = 0;
- virtual nsecs_t computeNextRefresh(int periodOffset, nsecs_t now) const = 0;
- virtual void setIgnorePresentFences(bool ignore) = 0;
- virtual nsecs_t expectedPresentTime(nsecs_t now) = 0;
-
- virtual void dump(std::string& result) const = 0;
-
-protected:
- DispSync(DispSync const&) = delete;
- DispSync& operator=(DispSync const&) = delete;
-};
-
-namespace impl {
-
-class DispSyncThread;
-
-// DispSync maintains a model of the periodic hardware-based vsync events of a
-// display and uses that model to execute period callbacks at specific phase
-// offsets from the hardware vsync events. The model is constructed by
-// feeding consecutive hardware event timestamps to the DispSync object via
-// the addResyncSample method.
-//
-// The model is validated using timestamps from Fence objects that are passed
-// to the DispSync object via the addPresentFence method. These fence
-// timestamps should correspond to a hardware vsync event, but they need not
-// be consecutive hardware vsync times. If this method determines that the
-// current model accurately represents the hardware event times it will return
-// false to indicate that a resynchronization (via addResyncSample) is not
-// needed.
-class DispSync : public android::DispSync {
-public:
- // hasSyncFramework specifies whether the platform supports present fences.
- DispSync(const char* name, bool hasSyncFramework);
- ~DispSync() override;
-
- // reset clears the resync samples and error value.
- void reset() override;
-
- // addPresentFence adds a fence for use in validating the current vsync
- // event model. The fence need not be signaled at the time
- // addPresentFence is called. When the fence does signal, its timestamp
- // should correspond to a hardware vsync event. Unlike the
- // addResyncSample method, the timestamps of consecutive fences need not
- // correspond to consecutive hardware vsync events.
- //
- // This method should be called with the retire fence from each HWComposer
- // set call that affects the display.
- bool addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) override;
-
- // The beginResync, addResyncSample, and endResync methods are used to re-
- // synchronize the DispSync's model to the hardware vsync events. The re-
- // synchronization process involves first calling beginResync, then
- // calling addResyncSample with a sequence of consecutive hardware vsync
- // event timestamps, and finally calling endResync when addResyncSample
- // indicates that no more samples are needed by returning false.
- //
- // This resynchronization process should be performed whenever the display
- // is turned on (i.e. once immediately after it's turned on) and whenever
- // addPresentFence returns true indicating that the model has drifted away
- // from the hardware vsync events.
- void beginResync() override;
- // Adds a vsync sample to the dispsync model. The timestamp is the time
- // of the vsync event that fired. periodFlushed will return true if the
- // vsync period was detected to have changed to mPendingPeriod.
- //
- // This method will return true if more vsync samples are needed to lock
- // down the DispSync model, and false otherwise.
- // periodFlushed will be set to true if mPendingPeriod is flushed to
- // mIntendedPeriod, and false otherwise.
- bool addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
- bool* periodFlushed) override;
- void endResync() override;
-
- // The setPeriod method sets the vsync event model's period to a specific
- // value. This should be used to prime the model when a display is first
- // turned on, or when a refresh rate change is requested.
- void setPeriod(nsecs_t period) override;
-
- // The getPeriod method returns the current vsync period.
- nsecs_t getPeriod() override;
-
- // addEventListener registers a callback to be called repeatedly at the
- // given phase offset from the hardware vsync events. The callback is
- // called from a separate thread and it should return reasonably quickly
- // (i.e. within a few hundred microseconds).
- // If the callback was previously registered, and the last clock time the
- // callback was invoked was known to the caller (e.g. via removeEventListener),
- // then the caller may pass that through to lastCallbackTime, so that
- // callbacks do not accidentally double-fire if they are unregistered and
- // reregistered in rapid succession.
- status_t addEventListener(const char* name, nsecs_t phase, Callback* callback,
- nsecs_t lastCallbackTime) override;
-
- // removeEventListener removes an already-registered event callback. Once
- // this method returns that callback will no longer be called by the
- // DispSync object.
- // outLastCallbackTime will contain the last time that the callback was invoked.
- // If the caller wishes to reregister the same callback, they should pass the
- // callback time back into lastCallbackTime (see addEventListener).
- status_t removeEventListener(Callback* callback, nsecs_t* outLastCallbackTime) override;
-
- // changePhaseOffset changes the phase offset of an already-registered event callback. The
- // method will make sure that there is no skipping or double-firing on the listener per frame,
- // even when changing the offsets multiple times.
- status_t changePhaseOffset(Callback* callback, nsecs_t phase) override;
-
- // computeNextRefresh computes when the next refresh is expected to begin.
- // The periodOffset value can be used to move forward or backward; an
- // offset of zero is the next refresh, -1 is the previous refresh, 1 is
- // the refresh after next. etc.
- nsecs_t computeNextRefresh(int periodOffset, nsecs_t now) const override;
-
- // In certain situations the present fences aren't a good indicator of vsync
- // time, e.g. when vr flinger is active, or simply aren't available,
- // e.g. when the sync framework isn't present. Use this method to toggle
- // whether or not DispSync ignores present fences. If present fences are
- // ignored, DispSync will always ask for hardware vsync events by returning
- // true from addPresentFence() and addResyncSample().
- void setIgnorePresentFences(bool ignore) override;
-
- // Determine the expected present time when a buffer acquired now will be displayed.
- nsecs_t expectedPresentTime(nsecs_t now);
-
- // dump appends human-readable debug info to the result string.
- void dump(std::string& result) const override;
-
-private:
- void updateModelLocked();
- void updateErrorLocked();
- void resetLocked();
- void resetErrorLocked();
-
- enum { MAX_RESYNC_SAMPLES = 32 };
- enum { MIN_RESYNC_SAMPLES_FOR_UPDATE = 6 };
- enum { NUM_PRESENT_SAMPLES = 8 };
- enum { MAX_RESYNC_SAMPLES_WITHOUT_PRESENT = 4 };
- enum { ACCEPTABLE_ZERO_ERR_SAMPLES_COUNT = 64 };
-
- const char* const mName;
-
- // mPeriod is the computed period of the modeled vsync events in
- // nanoseconds.
- nsecs_t mPeriod;
-
- // mIntendedPeriod is the intended period of the modeled vsync events in
- // nanoseconds. Under ideal conditions this should be similar if not the
- // same as mPeriod, plus or minus an observed error.
- nsecs_t mIntendedPeriod = 0;
-
- // mPendingPeriod is the proposed period change in nanoseconds.
- // If mPendingPeriod differs from mPeriod and is nonzero, it will
- // be flushed to mPeriod when we detect that the hardware switched
- // vsync frequency.
- nsecs_t mPendingPeriod = 0;
-
- // mPhase is the phase offset of the modeled vsync events. It is the
- // number of nanoseconds from time 0 to the first vsync event.
- nsecs_t mPhase;
-
- // mReferenceTime is the reference time of the modeled vsync events.
- // It is the nanosecond timestamp of the first vsync event after a resync.
- nsecs_t mReferenceTime;
-
- // mError is the computed model error. It is based on the difference
- // between the estimated vsync event times and those observed in the
- // mPresentFences array.
- nsecs_t mError;
-
- // mZeroErrSamplesCount keeps track of how many times in a row there were
- // zero timestamps available in the mPresentFences array.
- // Used to check that we are able to calculate the model error.
- size_t mZeroErrSamplesCount;
-
- // Whether we have updated the vsync event model since the last resync.
- bool mModelUpdated;
-
- // These member variables are the state used during the resynchronization
- // process to store information about the hardware vsync event times used
- // to compute the model.
- nsecs_t mResyncSamples[MAX_RESYNC_SAMPLES] = {0};
- size_t mFirstResyncSample = 0;
- size_t mNumResyncSamples = 0;
- int mNumResyncSamplesSincePresent;
-
- // These member variables store information about the present fences used
- // to validate the currently computed model.
- std::shared_ptr<FenceTime> mPresentFences[NUM_PRESENT_SAMPLES]{FenceTime::NO_FENCE};
- size_t mPresentSampleOffset;
-
- // mThread is the thread from which all the callbacks are called.
- sp<DispSyncThread> mThread;
-
- // mMutex is used to protect access to all member variables.
- mutable Mutex mMutex;
-
- // Ignore present (retire) fences if the device doesn't have support for the
- // sync framework
- bool mIgnorePresentFences;
-
- std::unique_ptr<Callback> mZeroPhaseTracer;
-
- // Flag to turn on logging in systrace.
- bool mTraceDetailedInfo = false;
-};
-
-} // namespace impl
-
-} // namespace android
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
index 8752b66..ce5c31a 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "DispSyncSource.h"
@@ -26,37 +22,129 @@
#include <utils/Trace.h>
#include <mutex>
-#include "DispSync.h"
#include "EventThread.h"
+#include "VsyncController.h"
-namespace android {
+namespace android::scheduler {
using base::StringAppendF;
+using namespace std::chrono_literals;
-DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync,
+class CallbackRepeater {
+public:
+ CallbackRepeater(VSyncDispatch& dispatch, VSyncDispatch::Callback cb, const char* name,
+ std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration,
+ std::chrono::nanoseconds notBefore)
+ : mName(name),
+ mCallback(cb),
+ mRegistration(dispatch,
+ std::bind(&CallbackRepeater::callback, this, std::placeholders::_1,
+ std::placeholders::_2, std::placeholders::_3),
+ mName),
+ mStarted(false),
+ mWorkDuration(workDuration),
+ mReadyDuration(readyDuration),
+ mLastCallTime(notBefore) {}
+
+ ~CallbackRepeater() {
+ std::lock_guard lock(mMutex);
+ mRegistration.cancel();
+ }
+
+ void start(std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration) {
+ std::lock_guard lock(mMutex);
+ mStarted = true;
+ mWorkDuration = workDuration;
+ mReadyDuration = readyDuration;
+
+ auto const scheduleResult =
+ mRegistration.schedule({.workDuration = mWorkDuration.count(),
+ .readyDuration = mReadyDuration.count(),
+ .earliestVsync = mLastCallTime.count()});
+ LOG_ALWAYS_FATAL_IF((scheduleResult != scheduler::ScheduleResult::Scheduled),
+ "Error scheduling callback: rc %X", scheduleResult);
+ }
+
+ void stop() {
+ std::lock_guard lock(mMutex);
+ LOG_ALWAYS_FATAL_IF(!mStarted, "DispSyncInterface misuse: callback already stopped");
+ mStarted = false;
+ mRegistration.cancel();
+ }
+
+ void dump(std::string& result) const {
+ std::lock_guard lock(mMutex);
+ const auto relativeLastCallTime =
+ mLastCallTime - std::chrono::steady_clock::now().time_since_epoch();
+ StringAppendF(&result, "\t%s: ", mName.c_str());
+ StringAppendF(&result, "mWorkDuration=%.2f mReadyDuration=%.2f last vsync time ",
+ mWorkDuration.count() / 1e6f, mReadyDuration.count() / 1e6f);
+ StringAppendF(&result, "%.2fms relative to now (%s)\n", relativeLastCallTime.count() / 1e6f,
+ mStarted ? "running" : "stopped");
+ }
+
+private:
+ void callback(nsecs_t vsyncTime, nsecs_t wakeupTime, nsecs_t readyTime) {
+ {
+ std::lock_guard lock(mMutex);
+ mLastCallTime = std::chrono::nanoseconds(vsyncTime);
+ }
+
+ mCallback(vsyncTime, wakeupTime, readyTime);
+
+ {
+ std::lock_guard lock(mMutex);
+ if (!mStarted) {
+ return;
+ }
+ auto const scheduleResult =
+ mRegistration.schedule({.workDuration = mWorkDuration.count(),
+ .readyDuration = mReadyDuration.count(),
+ .earliestVsync = vsyncTime});
+ LOG_ALWAYS_FATAL_IF((scheduleResult != ScheduleResult::Scheduled),
+ "Error rescheduling callback: rc %X", scheduleResult);
+ }
+ }
+
+ const std::string mName;
+ scheduler::VSyncDispatch::Callback mCallback;
+
+ mutable std::mutex mMutex;
+ VSyncCallbackRegistration mRegistration GUARDED_BY(mMutex);
+ bool mStarted GUARDED_BY(mMutex) = false;
+ std::chrono::nanoseconds mWorkDuration GUARDED_BY(mMutex) = 0ns;
+ std::chrono::nanoseconds mReadyDuration GUARDED_BY(mMutex) = 0ns;
+ std::chrono::nanoseconds mLastCallTime GUARDED_BY(mMutex) = 0ns;
+};
+
+DispSyncSource::DispSyncSource(scheduler::VSyncDispatch& vSyncDispatch,
+ std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration, bool traceVsync,
const char* name)
: mName(name),
mValue(base::StringPrintf("VSYNC-%s", name), 0),
mTraceVsync(traceVsync),
mVsyncOnLabel(base::StringPrintf("VsyncOn-%s", name)),
- mDispSync(dispSync),
- mPhaseOffset(base::StringPrintf("VsyncOffset-%s", name), phaseOffset) {}
+ mWorkDuration(base::StringPrintf("VsyncWorkDuration-%s", name), workDuration),
+ mReadyDuration(readyDuration) {
+ mCallbackRepeater =
+ std::make_unique<CallbackRepeater>(vSyncDispatch,
+ std::bind(&DispSyncSource::onVsyncCallback, this,
+ std::placeholders::_1,
+ std::placeholders::_2,
+ std::placeholders::_3),
+ name, workDuration, readyDuration,
+ std::chrono::steady_clock::now().time_since_epoch());
+}
+
+DispSyncSource::~DispSyncSource() = default;
void DispSyncSource::setVSyncEnabled(bool enable) {
std::lock_guard lock(mVsyncMutex);
if (enable) {
- status_t err = mDispSync->addEventListener(mName, mPhaseOffset,
- static_cast<DispSync::Callback*>(this),
- mLastCallbackTime);
- if (err != NO_ERROR) {
- ALOGE("error registering vsync callback: %s (%d)", strerror(-err), err);
- }
+ mCallbackRepeater->start(mWorkDuration, mReadyDuration);
// ATRACE_INT(mVsyncOnLabel.c_str(), 1);
} else {
- status_t err = mDispSync->removeEventListener(static_cast<DispSync::Callback*>(this),
- &mLastCallbackTime);
- if (err != NO_ERROR) {
- ALOGE("error unregistering vsync callback: %s (%d)", strerror(-err), err);
- }
+ mCallbackRepeater->stop();
// ATRACE_INT(mVsyncOnLabel.c_str(), 0);
}
mEnabled = enable;
@@ -67,32 +155,22 @@
mCallback = callback;
}
-void DispSyncSource::setPhaseOffset(nsecs_t phaseOffset) {
+void DispSyncSource::setDuration(std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration) {
std::lock_guard lock(mVsyncMutex);
- const nsecs_t period = mDispSync->getPeriod();
-
- // Normalize phaseOffset to [-period, period)
- const int numPeriods = phaseOffset / period;
- phaseOffset -= numPeriods * period;
- if (mPhaseOffset == phaseOffset) {
- return;
- }
-
- mPhaseOffset = phaseOffset;
+ mWorkDuration = workDuration;
+ mReadyDuration = readyDuration;
// If we're not enabled, we don't need to mess with the listeners
if (!mEnabled) {
return;
}
- status_t err =
- mDispSync->changePhaseOffset(static_cast<DispSync::Callback*>(this), mPhaseOffset);
- if (err != NO_ERROR) {
- ALOGE("error changing vsync offset: %s (%d)", strerror(-err), err);
- }
+ mCallbackRepeater->start(mWorkDuration, mReadyDuration);
}
-void DispSyncSource::onDispSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) {
+void DispSyncSource::onVsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime,
+ nsecs_t readyTime) {
VSyncSource::Callback* callback;
{
std::lock_guard lock(mCallbackMutex);
@@ -104,17 +182,13 @@
}
if (callback != nullptr) {
- callback->onVSyncEvent(when, expectedVSyncTimestamp);
+ callback->onVSyncEvent(targetWakeupTime, vsyncTime, readyTime);
}
}
void DispSyncSource::dump(std::string& result) const {
std::lock_guard lock(mVsyncMutex);
StringAppendF(&result, "DispSyncSource: %s(%s)\n", mName, mEnabled ? "enabled" : "disabled");
- mDispSync->dump(result);
}
-} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.h b/services/surfaceflinger/Scheduler/DispSyncSource.h
index 2aee3f6..2fce235 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.h
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.h
@@ -18,45 +18,47 @@
#include <mutex>
#include <string>
-#include "DispSync.h"
#include "EventThread.h"
#include "TracedOrdinal.h"
+#include "VSyncDispatch.h"
-namespace android {
+namespace android::scheduler {
+class CallbackRepeater;
-class DispSyncSource final : public VSyncSource, private DispSync::Callback {
+class DispSyncSource final : public VSyncSource {
public:
- DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync, const char* name);
+ DispSyncSource(VSyncDispatch& vSyncDispatch, std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration, bool traceVsync, const char* name);
- ~DispSyncSource() override = default;
+ ~DispSyncSource() override;
// The following methods are implementation of VSyncSource.
const char* getName() const override { return mName; }
void setVSyncEnabled(bool enable) override;
void setCallback(VSyncSource::Callback* callback) override;
- void setPhaseOffset(nsecs_t phaseOffset) override;
+ void setDuration(std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration) override;
void dump(std::string&) const override;
private:
- // The following method is the implementation of the DispSync::Callback.
- void onDispSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) override;
+ void onVsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime);
const char* const mName;
TracedOrdinal<int> mValue;
const bool mTraceVsync;
const std::string mVsyncOnLabel;
- nsecs_t mLastCallbackTime GUARDED_BY(mVsyncMutex) = 0;
- DispSync* mDispSync;
+ std::unique_ptr<CallbackRepeater> mCallbackRepeater;
std::mutex mCallbackMutex;
VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr;
mutable std::mutex mVsyncMutex;
- TracedOrdinal<nsecs_t> mPhaseOffset GUARDED_BY(mVsyncMutex);
+ TracedOrdinal<std::chrono::nanoseconds> mWorkDuration GUARDED_BY(mVsyncMutex);
+ std::chrono::nanoseconds mReadyDuration GUARDED_BY(mVsyncMutex);
bool mEnabled GUARDED_BY(mVsyncMutex) = false;
};
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/EventControlThread.cpp b/services/surfaceflinger/Scheduler/EventControlThread.cpp
deleted file mode 100644
index 7f9db9c..0000000
--- a/services/surfaceflinger/Scheduler/EventControlThread.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#include <pthread.h>
-#include <sched.h>
-#include <sys/resource.h>
-
-#include <cutils/sched_policy.h>
-#include <log/log.h>
-#include <system/thread_defs.h>
-
-#include "EventControlThread.h"
-
-namespace android {
-
-EventControlThread::~EventControlThread() = default;
-
-namespace impl {
-
-EventControlThread::EventControlThread(EventControlThread::SetVSyncEnabledFunction function)
- : mSetVSyncEnabled(std::move(function)) {
- pthread_setname_np(mThread.native_handle(), "EventControlThread");
-
- pid_t tid = pthread_gettid_np(mThread.native_handle());
- setpriority(PRIO_PROCESS, tid, ANDROID_PRIORITY_URGENT_DISPLAY);
- set_sched_policy(tid, SP_FOREGROUND);
-}
-
-EventControlThread::~EventControlThread() {
- {
- std::lock_guard<std::mutex> lock(mMutex);
- mKeepRunning = false;
- mCondition.notify_all();
- }
- mThread.join();
-}
-
-void EventControlThread::setVsyncEnabled(bool enabled) {
- std::lock_guard<std::mutex> lock(mMutex);
- mVsyncEnabled = enabled;
- mCondition.notify_all();
-}
-
-// Unfortunately std::unique_lock gives warnings with -Wthread-safety
-void EventControlThread::threadMain() NO_THREAD_SAFETY_ANALYSIS {
- auto keepRunning = true;
- auto currentVsyncEnabled = false;
-
- while (keepRunning) {
- mSetVSyncEnabled(currentVsyncEnabled);
-
- std::unique_lock<std::mutex> lock(mMutex);
- mCondition.wait(lock, [this, currentVsyncEnabled, keepRunning]() NO_THREAD_SAFETY_ANALYSIS {
- return currentVsyncEnabled != mVsyncEnabled || keepRunning != mKeepRunning;
- });
- currentVsyncEnabled = mVsyncEnabled;
- keepRunning = mKeepRunning;
- }
-}
-
-} // namespace impl
-} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/EventControlThread.h b/services/surfaceflinger/Scheduler/EventControlThread.h
deleted file mode 100644
index cafae53..0000000
--- a/services/surfaceflinger/Scheduler/EventControlThread.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-#pragma once
-
-#include <condition_variable>
-#include <cstddef>
-#include <functional>
-#include <mutex>
-#include <thread>
-
-#include <android-base/thread_annotations.h>
-
-namespace android {
-
-class EventControlThread {
-public:
- virtual ~EventControlThread();
-
- virtual void setVsyncEnabled(bool enabled) = 0;
-};
-
-namespace impl {
-
-class EventControlThread final : public android::EventControlThread {
-public:
- using SetVSyncEnabledFunction = std::function<void(bool)>;
-
- explicit EventControlThread(SetVSyncEnabledFunction function);
- ~EventControlThread();
-
- // EventControlThread implementation
- void setVsyncEnabled(bool enabled) override;
-
-private:
- void threadMain();
-
- std::mutex mMutex;
- std::condition_variable mCondition;
-
- const SetVSyncEnabledFunction mSetVSyncEnabled;
- bool mVsyncEnabled GUARDED_BY(mMutex) = false;
- bool mKeepRunning GUARDED_BY(mMutex) = true;
-
- // Must be last so that everything is initialized before the thread starts.
- std::thread mThread{&EventControlThread::threadMain, this};
-};
-
-} // namespace impl
-} // namespace android
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 845bf50..2321e2d 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -31,6 +31,8 @@
#include <android-base/stringprintf.h>
+#include <binder/IPCThreadState.h>
+
#include <cutils/compiler.h>
#include <cutils/sched_policy.h>
@@ -39,8 +41,13 @@
#include <utils/Errors.h>
#include <utils/Trace.h>
+#include "DisplayHardware/DisplayMode.h"
+#include "FrameTimeline.h"
+
#include "EventThread.h"
-#include "HwcStrongTypes.h"
+
+#undef LOG_TAG
+#define LOG_TAG "EventThread"
using namespace std::chrono_literals;
@@ -61,6 +68,8 @@
return "VSyncRequest::None";
case VSyncRequest::Single:
return "VSyncRequest::Single";
+ case VSyncRequest::SingleSuppressCallback:
+ return "VSyncRequest::SingleSuppressCallback";
default:
return StringPrintf("VSyncRequest::Periodic{period=%d}", vsyncPeriod(request));
}
@@ -74,18 +83,16 @@
std::string toString(const DisplayEventReceiver::Event& event) {
switch (event.header.type) {
case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
- return StringPrintf("Hotplug{displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", %s}",
- event.header.displayId,
+ return StringPrintf("Hotplug{displayId=%s, %s}",
+ to_string(event.header.displayId).c_str(),
event.hotplug.connected ? "connected" : "disconnected");
case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
- return StringPrintf("VSync{displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
- ", count=%u, expectedVSyncTimestamp=%" PRId64 "}",
- event.header.displayId, event.vsync.count,
+ return StringPrintf("VSync{displayId=%s, count=%u, expectedVSyncTimestamp=%" PRId64 "}",
+ to_string(event.header.displayId).c_str(), event.vsync.count,
event.vsync.expectedVSyncTimestamp);
- case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
- return StringPrintf("ConfigChanged{displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
- ", configId=%u}",
- event.header.displayId, event.config.configId);
+ case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE:
+ return StringPrintf("ModeChanged{displayId=%s, modeId=%u}",
+ to_string(event.header.displayId).c_str(), event.modeChange.modeId);
default:
return "Event{}";
}
@@ -100,30 +107,56 @@
}
DisplayEventReceiver::Event makeVSync(PhysicalDisplayId displayId, nsecs_t timestamp,
- uint32_t count, nsecs_t expectedVSyncTimestamp) {
+ uint32_t count, nsecs_t expectedVSyncTimestamp,
+ nsecs_t deadlineTimestamp, int64_t vsyncId) {
DisplayEventReceiver::Event event;
event.header = {DisplayEventReceiver::DISPLAY_EVENT_VSYNC, displayId, timestamp};
event.vsync.count = count;
event.vsync.expectedVSyncTimestamp = expectedVSyncTimestamp;
+ event.vsync.deadlineTimestamp = deadlineTimestamp;
+ event.vsync.vsyncId = vsyncId;
return event;
}
-DisplayEventReceiver::Event makeConfigChanged(PhysicalDisplayId displayId,
- HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
+DisplayEventReceiver::Event makeModeChanged(PhysicalDisplayId displayId, DisplayModeId modeId,
+ nsecs_t vsyncPeriod) {
DisplayEventReceiver::Event event;
- event.header = {DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED, displayId, systemTime()};
- event.config.configId = configId.value();
- event.config.vsyncPeriod = vsyncPeriod;
+ event.header = {DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE, displayId, systemTime()};
+ event.modeChange.modeId = modeId.value();
+ event.modeChange.vsyncPeriod = vsyncPeriod;
return event;
}
+DisplayEventReceiver::Event makeFrameRateOverrideEvent(PhysicalDisplayId displayId,
+ FrameRateOverride frameRateOverride) {
+ return DisplayEventReceiver::Event{
+ .header =
+ DisplayEventReceiver::Event::Header{
+ .type = DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE,
+ .displayId = displayId,
+ .timestamp = systemTime(),
+ },
+ .frameRateOverride = frameRateOverride,
+ };
+}
+
+DisplayEventReceiver::Event makeFrameRateOverrideFlushEvent(PhysicalDisplayId displayId) {
+ return DisplayEventReceiver::Event{
+ .header = DisplayEventReceiver::Event::Header{
+ .type = DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH,
+ .displayId = displayId,
+ .timestamp = systemTime(),
+ }};
+}
+
} // namespace
-EventThreadConnection::EventThreadConnection(EventThread* eventThread,
- ResyncCallback resyncCallback,
- ISurfaceComposer::ConfigChanged configChanged)
+EventThreadConnection::EventThreadConnection(
+ EventThread* eventThread, uid_t callingUid, ResyncCallback resyncCallback,
+ ISurfaceComposer::EventRegistrationFlags eventRegistration)
: resyncCallback(std::move(resyncCallback)),
- mConfigChanged(configChanged),
+ mOwnerUid(callingUid),
+ mEventRegistration(eventRegistration),
mEventThread(eventThread),
mChannel(gui::BitTube::DefaultSize) {}
@@ -154,8 +187,25 @@
}
status_t EventThreadConnection::postEvent(const DisplayEventReceiver::Event& event) {
- ssize_t size = DisplayEventReceiver::sendEvents(&mChannel, &event, 1);
- return size < 0 ? status_t(size) : status_t(NO_ERROR);
+ constexpr auto toStatus = [](ssize_t size) {
+ return size < 0 ? status_t(size) : status_t(NO_ERROR);
+ };
+
+ if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE ||
+ event.header.type == DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH) {
+ mPendingEvents.emplace_back(event);
+ if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE) {
+ return status_t(NO_ERROR);
+ }
+
+ auto size = DisplayEventReceiver::sendEvents(&mChannel, mPendingEvents.data(),
+ mPendingEvents.size());
+ mPendingEvents.clear();
+ return toStatus(size);
+ }
+
+ auto size = DisplayEventReceiver::sendEvents(&mChannel, &event, 1);
+ return toStatus(size);
}
// ---------------------------------------------------------------------------
@@ -165,10 +215,20 @@
namespace impl {
EventThread::EventThread(std::unique_ptr<VSyncSource> vsyncSource,
- InterceptVSyncsCallback interceptVSyncsCallback)
+ android::frametimeline::TokenManager* tokenManager,
+ InterceptVSyncsCallback interceptVSyncsCallback,
+ ThrottleVsyncCallback throttleVsyncCallback,
+ GetVsyncPeriodFunction getVsyncPeriodFunction)
: mVSyncSource(std::move(vsyncSource)),
+ mTokenManager(tokenManager),
mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)),
+ mThrottleVsyncCallback(std::move(throttleVsyncCallback)),
+ mGetVsyncPeriodFunction(std::move(getVsyncPeriodFunction)),
mThreadName(mVSyncSource->getName()) {
+
+ LOG_ALWAYS_FATAL_IF(getVsyncPeriodFunction == nullptr,
+ "getVsyncPeriodFunction must not be null");
+
mVSyncSource->setCallback(this);
mThread = std::thread([this]() NO_THREAD_SAFETY_ANALYSIS {
@@ -202,15 +262,18 @@
mThread.join();
}
-void EventThread::setPhaseOffset(nsecs_t phaseOffset) {
+void EventThread::setDuration(std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration) {
std::lock_guard<std::mutex> lock(mMutex);
- mVSyncSource->setPhaseOffset(phaseOffset);
+ mVSyncSource->setDuration(workDuration, readyDuration);
}
sp<EventThreadConnection> EventThread::createEventConnection(
- ResyncCallback resyncCallback, ISurfaceComposer::ConfigChanged configChanged) const {
- return new EventThreadConnection(const_cast<EventThread*>(this), std::move(resyncCallback),
- configChanged);
+ ResyncCallback resyncCallback,
+ ISurfaceComposer::EventRegistrationFlags eventRegistration) const {
+ return new EventThreadConnection(const_cast<EventThread*>(this),
+ IPCThreadState::self()->getCallingUid(),
+ std::move(resyncCallback), eventRegistration);
}
status_t EventThread::registerDisplayEventConnection(const sp<EventThreadConnection>& connection) {
@@ -262,6 +325,8 @@
if (connection->vsyncRequest == VSyncRequest::None) {
connection->vsyncRequest = VSyncRequest::Single;
mCondition.notify_all();
+ } else if (connection->vsyncRequest == VSyncRequest::SingleSuppressCallback) {
+ connection->vsyncRequest = VSyncRequest::Single;
}
}
@@ -285,12 +350,21 @@
mCondition.notify_all();
}
-void EventThread::onVSyncEvent(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp) {
+void EventThread::onVSyncEvent(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp,
+ nsecs_t deadlineTimestamp) {
std::lock_guard<std::mutex> lock(mMutex);
LOG_FATAL_IF(!mVSyncState);
+ const int64_t vsyncId = [&] {
+ if (mTokenManager != nullptr) {
+ return mTokenManager->generateTokenForPredictions(
+ {timestamp, deadlineTimestamp, expectedVSyncTimestamp});
+ }
+ return FrameTimelineInfo::INVALID_VSYNC_ID;
+ }();
+
mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count,
- expectedVSyncTimestamp));
+ expectedVSyncTimestamp, deadlineTimestamp, vsyncId));
mCondition.notify_all();
}
@@ -301,11 +375,23 @@
mCondition.notify_all();
}
-void EventThread::onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId,
- nsecs_t vsyncPeriod) {
+void EventThread::onModeChanged(PhysicalDisplayId displayId, DisplayModeId modeId,
+ nsecs_t vsyncPeriod) {
std::lock_guard<std::mutex> lock(mMutex);
- mPendingEvents.push_back(makeConfigChanged(displayId, configId, vsyncPeriod));
+ mPendingEvents.push_back(makeModeChanged(displayId, modeId, vsyncPeriod));
+ mCondition.notify_all();
+}
+
+void EventThread::onFrameRateOverridesChanged(PhysicalDisplayId displayId,
+ std::vector<FrameRateOverride> overrides) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ for (auto frameRateOverride : overrides) {
+ mPendingEvents.push_back(makeFrameRateOverrideEvent(displayId, frameRateOverride));
+ }
+ mPendingEvents.push_back(makeFrameRateOverrideFlushEvent(displayId));
+
mCondition.notify_all();
}
@@ -412,9 +498,18 @@
LOG_FATAL_IF(!mVSyncState);
const auto now = systemTime(SYSTEM_TIME_MONOTONIC);
- const auto expectedVSyncTime = now + timeout.count();
+ const auto deadlineTimestamp = now + timeout.count();
+ const auto expectedVSyncTime = deadlineTimestamp + timeout.count();
+ const int64_t vsyncId = [&] {
+ if (mTokenManager != nullptr) {
+ return mTokenManager->generateTokenForPredictions(
+ {now, deadlineTimestamp, expectedVSyncTime});
+ }
+ return FrameTimelineInfo::INVALID_VSYNC_ID;
+ }();
mPendingEvents.push_back(makeVSync(mVSyncState->displayId, now,
- ++mVSyncState->count, expectedVSyncTime));
+ ++mVSyncState->count, expectedVSyncTime,
+ deadlineTimestamp, vsyncId));
}
}
}
@@ -422,27 +517,52 @@
bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event,
const sp<EventThreadConnection>& connection) const {
+ const auto throttleVsync = [&] {
+ return mThrottleVsyncCallback &&
+ mThrottleVsyncCallback(event.vsync.expectedVSyncTimestamp, connection->mOwnerUid);
+ };
+
switch (event.header.type) {
case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
return true;
- case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED: {
- return connection->mConfigChanged == ISurfaceComposer::eConfigChangedDispatch;
+ case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE: {
+ return connection->mEventRegistration.test(
+ ISurfaceComposer::EventRegistration::modeChanged);
}
case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
switch (connection->vsyncRequest) {
case VSyncRequest::None:
return false;
- case VSyncRequest::Single:
+ case VSyncRequest::SingleSuppressCallback:
connection->vsyncRequest = VSyncRequest::None;
+ return false;
+ case VSyncRequest::Single: {
+ if (throttleVsync()) {
+ return false;
+ }
+ connection->vsyncRequest = VSyncRequest::SingleSuppressCallback;
return true;
+ }
case VSyncRequest::Periodic:
+ if (throttleVsync()) {
+ return false;
+ }
return true;
default:
+ // We don't throttle vsync if the app set a vsync request rate
+ // since there is no easy way to do that and this is a very
+ // rare case
return event.vsync.count % vsyncPeriod(connection->vsyncRequest) == 0;
}
+ case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE:
+ [[fallthrough]];
+ case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH:
+ return connection->mEventRegistration.test(
+ ISurfaceComposer::EventRegistration::frameRateOverride);
+
default:
return false;
}
@@ -451,7 +571,11 @@
void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event,
const DisplayEventConsumers& consumers) {
for (const auto& consumer : consumers) {
- switch (consumer->postEvent(event)) {
+ DisplayEventReceiver::Event copy = event;
+ if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
+ copy.vsync.frameInterval = mGetVsyncPeriodFunction(consumer->mOwnerUid);
+ }
+ switch (consumer->postEvent(copy)) {
case NO_ERROR:
break;
@@ -473,8 +597,8 @@
StringAppendF(&result, "%s: state=%s VSyncState=", mThreadName, toCString(mState));
if (mVSyncState) {
- StringAppendF(&result, "{displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%u%s}\n",
- mVSyncState->displayId, mVSyncState->count,
+ StringAppendF(&result, "{displayId=%s, count=%u%s}\n",
+ to_string(mVSyncState->displayId).c_str(), mVSyncState->count,
mVSyncState->synthetic ? ", synthetic" : "");
} else {
StringAppendF(&result, "none\n");
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 49f624c..1e6793f 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -31,7 +31,7 @@
#include <thread>
#include <vector>
-#include "HwcStrongTypes.h"
+#include "DisplayHardware/DisplayMode.h"
// ---------------------------------------------------------------------------
namespace android {
@@ -41,13 +41,21 @@
class EventThreadTest;
class SurfaceFlinger;
+namespace frametimeline {
+class TokenManager;
+} // namespace frametimeline
+
// ---------------------------------------------------------------------------
using ResyncCallback = std::function<void()>;
+using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
enum class VSyncRequest {
- None = -1,
- Single = 0,
+ None = -2,
+ // Single wakes up for the next two frames to avoid scheduler overhead
+ Single = -1,
+ // SingleSuppressCallback only wakes up for the next frame
+ SingleSuppressCallback = 0,
Periodic = 1,
// Subsequent values are periods.
};
@@ -57,7 +65,8 @@
class Callback {
public:
virtual ~Callback() {}
- virtual void onVSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) = 0;
+ virtual void onVSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp,
+ nsecs_t deadlineTimestamp) = 0;
};
virtual ~VSyncSource() {}
@@ -65,15 +74,16 @@
virtual const char* getName() const = 0;
virtual void setVSyncEnabled(bool enable) = 0;
virtual void setCallback(Callback* callback) = 0;
- virtual void setPhaseOffset(nsecs_t phaseOffset) = 0;
+ virtual void setDuration(std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration) = 0;
virtual void dump(std::string& result) const = 0;
};
class EventThreadConnection : public BnDisplayEventConnection {
public:
- EventThreadConnection(EventThread*, ResyncCallback,
- ISurfaceComposer::ConfigChanged configChanged);
+ EventThreadConnection(EventThread*, uid_t callingUid, ResyncCallback,
+ ISurfaceComposer::EventRegistrationFlags eventRegistration = {});
virtual ~EventThreadConnection();
virtual status_t postEvent(const DisplayEventReceiver::Event& event);
@@ -86,13 +96,15 @@
const ResyncCallback resyncCallback;
VSyncRequest vsyncRequest = VSyncRequest::None;
- const ISurfaceComposer::ConfigChanged mConfigChanged =
- ISurfaceComposer::ConfigChanged::eConfigChangedSuppress;
+ const uid_t mOwnerUid;
+ const ISurfaceComposer::EventRegistrationFlags mEventRegistration;
private:
virtual void onFirstRef();
EventThread* const mEventThread;
gui::BitTube mChannel;
+
+ std::vector<DisplayEventReceiver::Event> mPendingEvents;
};
class EventThread {
@@ -100,7 +112,8 @@
virtual ~EventThread();
virtual sp<EventThreadConnection> createEventConnection(
- ResyncCallback, ISurfaceComposer::ConfigChanged configChanged) const = 0;
+ ResyncCallback,
+ ISurfaceComposer::EventRegistrationFlags eventRegistration = {}) const = 0;
// called before the screen is turned off from main thread
virtual void onScreenReleased() = 0;
@@ -110,13 +123,18 @@
virtual void onHotplugReceived(PhysicalDisplayId displayId, bool connected) = 0;
- // called when SF changes the active config and apps needs to be notified about the change
- virtual void onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId,
- nsecs_t vsyncPeriod) = 0;
+ // called when SF changes the active mode and apps needs to be notified about the change
+ virtual void onModeChanged(PhysicalDisplayId displayId, DisplayModeId modeId,
+ nsecs_t vsyncPeriod) = 0;
+
+ // called when SF updates the Frame Rate Override list
+ virtual void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
+ std::vector<FrameRateOverride> overrides) = 0;
virtual void dump(std::string& result) const = 0;
- virtual void setPhaseOffset(nsecs_t phaseOffset) = 0;
+ virtual void setDuration(std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration) = 0;
virtual status_t registerDisplayEventConnection(
const sp<EventThreadConnection>& connection) = 0;
@@ -133,12 +151,16 @@
class EventThread : public android::EventThread, private VSyncSource::Callback {
public:
using InterceptVSyncsCallback = std::function<void(nsecs_t)>;
+ using ThrottleVsyncCallback = std::function<bool(nsecs_t, uid_t)>;
+ using GetVsyncPeriodFunction = std::function<nsecs_t(uid_t)>;
- EventThread(std::unique_ptr<VSyncSource>, InterceptVSyncsCallback);
+ EventThread(std::unique_ptr<VSyncSource>, frametimeline::TokenManager*, InterceptVSyncsCallback,
+ ThrottleVsyncCallback, GetVsyncPeriodFunction);
~EventThread();
sp<EventThreadConnection> createEventConnection(
- ResyncCallback, ISurfaceComposer::ConfigChanged configChanged) const override;
+ ResyncCallback,
+ ISurfaceComposer::EventRegistrationFlags eventRegistration = {}) const override;
status_t registerDisplayEventConnection(const sp<EventThreadConnection>& connection) override;
void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) override;
@@ -152,12 +174,16 @@
void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override;
- void onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId,
- nsecs_t vsyncPeriod) override;
+ void onModeChanged(PhysicalDisplayId displayId, DisplayModeId modeId,
+ nsecs_t vsyncPeriod) override;
+
+ void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
+ std::vector<FrameRateOverride> overrides) override;
void dump(std::string& result) const override;
- void setPhaseOffset(nsecs_t phaseOffset) override;
+ void setDuration(std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration) override;
size_t getEventThreadConnectionCount() override;
@@ -177,11 +203,15 @@
REQUIRES(mMutex);
// Implements VSyncSource::Callback
- void onVSyncEvent(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp) override;
+ void onVSyncEvent(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp,
+ nsecs_t deadlineTimestamp) override;
const std::unique_ptr<VSyncSource> mVSyncSource GUARDED_BY(mMutex);
+ frametimeline::TokenManager* const mTokenManager;
const InterceptVSyncsCallback mInterceptVSyncsCallback;
+ const ThrottleVsyncCallback mThrottleVsyncCallback;
+ const GetVsyncPeriodFunction mGetVsyncPeriodFunction;
const char* const mThreadName;
std::thread mThread;
diff --git a/services/surfaceflinger/Scheduler/HwcStrongTypes.h b/services/surfaceflinger/Scheduler/HwcStrongTypes.h
deleted file mode 100644
index 8ba4f20..0000000
--- a/services/surfaceflinger/Scheduler/HwcStrongTypes.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright 2019 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.
- */
-
-#pragma once
-
-#include "StrongTyping.h"
-
-namespace android {
-
-// Strong types for the different indexes as they are referring to a different base.
-using HwcConfigIndexType = StrongTyping<int, struct HwcConfigIndexTypeTag, Compare, Add, Hash>;
-
-} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/InjectVSyncSource.h b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
index 975c9db..016b076 100644
--- a/services/surfaceflinger/Scheduler/InjectVSyncSource.h
+++ b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
@@ -35,16 +35,17 @@
mCallback = callback;
}
- void onInjectSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) {
+ void onInjectSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp,
+ nsecs_t deadlineTimestamp) {
std::lock_guard<std::mutex> lock(mCallbackMutex);
if (mCallback) {
- mCallback->onVSyncEvent(when, expectedVSyncTimestamp);
+ mCallback->onVSyncEvent(when, expectedVSyncTimestamp, deadlineTimestamp);
}
}
const char* getName() const override { return "inject"; }
void setVSyncEnabled(bool) override {}
- void setPhaseOffset(nsecs_t) override {}
+ void setDuration(std::chrono::nanoseconds, std::chrono::nanoseconds) override {}
void dump(std::string&) const override {}
private:
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index ecf2597..f4bc2a1 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 The Android Open Source Project
+ * Copyright 2020 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.
@@ -20,6 +20,7 @@
#include "LayerHistory.h"
+#include <android-base/stringprintf.h>
#include <cutils/properties.h>
#include <utils/Log.h>
#include <utils/Timers.h>
@@ -34,21 +35,21 @@
#include "LayerInfo.h"
#include "SchedulerUtils.h"
-namespace android::scheduler::impl {
+namespace android::scheduler {
namespace {
-bool isLayerActive(const Layer& layer, const LayerInfo& info, nsecs_t threshold) {
- if (layer.getFrameRateForLayerTree().rate > 0) {
- return layer.isVisible();
+bool isLayerActive(const LayerInfo& info, nsecs_t threshold) {
+ // Layers with an explicit vote are always kept active
+ if (info.getSetFrameRateVote().rate.isValid()) {
+ return true;
}
- return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
+
+ return info.isVisible() && info.getLastUpdatedTime() >= threshold;
}
bool traceEnabled() {
- char value[PROPERTY_VALUE_MAX];
- property_get("debug.sf.layer_history_trace", value, "0");
- return atoi(value);
+ return property_get_bool("debug.sf.layer_history_trace", false);
}
bool useFrameRatePriority() {
@@ -57,30 +58,55 @@
return atoi(value);
}
-void trace(const wp<Layer>& weak, int fps) {
- const auto layer = weak.promote();
- if (!layer) return;
+void trace(const LayerInfo& info, LayerHistory::LayerVoteType type, int fps) {
+ const auto traceType = [&](LayerHistory::LayerVoteType checkedType, int value) {
+ ATRACE_INT(info.getTraceTag(checkedType), type == checkedType ? value : 0);
+ };
- const auto& name = layer->getName();
- const auto tag = "LFPS " + name;
- ATRACE_INT(tag.c_str(), fps);
- ALOGD("%s: %s @ %d Hz", __FUNCTION__, name.c_str(), fps);
+ traceType(LayerHistory::LayerVoteType::NoVote, 1);
+ traceType(LayerHistory::LayerVoteType::Heuristic, fps);
+ traceType(LayerHistory::LayerVoteType::ExplicitDefault, fps);
+ traceType(LayerHistory::LayerVoteType::ExplicitExactOrMultiple, fps);
+ traceType(LayerHistory::LayerVoteType::ExplicitExact, fps);
+ traceType(LayerHistory::LayerVoteType::Min, 1);
+ traceType(LayerHistory::LayerVoteType::Max, 1);
+
+ ALOGD("%s: %s @ %d Hz", __FUNCTION__, info.getName().c_str(), fps);
}
} // namespace
-LayerHistory::LayerHistory()
- : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) {}
+LayerHistory::LayerHistory(const RefreshRateConfigs& refreshRateConfigs)
+ : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) {
+ LayerInfo::setTraceEnabled(mTraceEnabled);
+ LayerInfo::setRefreshRateConfigs(refreshRateConfigs);
+}
+
LayerHistory::~LayerHistory() = default;
-void LayerHistory::registerLayer(Layer* layer, float lowRefreshRate, float highRefreshRate,
- LayerVoteType /*type*/) {
- auto info = std::make_unique<LayerInfo>(lowRefreshRate, highRefreshRate);
+void LayerHistory::registerLayer(Layer* layer, LayerVoteType type) {
+ auto info = std::make_unique<LayerInfo>(layer->getName(), layer->getOwnerUid(), type);
std::lock_guard lock(mLock);
mLayerInfos.emplace_back(layer, std::move(info));
}
+void LayerHistory::deregisterLayer(Layer* layer) {
+ std::lock_guard lock(mLock);
+
+ const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
+ [layer](const auto& pair) { return pair.first == layer; });
+ LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
+
+ const size_t i = static_cast<size_t>(it - mLayerInfos.begin());
+ if (i < mActiveLayersEnd) {
+ mActiveLayersEnd--;
+ }
+ const size_t last = mLayerInfos.size() - 1;
+ std::swap(mLayerInfos[i], mLayerInfos[last]);
+ mLayerInfos.erase(mLayerInfos.begin() + static_cast<long>(last));
+}
+
void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now,
- LayerUpdateType /*updateType*/) {
+ LayerUpdateType updateType) {
std::lock_guard lock(mLock);
const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
@@ -88,7 +114,15 @@
LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
const auto& info = it->second;
- info->setLastPresentTime(presentTime, now);
+ const auto layerProps = LayerInfo::LayerProps{
+ .visible = layer->isVisible(),
+ .bounds = layer->getBounds(),
+ .transform = layer->getTransform(),
+ .setFrameRateVote = layer->getFrameRateForLayerTree(),
+ .frameRateSelectionPriority = layer->getFrameRateSelectionPriority(),
+ };
+
+ info->setLastPresentTime(presentTime, now, updateType, mModeChangePending, layerProps);
// Activate layer if inactive.
if (const auto end = activeLayers().end(); it >= end) {
@@ -98,43 +132,37 @@
}
LayerHistory::Summary LayerHistory::summarize(nsecs_t now) {
- ATRACE_CALL();
+ LayerHistory::Summary summary;
+
std::lock_guard lock(mLock);
partitionLayers(now);
- LayerHistory::Summary summary;
- for (const auto& [weakLayer, info] : activeLayers()) {
- const bool recent = info->isRecentlyActive(now);
- auto layer = weakLayer.promote();
- // Only use the layer if the reference still exists.
- if (layer || CC_UNLIKELY(mTraceEnabled)) {
- const auto layerFocused =
- Layer::isLayerFocusedBasedOnPriority(layer->getFrameRateSelectionPriority());
- // Check if frame rate was set on layer.
- const auto frameRate = layer->getFrameRateForLayerTree();
- if (frameRate.rate > 0.f) {
- const auto voteType = [&]() {
- switch (frameRate.type) {
- case Layer::FrameRateCompatibility::Default:
- return LayerVoteType::ExplicitDefault;
- case Layer::FrameRateCompatibility::ExactOrMultiple:
- return LayerVoteType::ExplicitExactOrMultiple;
- case Layer::FrameRateCompatibility::NoVote:
- return LayerVoteType::NoVote;
- }
- }();
- summary.push_back({layer->getName(), voteType, frameRate.rate, /* weight */ 1.0f,
- layerFocused});
- } else if (recent) {
- summary.push_back({layer->getName(), LayerVoteType::Heuristic,
- info->getRefreshRate(now),
- /* weight */ 1.0f, layerFocused});
- }
+ for (const auto& [layer, info] : activeLayers()) {
+ const auto frameRateSelectionPriority = info->getFrameRateSelectionPriority();
+ const auto layerFocused = Layer::isLayerFocusedBasedOnPriority(frameRateSelectionPriority);
+ ALOGV("%s has priority: %d %s focused", info->getName().c_str(), frameRateSelectionPriority,
+ layerFocused ? "" : "not");
- if (CC_UNLIKELY(mTraceEnabled)) {
- trace(weakLayer, round<int>(frameRate.rate));
- }
+ const auto vote = info->getRefreshRateVote(now);
+ // Skip NoVote layer as those don't have any requirements
+ if (vote.type == LayerHistory::LayerVoteType::NoVote) {
+ continue;
+ }
+
+ // Compute the layer's position on the screen
+ const Rect bounds = Rect(info->getBounds());
+ const ui::Transform transform = info->getTransform();
+ constexpr bool roundOutwards = true;
+ Rect transformed = transform.transform(bounds, roundOutwards);
+
+ const float layerArea = transformed.getWidth() * transformed.getHeight();
+ float weight = mDisplayArea ? layerArea / mDisplayArea : 0.0f;
+ summary.push_back({info->getName(), info->getOwnerUid(), vote.type, vote.fps,
+ vote.seamlessness, weight, layerFocused});
+
+ if (CC_UNLIKELY(mTraceEnabled)) {
+ trace(*info, vote.type, vote.fps.getIntValue());
}
}
@@ -147,40 +175,54 @@
// Collect expired and inactive layers after active layers.
size_t i = 0;
while (i < mActiveLayersEnd) {
- auto& [weak, info] = mLayerInfos[i];
- if (const auto layer = weak.promote(); layer && isLayerActive(*layer, *info, threshold)) {
+ auto& [layerUnsafe, info] = mLayerInfos[i];
+ if (isLayerActive(*info, threshold)) {
i++;
+ // Set layer vote if set
+ const auto frameRate = info->getSetFrameRateVote();
+ const auto voteType = [&]() {
+ switch (frameRate.type) {
+ case Layer::FrameRateCompatibility::Default:
+ return LayerVoteType::ExplicitDefault;
+ case Layer::FrameRateCompatibility::ExactOrMultiple:
+ return LayerVoteType::ExplicitExactOrMultiple;
+ case Layer::FrameRateCompatibility::NoVote:
+ return LayerVoteType::NoVote;
+ case Layer::FrameRateCompatibility::Exact:
+ return LayerVoteType::ExplicitExact;
+ }
+ }();
+
+ if (frameRate.rate.isValid() || voteType == LayerVoteType::NoVote) {
+ const auto type = info->isVisible() ? voteType : LayerVoteType::NoVote;
+ info->setLayerVote({type, frameRate.rate, frameRate.seamlessness});
+ } else {
+ info->resetLayerVote();
+ }
continue;
}
if (CC_UNLIKELY(mTraceEnabled)) {
- trace(weak, 0);
+ trace(*info, LayerHistory::LayerVoteType::NoVote, 0);
}
- info->clearHistory();
+ info->onLayerInactive(now);
std::swap(mLayerInfos[i], mLayerInfos[--mActiveLayersEnd]);
}
-
- // Collect expired layers after inactive layers.
- size_t end = mLayerInfos.size();
- while (i < end) {
- if (mLayerInfos[i].first.promote()) {
- i++;
- } else {
- std::swap(mLayerInfos[i], mLayerInfos[--end]);
- }
- }
-
- mLayerInfos.erase(mLayerInfos.begin() + static_cast<long>(end), mLayerInfos.end());
}
void LayerHistory::clear() {
std::lock_guard lock(mLock);
for (const auto& [layer, info] : activeLayers()) {
- info->clearHistory();
+ info->clearHistory(systemTime());
}
-
- mActiveLayersEnd = 0;
}
-} // namespace android::scheduler::impl
+
+std::string LayerHistory::dump() const {
+ std::lock_guard lock(mLock);
+ return base::StringPrintf("LayerHistory{size=%zu, active=%zu}", mLayerInfos.size(),
+ mActiveLayersEnd);
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 228b8a0..82f6c39 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -22,6 +22,7 @@
#include <memory>
#include <mutex>
+#include <string>
#include <utility>
#include <vector>
@@ -35,25 +36,23 @@
namespace scheduler {
class LayerHistoryTest;
-class LayerHistoryTestV2;
class LayerInfo;
-class LayerInfoV2;
class LayerHistory {
public:
using LayerVoteType = RefreshRateConfigs::LayerVoteType;
- virtual ~LayerHistory() = default;
+ LayerHistory(const RefreshRateConfigs&);
+ ~LayerHistory();
// Layers are unregistered when the weak reference expires.
- virtual void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate,
- LayerVoteType type) = 0;
+ void registerLayer(Layer*, LayerVoteType type);
// Sets the display size. Client is responsible for synchronization.
- virtual void setDisplayArea(uint32_t displayArea) = 0;
+ void setDisplayArea(uint32_t displayArea) { mDisplayArea = displayArea; }
- // Sets whether a config change is pending to be applied
- virtual void setConfigChangePending(bool pending) = 0;
+ // Sets whether a mode change is pending to be applied
+ void setModeChangePending(bool pending) { mModeChangePending = pending; }
// Represents which layer activity is recorded
enum class LayerUpdateType {
@@ -63,104 +62,23 @@
};
// Marks the layer as active, and records the given state to its history.
- virtual void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType) = 0;
+ void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType);
using Summary = std::vector<RefreshRateConfigs::LayerRequirement>;
// Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
- virtual Summary summarize(nsecs_t now) = 0;
+ Summary summarize(nsecs_t now);
- virtual void clear() = 0;
-};
+ void clear();
-namespace impl {
-// Records per-layer history of scheduling-related information (primarily present time),
-// heuristically categorizes layers as active or inactive, and summarizes stats about
-// active layers (primarily maximum refresh rate). See go/content-fps-detection-in-scheduler.
-class LayerHistory : public android::scheduler::LayerHistory {
-public:
- LayerHistory();
- virtual ~LayerHistory();
-
- // Layers are unregistered when the weak reference expires.
- void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate,
- LayerVoteType type) override;
-
- void setDisplayArea(uint32_t /*displayArea*/) override {}
-
- void setConfigChangePending(bool /*pending*/) override {}
-
- // Marks the layer as active, and records the given state to its history.
- void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType) override;
-
- // Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
- android::scheduler::LayerHistory::Summary summarize(nsecs_t now) override;
-
- void clear() override;
+ void deregisterLayer(Layer*);
+ std::string dump() const;
private:
- friend class android::scheduler::LayerHistoryTest;
+ friend LayerHistoryTest;
friend TestableScheduler;
- using LayerPair = std::pair<wp<Layer>, std::unique_ptr<LayerInfo>>;
- using LayerInfos = std::vector<LayerPair>;
-
- struct ActiveLayers {
- LayerInfos& infos;
- const size_t index;
-
- auto begin() { return infos.begin(); }
- auto end() { return begin() + static_cast<long>(index); }
- };
-
- ActiveLayers activeLayers() REQUIRES(mLock) { return {mLayerInfos, mActiveLayersEnd}; }
-
- // Iterates over layers in a single pass, swapping pairs such that active layers precede
- // inactive layers, and inactive layers precede expired layers. Removes expired layers by
- // truncating after inactive layers.
- void partitionLayers(nsecs_t now) REQUIRES(mLock);
-
- mutable std::mutex mLock;
-
- // Partitioned such that active layers precede inactive layers. For fast lookup, the few active
- // layers are at the front, and weak pointers are stored in contiguous memory to hit the cache.
- LayerInfos mLayerInfos GUARDED_BY(mLock);
- size_t mActiveLayersEnd GUARDED_BY(mLock) = 0;
-
- // Whether to emit systrace output and debug logs.
- const bool mTraceEnabled;
-
- // Whether to use priority sent from WindowManager to determine the relevancy of the layer.
- const bool mUseFrameRatePriority;
-};
-
-class LayerHistoryV2 : public android::scheduler::LayerHistory {
-public:
- LayerHistoryV2(const scheduler::RefreshRateConfigs&);
- virtual ~LayerHistoryV2();
-
- // Layers are unregistered when the weak reference expires.
- void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate,
- LayerVoteType type) override;
-
- // Sets the display size. Client is responsible for synchronization.
- void setDisplayArea(uint32_t displayArea) override { mDisplayArea = displayArea; }
-
- void setConfigChangePending(bool pending) override { mConfigChangePending = pending; }
-
- // Marks the layer as active, and records the given state to its history.
- void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType) override;
-
- // Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
- android::scheduler::LayerHistory::Summary summarize(nsecs_t /*now*/) override;
-
- void clear() override;
-
-private:
- friend android::scheduler::LayerHistoryTestV2;
- friend TestableScheduler;
-
- using LayerPair = std::pair<wp<Layer>, std::unique_ptr<LayerInfoV2>>;
+ using LayerPair = std::pair<Layer*, std::unique_ptr<LayerInfo>>;
using LayerInfos = std::vector<LayerPair>;
struct ActiveLayers {
@@ -193,10 +111,9 @@
// Whether to use priority sent from WindowManager to determine the relevancy of the layer.
const bool mUseFrameRatePriority;
- // Whether a config change is in progress or not
- std::atomic<bool> mConfigChangePending = false;
+ // Whether a mode change is in progress or not
+ std::atomic<bool> mModeChangePending = false;
};
-} // namespace impl
} // namespace scheduler
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
deleted file mode 100644
index aa04bd7..0000000
--- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Copyright 2020 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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "LayerHistoryV2"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "LayerHistory.h"
-
-#include <cutils/properties.h>
-#include <utils/Log.h>
-#include <utils/Timers.h>
-#include <utils/Trace.h>
-
-#include <algorithm>
-#include <cmath>
-#include <string>
-#include <utility>
-
-#include "../Layer.h"
-#include "SchedulerUtils.h"
-
-#include "LayerInfoV2.h"
-
-namespace android::scheduler::impl {
-
-namespace {
-
-bool isLayerActive(const Layer& layer, const LayerInfoV2& info, nsecs_t threshold) {
- // Layers with an explicit vote are always kept active
- if (layer.getFrameRateForLayerTree().rate > 0) {
- return true;
- }
-
- return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
-}
-
-bool traceEnabled() {
- return property_get_bool("debug.sf.layer_history_trace", false);
-}
-
-bool useFrameRatePriority() {
- char value[PROPERTY_VALUE_MAX];
- property_get("debug.sf.use_frame_rate_priority", value, "1");
- return atoi(value);
-}
-
-void trace(const wp<Layer>& weak, const LayerInfoV2& info, LayerHistory::LayerVoteType type,
- int fps) {
- const auto layer = weak.promote();
- if (!layer) return;
-
- const auto traceType = [&](LayerHistory::LayerVoteType checkedType, int value) {
- ATRACE_INT(info.getTraceTag(checkedType), type == checkedType ? value : 0);
- };
-
- traceType(LayerHistory::LayerVoteType::NoVote, 1);
- traceType(LayerHistory::LayerVoteType::Heuristic, fps);
- traceType(LayerHistory::LayerVoteType::ExplicitDefault, fps);
- traceType(LayerHistory::LayerVoteType::ExplicitExactOrMultiple, fps);
- traceType(LayerHistory::LayerVoteType::Min, 1);
- traceType(LayerHistory::LayerVoteType::Max, 1);
-
- ALOGD("%s: %s @ %d Hz", __FUNCTION__, layer->getName().c_str(), fps);
-}
-} // namespace
-
-LayerHistoryV2::LayerHistoryV2(const scheduler::RefreshRateConfigs& refreshRateConfigs)
- : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) {
- LayerInfoV2::setTraceEnabled(mTraceEnabled);
- LayerInfoV2::setRefreshRateConfigs(refreshRateConfigs);
-}
-
-LayerHistoryV2::~LayerHistoryV2() = default;
-
-void LayerHistoryV2::registerLayer(Layer* layer, float /*lowRefreshRate*/, float highRefreshRate,
- LayerVoteType type) {
- const nsecs_t highRefreshRatePeriod = static_cast<nsecs_t>(1e9f / highRefreshRate);
- auto info = std::make_unique<LayerInfoV2>(layer->getName(), highRefreshRatePeriod, type);
- std::lock_guard lock(mLock);
- mLayerInfos.emplace_back(layer, std::move(info));
-}
-
-void LayerHistoryV2::record(Layer* layer, nsecs_t presentTime, nsecs_t now,
- LayerUpdateType updateType) {
- std::lock_guard lock(mLock);
-
- const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
- [layer](const auto& pair) { return pair.first == layer; });
- LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
-
- const auto& info = it->second;
- info->setLastPresentTime(presentTime, now, updateType, mConfigChangePending);
-
- // Activate layer if inactive.
- if (const auto end = activeLayers().end(); it >= end) {
- std::iter_swap(it, end);
- mActiveLayersEnd++;
- }
-}
-
-LayerHistoryV2::Summary LayerHistoryV2::summarize(nsecs_t now) {
- LayerHistory::Summary summary;
-
- std::lock_guard lock(mLock);
-
- partitionLayers(now);
-
- for (const auto& [layer, info] : activeLayers()) {
- const auto strong = layer.promote();
- if (!strong) {
- continue;
- }
-
- const auto frameRateSelectionPriority = strong->getFrameRateSelectionPriority();
- const auto layerFocused = Layer::isLayerFocusedBasedOnPriority(frameRateSelectionPriority);
- ALOGV("%s has priority: %d %s focused", strong->getName().c_str(),
- frameRateSelectionPriority, layerFocused ? "" : "not");
-
- const auto [type, refreshRate] = info->getRefreshRate(now);
- // Skip NoVote layer as those don't have any requirements
- if (type == LayerHistory::LayerVoteType::NoVote) {
- continue;
- }
-
- // Compute the layer's position on the screen
- const Rect bounds = Rect(strong->getBounds());
- const ui::Transform transform = strong->getTransform();
- constexpr bool roundOutwards = true;
- Rect transformed = transform.transform(bounds, roundOutwards);
-
- const float layerArea = transformed.getWidth() * transformed.getHeight();
- float weight = mDisplayArea ? layerArea / mDisplayArea : 0.0f;
- summary.push_back({strong->getName(), type, refreshRate, weight, layerFocused});
-
- if (CC_UNLIKELY(mTraceEnabled)) {
- trace(layer, *info, type, static_cast<int>(std::round(refreshRate)));
- }
- }
-
- return summary;
-}
-
-void LayerHistoryV2::partitionLayers(nsecs_t now) {
- const nsecs_t threshold = getActiveLayerThreshold(now);
-
- // Collect expired and inactive layers after active layers.
- size_t i = 0;
- while (i < mActiveLayersEnd) {
- auto& [weak, info] = mLayerInfos[i];
- if (const auto layer = weak.promote(); layer && isLayerActive(*layer, *info, threshold)) {
- i++;
- // Set layer vote if set
- const auto frameRate = layer->getFrameRateForLayerTree();
- const auto voteType = [&]() {
- switch (frameRate.type) {
- case Layer::FrameRateCompatibility::Default:
- return LayerVoteType::ExplicitDefault;
- case Layer::FrameRateCompatibility::ExactOrMultiple:
- return LayerVoteType::ExplicitExactOrMultiple;
- case Layer::FrameRateCompatibility::NoVote:
- return LayerVoteType::NoVote;
- }
- }();
-
- if (frameRate.rate > 0 || voteType == LayerVoteType::NoVote) {
- const auto type = layer->isVisible() ? voteType : LayerVoteType::NoVote;
- info->setLayerVote(type, frameRate.rate);
- } else {
- info->resetLayerVote();
- }
- continue;
- }
-
- if (CC_UNLIKELY(mTraceEnabled)) {
- trace(weak, *info, LayerHistory::LayerVoteType::NoVote, 0);
- }
-
- info->onLayerInactive(now);
- std::swap(mLayerInfos[i], mLayerInfos[--mActiveLayersEnd]);
- }
-
- // Collect expired layers after inactive layers.
- size_t end = mLayerInfos.size();
- while (i < end) {
- if (mLayerInfos[i].first.promote()) {
- i++;
- } else {
- std::swap(mLayerInfos[i], mLayerInfos[--end]);
- }
- }
-
- mLayerInfos.erase(mLayerInfos.begin() + static_cast<long>(end), mLayerInfos.end());
-}
-
-void LayerHistoryV2::clear() {
- std::lock_guard lock(mLock);
-
- for (const auto& [layer, info] : activeLayers()) {
- info->clearHistory(systemTime());
- }
-}
-
-} // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 6d9dd43..989bf4e 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 The Android Open Source Project
+ * Copyright 2020 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.
@@ -14,36 +14,312 @@
* limitations under the License.
*/
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wextra"
+
+// #define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
#include "LayerInfo.h"
#include <algorithm>
#include <utility>
+#include <cutils/compiler.h>
+#include <cutils/trace.h>
+
+#undef LOG_TAG
+#define LOG_TAG "LayerInfo"
+
namespace android::scheduler {
-LayerInfo::LayerInfo(float lowRefreshRate, float highRefreshRate)
- : mLowRefreshRate(lowRefreshRate), mHighRefreshRate(highRefreshRate) {}
+const RefreshRateConfigs* LayerInfo::sRefreshRateConfigs = nullptr;
+bool LayerInfo::sTraceEnabled = false;
-void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now) {
+LayerInfo::LayerInfo(const std::string& name, uid_t ownerUid,
+ LayerHistory::LayerVoteType defaultVote)
+ : mName(name),
+ mOwnerUid(ownerUid),
+ mDefaultVote(defaultVote),
+ mLayerVote({defaultVote, Fps(0.0f)}),
+ mRefreshRateHistory(name) {}
+
+void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
+ bool pendingModeChange, LayerProps props) {
lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
- // Buffers can come with a present time far in the future. That keeps them relevant.
mLastUpdatedTime = std::max(lastPresentTime, now);
- mPresentTimeHistory.insertPresentTime(mLastUpdatedTime);
+ mLayerProps = props;
+ switch (updateType) {
+ case LayerUpdateType::AnimationTX:
+ mLastAnimationTime = std::max(lastPresentTime, now);
+ break;
+ case LayerUpdateType::SetFrameRate:
+ case LayerUpdateType::Buffer:
+ FrameTimeData frameTime = {.presentTime = lastPresentTime,
+ .queueTime = mLastUpdatedTime,
+ .pendingModeChange = pendingModeChange};
+ mFrameTimes.push_back(frameTime);
+ if (mFrameTimes.size() > HISTORY_SIZE) {
+ mFrameTimes.pop_front();
+ }
+ break;
+ }
+}
- if (mLastPresentTime == 0) {
- // First frame
- mLastPresentTime = lastPresentTime;
- return;
+bool LayerInfo::isFrameTimeValid(const FrameTimeData& frameTime) const {
+ return frameTime.queueTime >= std::chrono::duration_cast<std::chrono::nanoseconds>(
+ mFrameTimeValidSince.time_since_epoch())
+ .count();
+}
+
+bool LayerInfo::isFrequent(nsecs_t now) const {
+ // If we know nothing about this layer we consider it as frequent as it might be the start
+ // of an animation.
+ if (mFrameTimes.size() < kFrequentLayerWindowSize) {
+ return true;
}
- const nsecs_t period = lastPresentTime - mLastPresentTime;
- mLastPresentTime = lastPresentTime;
- // Ignore time diff that are too high - those are stale values
- if (period > MAX_ACTIVE_LAYER_PERIOD_NS.count()) return;
+ // Find the first active frame
+ auto it = mFrameTimes.begin();
+ for (; it != mFrameTimes.end(); ++it) {
+ if (it->queueTime >= getActiveLayerThreshold(now)) {
+ break;
+ }
+ }
- const float fps = std::min(1e9f / period, mHighRefreshRate);
- mRefreshRateHistory.insertRefreshRate(fps);
+ const auto numFrames = std::distance(it, mFrameTimes.end());
+ if (numFrames < kFrequentLayerWindowSize) {
+ return false;
+ }
+
+ // Layer is considered frequent if the average frame rate is higher than the threshold
+ const auto totalTime = mFrameTimes.back().queueTime - it->queueTime;
+ return Fps::fromPeriodNsecs(totalTime / (numFrames - 1))
+ .greaterThanOrEqualWithMargin(kMinFpsForFrequentLayer);
+}
+
+bool LayerInfo::isAnimating(nsecs_t now) const {
+ return mLastAnimationTime >= getActiveLayerThreshold(now);
+}
+
+bool LayerInfo::hasEnoughDataForHeuristic() const {
+ // The layer had to publish at least HISTORY_SIZE or HISTORY_DURATION of updates
+ if (mFrameTimes.size() < 2) {
+ ALOGV("fewer than 2 frames recorded: %zu", mFrameTimes.size());
+ return false;
+ }
+
+ if (!isFrameTimeValid(mFrameTimes.front())) {
+ ALOGV("stale frames still captured");
+ return false;
+ }
+
+ const auto totalDuration = mFrameTimes.back().queueTime - mFrameTimes.front().queueTime;
+ if (mFrameTimes.size() < HISTORY_SIZE && totalDuration < HISTORY_DURATION.count()) {
+ ALOGV("not enough frames captured: %zu | %.2f seconds", mFrameTimes.size(),
+ totalDuration / 1e9f);
+ return false;
+ }
+
+ return true;
+}
+
+std::optional<nsecs_t> LayerInfo::calculateAverageFrameTime() const {
+ // Ignore frames captured during a mode change
+ const bool isDuringModeChange =
+ std::any_of(mFrameTimes.begin(), mFrameTimes.end(),
+ [](const auto& frame) { return frame.pendingModeChange; });
+ if (isDuringModeChange) {
+ return std::nullopt;
+ }
+
+ const bool isMissingPresentTime =
+ std::any_of(mFrameTimes.begin(), mFrameTimes.end(),
+ [](auto frame) { return frame.presentTime == 0; });
+ if (isMissingPresentTime && !mLastRefreshRate.reported.isValid()) {
+ // If there are no presentation timestamps and we haven't calculated
+ // one in the past then we can't calculate the refresh rate
+ return std::nullopt;
+ }
+
+ // Calculate the average frame time based on presentation timestamps. If those
+ // doesn't exist, we look at the time the buffer was queued only. We can do that only if
+ // we calculated a refresh rate based on presentation timestamps in the past. The reason
+ // we look at the queue time is to handle cases where hwui attaches presentation timestamps
+ // when implementing render ahead for specific refresh rates. When hwui no longer provides
+ // presentation timestamps we look at the queue time to see if the current refresh rate still
+ // matches the content.
+
+ auto getFrameTime = isMissingPresentTime ? [](FrameTimeData data) { return data.queueTime; }
+ : [](FrameTimeData data) { return data.presentTime; };
+
+ nsecs_t totalDeltas = 0;
+ int numDeltas = 0;
+ auto prevFrame = mFrameTimes.begin();
+ for (auto it = mFrameTimes.begin() + 1; it != mFrameTimes.end(); ++it) {
+ const auto currDelta = getFrameTime(*it) - getFrameTime(*prevFrame);
+ if (currDelta < kMinPeriodBetweenFrames) {
+ // Skip this frame, but count the delta into the next frame
+ continue;
+ }
+
+ prevFrame = it;
+
+ if (currDelta > kMaxPeriodBetweenFrames) {
+ // Skip this frame and the current delta.
+ continue;
+ }
+
+ totalDeltas += currDelta;
+ numDeltas++;
+ }
+
+ if (numDeltas == 0) {
+ return std::nullopt;
+ }
+
+ const auto averageFrameTime = static_cast<double>(totalDeltas) / static_cast<double>(numDeltas);
+ return static_cast<nsecs_t>(averageFrameTime);
+}
+
+std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible(nsecs_t now) {
+ static constexpr float MARGIN = 1.0f; // 1Hz
+ if (!hasEnoughDataForHeuristic()) {
+ ALOGV("Not enough data");
+ return std::nullopt;
+ }
+
+ const auto averageFrameTime = calculateAverageFrameTime();
+ if (averageFrameTime.has_value()) {
+ const auto refreshRate = Fps::fromPeriodNsecs(*averageFrameTime);
+ const bool refreshRateConsistent = mRefreshRateHistory.add(refreshRate, now);
+ if (refreshRateConsistent) {
+ const auto knownRefreshRate =
+ sRefreshRateConfigs->findClosestKnownFrameRate(refreshRate);
+
+ // To avoid oscillation, use the last calculated refresh rate if it is
+ // close enough
+ if (std::abs(mLastRefreshRate.calculated.getValue() - refreshRate.getValue()) >
+ MARGIN &&
+ !mLastRefreshRate.reported.equalsWithMargin(knownRefreshRate)) {
+ mLastRefreshRate.calculated = refreshRate;
+ mLastRefreshRate.reported = knownRefreshRate;
+ }
+
+ ALOGV("%s %s rounded to nearest known frame rate %s", mName.c_str(),
+ to_string(refreshRate).c_str(), to_string(mLastRefreshRate.reported).c_str());
+ } else {
+ ALOGV("%s Not stable (%s) returning last known frame rate %s", mName.c_str(),
+ to_string(refreshRate).c_str(), to_string(mLastRefreshRate.reported).c_str());
+ }
+ }
+
+ return mLastRefreshRate.reported.isValid() ? std::make_optional(mLastRefreshRate.reported)
+ : std::nullopt;
+}
+
+LayerInfo::LayerVote LayerInfo::getRefreshRateVote(nsecs_t now) {
+ if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
+ ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type));
+ return mLayerVote;
+ }
+
+ if (isAnimating(now)) {
+ ALOGV("%s is animating", mName.c_str());
+ mLastRefreshRate.animatingOrInfrequent = true;
+ return {LayerHistory::LayerVoteType::Max, Fps(0.0f)};
+ }
+
+ if (!isFrequent(now)) {
+ ALOGV("%s is infrequent", mName.c_str());
+ mLastRefreshRate.animatingOrInfrequent = true;
+ // Infrequent layers vote for mininal refresh rate for
+ // battery saving purposes and also to prevent b/135718869.
+ return {LayerHistory::LayerVoteType::Min, Fps(0.0f)};
+ }
+
+ // If the layer was previously tagged as animating or infrequent, we clear
+ // the history as it is likely the layer just changed its behavior
+ // and we should not look at stale data
+ if (mLastRefreshRate.animatingOrInfrequent) {
+ clearHistory(now);
+ }
+
+ auto refreshRate = calculateRefreshRateIfPossible(now);
+ if (refreshRate.has_value()) {
+ ALOGV("%s calculated refresh rate: %s", mName.c_str(), to_string(*refreshRate).c_str());
+ return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
+ }
+
+ ALOGV("%s Max (can't resolve refresh rate)", mName.c_str());
+ return {LayerHistory::LayerVoteType::Max, Fps(0.0f)};
+}
+
+const char* LayerInfo::getTraceTag(android::scheduler::LayerHistory::LayerVoteType type) const {
+ if (mTraceTags.count(type) == 0) {
+ const auto tag = "LFPS " + mName + " " + RefreshRateConfigs::layerVoteTypeString(type);
+ mTraceTags.emplace(type, tag);
+ }
+
+ return mTraceTags.at(type).c_str();
+}
+
+LayerInfo::RefreshRateHistory::HeuristicTraceTagData
+LayerInfo::RefreshRateHistory::makeHeuristicTraceTagData() const {
+ const std::string prefix = "LFPS ";
+ const std::string suffix = "Heuristic ";
+ return {.min = prefix + mName + suffix + "min",
+ .max = prefix + mName + suffix + "max",
+ .consistent = prefix + mName + suffix + "consistent",
+ .average = prefix + mName + suffix + "average"};
+}
+
+void LayerInfo::RefreshRateHistory::clear() {
+ mRefreshRates.clear();
+}
+
+bool LayerInfo::RefreshRateHistory::add(Fps refreshRate, nsecs_t now) {
+ mRefreshRates.push_back({refreshRate, now});
+ while (mRefreshRates.size() >= HISTORY_SIZE ||
+ now - mRefreshRates.front().timestamp > HISTORY_DURATION.count()) {
+ mRefreshRates.pop_front();
+ }
+
+ if (CC_UNLIKELY(sTraceEnabled)) {
+ if (!mHeuristicTraceTagData.has_value()) {
+ mHeuristicTraceTagData = makeHeuristicTraceTagData();
+ }
+
+ ATRACE_INT(mHeuristicTraceTagData->average.c_str(), refreshRate.getIntValue());
+ }
+
+ return isConsistent();
+}
+
+bool LayerInfo::RefreshRateHistory::isConsistent() const {
+ if (mRefreshRates.empty()) return true;
+
+ const auto max = std::max_element(mRefreshRates.begin(), mRefreshRates.end());
+ const auto min = std::min_element(mRefreshRates.begin(), mRefreshRates.end());
+ const auto consistent =
+ max->refreshRate.getValue() - min->refreshRate.getValue() < MARGIN_CONSISTENT_FPS;
+
+ if (CC_UNLIKELY(sTraceEnabled)) {
+ if (!mHeuristicTraceTagData.has_value()) {
+ mHeuristicTraceTagData = makeHeuristicTraceTagData();
+ }
+
+ ATRACE_INT(mHeuristicTraceTagData->max.c_str(), max->refreshRate.getIntValue());
+ ATRACE_INT(mHeuristicTraceTagData->min.c_str(), min->refreshRate.getIntValue());
+ ATRACE_INT(mHeuristicTraceTagData->consistent.c_str(), consistent);
+ }
+
+ return consistent;
}
} // namespace android::scheduler
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 820624b..ba03c89 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 The Android Open Source Project
+ * Copyright 2020 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.
@@ -16,11 +16,15 @@
#pragma once
+#include <ui/Transform.h>
#include <utils/Timers.h>
#include <chrono>
#include <deque>
+#include "LayerHistory.h"
+#include "RefreshRateConfigs.h"
+#include "Scheduler/Seamlessness.h"
#include "SchedulerUtils.h"
namespace android {
@@ -41,129 +45,263 @@
// Stores history of present times and refresh rates for a layer.
class LayerInfo {
+ using LayerUpdateType = LayerHistory::LayerUpdateType;
+
// Layer is considered frequent if the earliest value in the window of most recent present times
// is within a threshold. If a layer is infrequent, its average refresh rate is disregarded in
// favor of a low refresh rate.
- static constexpr size_t FREQUENT_LAYER_WINDOW_SIZE = 3;
- static constexpr std::chrono::nanoseconds MAX_FREQUENT_LAYER_PERIOD_NS = 250ms;
-
- /**
- * Struct that keeps the information about the refresh rate for last
- * HISTORY_SIZE frames. This is used to better determine the refresh rate
- * for individual layers.
- */
- class RefreshRateHistory {
- public:
- explicit RefreshRateHistory(float highRefreshRate) : mHighRefreshRate(highRefreshRate) {}
-
- void insertRefreshRate(float refreshRate) {
- mElements.push_back(refreshRate);
- if (mElements.size() > HISTORY_SIZE) {
- mElements.pop_front();
- }
- }
-
- float getRefreshRateAvg() const {
- return mElements.empty() ? mHighRefreshRate : calculate_mean(mElements);
- }
-
- void clearHistory() { mElements.clear(); }
-
- private:
- const float mHighRefreshRate;
-
- static constexpr size_t HISTORY_SIZE = 30;
- std::deque<float> mElements;
- };
-
- /**
- * Struct that keeps the information about the present time for last
- * HISTORY_SIZE frames. This is used to better determine whether the given layer
- * is still relevant and it's refresh rate should be considered.
- */
- class PresentTimeHistory {
- public:
- static constexpr size_t HISTORY_SIZE = 90;
-
- void insertPresentTime(nsecs_t presentTime) {
- mElements.push_back(presentTime);
- if (mElements.size() > HISTORY_SIZE) {
- mElements.pop_front();
- }
- }
-
- // Returns whether the earliest present time is within the active threshold.
- bool isRecentlyActive(nsecs_t now) const {
- if (mElements.size() < 2) {
- return false;
- }
-
- // The layer had to publish at least HISTORY_SIZE or HISTORY_DURATION of updates
- if (mElements.size() < HISTORY_SIZE &&
- mElements.back() - mElements.front() < HISTORY_DURATION.count()) {
- return false;
- }
-
- return mElements.back() >= getActiveLayerThreshold(now);
- }
-
- bool isFrequent(nsecs_t now) const {
- // Assume layer is infrequent if too few present times have been recorded.
- if (mElements.size() < FREQUENT_LAYER_WINDOW_SIZE) {
- return false;
- }
-
- // Layer is frequent if the earliest value in the window of most recent present times is
- // within threshold.
- const auto it = mElements.end() - FREQUENT_LAYER_WINDOW_SIZE;
- const nsecs_t threshold = now - MAX_FREQUENT_LAYER_PERIOD_NS.count();
- return *it >= threshold;
- }
-
- void clearHistory() { mElements.clear(); }
-
- private:
- std::deque<nsecs_t> mElements;
- static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s;
- };
+ static constexpr size_t kFrequentLayerWindowSize = 3;
+ static constexpr Fps kMinFpsForFrequentLayer{10.0f};
+ static constexpr auto kMaxPeriodForFrequentLayerNs =
+ std::chrono::nanoseconds(kMinFpsForFrequentLayer.getPeriodNsecs()) + 1ms;
friend class LayerHistoryTest;
+ friend class LayerInfoTest;
public:
- LayerInfo(float lowRefreshRate, float highRefreshRate);
+ // Holds information about the layer vote
+ struct LayerVote {
+ LayerHistory::LayerVoteType type = LayerHistory::LayerVoteType::Heuristic;
+ Fps fps{0.0f};
+ Seamlessness seamlessness = Seamlessness::Default;
+ };
+
+ // FrameRateCompatibility specifies how we should interpret the frame rate associated with
+ // the layer.
+ enum class FrameRateCompatibility {
+ Default, // Layer didn't specify any specific handling strategy
+
+ Exact, // Layer needs the exact frame rate.
+
+ ExactOrMultiple, // Layer needs the exact frame rate (or a multiple of it) to present the
+ // content properly. Any other value will result in a pull down.
+
+ NoVote, // Layer doesn't have any requirements for the refresh rate and
+ // should not be considered when the display refresh rate is determined.
+ };
+
+ // Encapsulates the frame rate and compatibility of the layer. This information will be used
+ // when the display refresh rate is determined.
+ struct FrameRate {
+ using Seamlessness = scheduler::Seamlessness;
+
+ Fps rate;
+ FrameRateCompatibility type;
+ Seamlessness seamlessness;
+
+ FrameRate()
+ : rate(0),
+ type(FrameRateCompatibility::Default),
+ seamlessness(Seamlessness::Default) {}
+ FrameRate(Fps rate, FrameRateCompatibility type,
+ Seamlessness seamlessness = Seamlessness::OnlySeamless)
+ : rate(rate), type(type), seamlessness(getSeamlessness(rate, seamlessness)) {}
+
+ bool operator==(const FrameRate& other) const {
+ return rate.equalsWithMargin(other.rate) && type == other.type &&
+ seamlessness == other.seamlessness;
+ }
+
+ bool operator!=(const FrameRate& other) const { return !(*this == other); }
+
+ // Convert an ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* value to a
+ // Layer::FrameRateCompatibility. Logs fatal if the compatibility value is invalid.
+ static FrameRateCompatibility convertCompatibility(int8_t compatibility);
+ static scheduler::Seamlessness convertChangeFrameRateStrategy(int8_t strategy);
+
+ private:
+ static Seamlessness getSeamlessness(Fps rate, Seamlessness seamlessness) {
+ if (!rate.isValid()) {
+ // Refresh rate of 0 is a special value which should reset the vote to
+ // its default value.
+ return Seamlessness::Default;
+ }
+ return seamlessness;
+ }
+ };
+
+ static void setTraceEnabled(bool enabled) { sTraceEnabled = enabled; }
+
+ static void setRefreshRateConfigs(const RefreshRateConfigs& refreshRateConfigs) {
+ sRefreshRateConfigs = &refreshRateConfigs;
+ }
+
+ LayerInfo(const std::string& name, uid_t ownerUid, LayerHistory::LayerVoteType defaultVote);
LayerInfo(const LayerInfo&) = delete;
LayerInfo& operator=(const LayerInfo&) = delete;
- // Records the last requested oresent time. It also stores information about when
+ struct LayerProps {
+ bool visible = false;
+ FloatRect bounds;
+ ui::Transform transform;
+ FrameRate setFrameRateVote;
+ int32_t frameRateSelectionPriority = -1;
+ };
+
+ // Records the last requested present time. It also stores information about when
// the layer was last updated. If the present time is farther in the future than the
// updated time, the updated time is the present time.
- void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now);
+ void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
+ bool pendingModeChange, LayerProps props);
- bool isRecentlyActive(nsecs_t now) const { return mPresentTimeHistory.isRecentlyActive(now); }
- bool isFrequent(nsecs_t now) const { return mPresentTimeHistory.isFrequent(now); }
+ // Sets an explicit layer vote. This usually comes directly from the application via
+ // ANativeWindow_setFrameRate API
+ void setLayerVote(LayerVote vote) { mLayerVote = vote; }
- float getRefreshRate(nsecs_t now) const {
- return isFrequent(now) ? mRefreshRateHistory.getRefreshRateAvg() : mLowRefreshRate;
- }
+ // Sets the default layer vote. This will be the layer vote after calling to resetLayerVote().
+ // This is used for layers that called to setLayerVote() and then removed the vote, so that the
+ // layer can go back to whatever vote it had before the app voted for it.
+ void setDefaultLayerVote(LayerHistory::LayerVoteType type) { mDefaultVote = type; }
+
+ // Resets the layer vote to its default.
+ void resetLayerVote() { mLayerVote = {mDefaultVote, Fps(0.0f), Seamlessness::Default}; }
+
+ std::string getName() const { return mName; }
+
+ uid_t getOwnerUid() const { return mOwnerUid; }
+
+ LayerVote getRefreshRateVote(nsecs_t now);
// Return the last updated time. If the present time is farther in the future than the
// updated time, the updated time is the present time.
nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; }
- void clearHistory() {
- mRefreshRateHistory.clearHistory();
- mPresentTimeHistory.clearHistory();
+ FrameRate getSetFrameRateVote() const { return mLayerProps.setFrameRateVote; }
+ bool isVisible() const { return mLayerProps.visible; }
+ int32_t getFrameRateSelectionPriority() const { return mLayerProps.frameRateSelectionPriority; }
+
+ FloatRect getBounds() const { return mLayerProps.bounds; }
+
+ ui::Transform getTransform() const { return mLayerProps.transform; }
+
+ // Returns a C string for tracing a vote
+ const char* getTraceTag(LayerHistory::LayerVoteType type) const;
+
+ void onLayerInactive(nsecs_t now) {
+ // Mark mFrameTimeValidSince to now to ignore all previous frame times.
+ // We are not deleting the old frame to keep track of whether we should treat the first
+ // buffer as Max as we don't know anything about this layer or Min as this layer is
+ // posting infrequent updates.
+ const auto timePoint = std::chrono::nanoseconds(now);
+ mFrameTimeValidSince = std::chrono::time_point<std::chrono::steady_clock>(timePoint);
+ mLastRefreshRate = {};
+ mRefreshRateHistory.clear();
+ }
+
+ void clearHistory(nsecs_t now) {
+ onLayerInactive(now);
+ mFrameTimes.clear();
}
private:
- const float mLowRefreshRate;
- const float mHighRefreshRate;
+ // Used to store the layer timestamps
+ struct FrameTimeData {
+ nsecs_t presentTime; // desiredPresentTime, if provided
+ nsecs_t queueTime; // buffer queue time
+ bool pendingModeChange;
+ };
+
+ // Holds information about the calculated and reported refresh rate
+ struct RefreshRateHeuristicData {
+ // Rate calculated on the layer
+ Fps calculated{0.0f};
+ // Last reported rate for LayerInfo::getRefreshRate()
+ Fps reported{0.0f};
+ // Whether the last reported rate for LayerInfo::getRefreshRate()
+ // was due to animation or infrequent updates
+ bool animatingOrInfrequent = false;
+ };
+
+ // Class to store past calculated refresh rate and determine whether
+ // the refresh rate calculated is consistent with past values
+ class RefreshRateHistory {
+ public:
+ static constexpr auto HISTORY_SIZE = 90;
+ static constexpr std::chrono::nanoseconds HISTORY_DURATION = 2s;
+
+ RefreshRateHistory(const std::string& name) : mName(name) {}
+
+ // Clears History
+ void clear();
+
+ // Adds a new refresh rate and returns true if it is consistent
+ bool add(Fps refreshRate, nsecs_t now);
+
+ private:
+ friend class LayerHistoryTest;
+
+ // Holds the refresh rate when it was calculated
+ struct RefreshRateData {
+ Fps refreshRate{0.0f};
+ nsecs_t timestamp = 0;
+
+ bool operator<(const RefreshRateData& other) const {
+ // We don't need comparison with margins since we are using
+ // this to find the min and max refresh rates.
+ return refreshRate.getValue() < other.refreshRate.getValue();
+ }
+ };
+
+ // Holds tracing strings
+ struct HeuristicTraceTagData {
+ std::string min;
+ std::string max;
+ std::string consistent;
+ std::string average;
+ };
+
+ bool isConsistent() const;
+ HeuristicTraceTagData makeHeuristicTraceTagData() const;
+
+ const std::string mName;
+ mutable std::optional<HeuristicTraceTagData> mHeuristicTraceTagData;
+ std::deque<RefreshRateData> mRefreshRates;
+ static constexpr float MARGIN_CONSISTENT_FPS = 1.0;
+ };
+
+ bool isFrequent(nsecs_t now) const;
+ bool isAnimating(nsecs_t now) const;
+ bool hasEnoughDataForHeuristic() const;
+ std::optional<Fps> calculateRefreshRateIfPossible(nsecs_t now);
+ std::optional<nsecs_t> calculateAverageFrameTime() const;
+ bool isFrameTimeValid(const FrameTimeData&) const;
+
+ const std::string mName;
+ const uid_t mOwnerUid;
+
+ // Used for sanitizing the heuristic data. If two frames are less than
+ // this period apart from each other they'll be considered as duplicates.
+ static constexpr nsecs_t kMinPeriodBetweenFrames = Fps(120.f).getPeriodNsecs();
+ // Used for sanitizing the heuristic data. If two frames are more than
+ // this period apart from each other, the interval between them won't be
+ // taken into account when calculating average frame rate.
+ static constexpr nsecs_t kMaxPeriodBetweenFrames = kMinFpsForFrequentLayer.getPeriodNsecs();
+ LayerHistory::LayerVoteType mDefaultVote;
+
+ LayerVote mLayerVote;
nsecs_t mLastUpdatedTime = 0;
- nsecs_t mLastPresentTime = 0;
- RefreshRateHistory mRefreshRateHistory{mHighRefreshRate};
- PresentTimeHistory mPresentTimeHistory;
+
+ nsecs_t mLastAnimationTime = 0;
+
+ RefreshRateHeuristicData mLastRefreshRate;
+
+ std::deque<FrameTimeData> mFrameTimes;
+ std::chrono::time_point<std::chrono::steady_clock> mFrameTimeValidSince =
+ std::chrono::steady_clock::now();
+ static constexpr size_t HISTORY_SIZE = RefreshRateHistory::HISTORY_SIZE;
+ static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s;
+
+ LayerProps mLayerProps;
+
+ RefreshRateHistory mRefreshRateHistory;
+
+ mutable std::unordered_map<LayerHistory::LayerVoteType, std::string> mTraceTags;
+
+ // Shared for all LayerInfo instances
+ static const RefreshRateConfigs* sRefreshRateConfigs;
+ static bool sTraceEnabled;
};
} // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
deleted file mode 100644
index 44f20d0..0000000
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
+++ /dev/null
@@ -1,297 +0,0 @@
-/*
- * Copyright 2020 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.
- */
-
-// #define LOG_NDEBUG 0
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "LayerInfoV2.h"
-
-#include <algorithm>
-#include <utility>
-
-#include <cutils/compiler.h>
-#include <cutils/trace.h>
-
-#undef LOG_TAG
-#define LOG_TAG "LayerInfoV2"
-
-namespace android::scheduler {
-
-const RefreshRateConfigs* LayerInfoV2::sRefreshRateConfigs = nullptr;
-bool LayerInfoV2::sTraceEnabled = false;
-
-LayerInfoV2::LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod,
- LayerHistory::LayerVoteType defaultVote)
- : mName(name),
- mHighRefreshRatePeriod(highRefreshRatePeriod),
- mDefaultVote(defaultVote),
- mLayerVote({defaultVote, 0.0f}),
- mRefreshRateHistory(name) {}
-
-void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now,
- LayerUpdateType updateType, bool pendingConfigChange) {
- lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
-
- mLastUpdatedTime = std::max(lastPresentTime, now);
- switch (updateType) {
- case LayerUpdateType::AnimationTX:
- mLastAnimationTime = std::max(lastPresentTime, now);
- break;
- case LayerUpdateType::SetFrameRate:
- case LayerUpdateType::Buffer:
- FrameTimeData frameTime = {.presetTime = lastPresentTime,
- .queueTime = mLastUpdatedTime,
- .pendingConfigChange = pendingConfigChange};
- mFrameTimes.push_back(frameTime);
- if (mFrameTimes.size() > HISTORY_SIZE) {
- mFrameTimes.pop_front();
- }
- break;
- }
-}
-
-bool LayerInfoV2::isFrameTimeValid(const FrameTimeData& frameTime) const {
- return frameTime.queueTime >= std::chrono::duration_cast<std::chrono::nanoseconds>(
- mFrameTimeValidSince.time_since_epoch())
- .count();
-}
-
-bool LayerInfoV2::isFrequent(nsecs_t now) const {
- // If we know nothing about this layer we consider it as frequent as it might be the start
- // of an animation.
- if (mFrameTimes.size() < FREQUENT_LAYER_WINDOW_SIZE) {
- return true;
- }
-
- // Find the first active frame
- auto it = mFrameTimes.begin();
- for (; it != mFrameTimes.end(); ++it) {
- if (it->queueTime >= getActiveLayerThreshold(now)) {
- break;
- }
- }
-
- const auto numFrames = std::distance(it, mFrameTimes.end());
- if (numFrames < FREQUENT_LAYER_WINDOW_SIZE) {
- return false;
- }
-
- // Layer is considered frequent if the average frame rate is higher than the threshold
- const auto totalTime = mFrameTimes.back().queueTime - it->queueTime;
- return (1e9f * (numFrames - 1)) / totalTime >= MIN_FPS_FOR_FREQUENT_LAYER;
-}
-
-bool LayerInfoV2::isAnimating(nsecs_t now) const {
- return mLastAnimationTime >= getActiveLayerThreshold(now);
-}
-
-bool LayerInfoV2::hasEnoughDataForHeuristic() const {
- // The layer had to publish at least HISTORY_SIZE or HISTORY_DURATION of updates
- if (mFrameTimes.size() < 2) {
- ALOGV("fewer than 2 frames recorded: %zu", mFrameTimes.size());
- return false;
- }
-
- if (!isFrameTimeValid(mFrameTimes.front())) {
- ALOGV("stale frames still captured");
- return false;
- }
-
- const auto totalDuration = mFrameTimes.back().queueTime - mFrameTimes.front().queueTime;
- if (mFrameTimes.size() < HISTORY_SIZE && totalDuration < HISTORY_DURATION.count()) {
- ALOGV("not enough frames captured: %zu | %.2f seconds", mFrameTimes.size(),
- totalDuration / 1e9f);
- return false;
- }
-
- return true;
-}
-
-std::optional<nsecs_t> LayerInfoV2::calculateAverageFrameTime() const {
- nsecs_t totalPresentTimeDeltas = 0;
- nsecs_t totalQueueTimeDeltas = 0;
- bool missingPresentTime = false;
- int numFrames = 0;
- for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
- // Ignore frames captured during a config change
- if (it->pendingConfigChange || (it + 1)->pendingConfigChange) {
- return std::nullopt;
- }
-
- totalQueueTimeDeltas +=
- std::max(((it + 1)->queueTime - it->queueTime), mHighRefreshRatePeriod);
- numFrames++;
-
- if (!missingPresentTime && (it->presetTime == 0 || (it + 1)->presetTime == 0)) {
- missingPresentTime = true;
- // If there are no presentation timestamps and we haven't calculated
- // one in the past then we can't calculate the refresh rate
- if (mLastRefreshRate.reported == 0) {
- return std::nullopt;
- }
- continue;
- }
-
- totalPresentTimeDeltas +=
- std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
- }
-
- // Calculate the average frame time based on presentation timestamps. If those
- // doesn't exist, we look at the time the buffer was queued only. We can do that only if
- // we calculated a refresh rate based on presentation timestamps in the past. The reason
- // we look at the queue time is to handle cases where hwui attaches presentation timestamps
- // when implementing render ahead for specific refresh rates. When hwui no longer provides
- // presentation timestamps we look at the queue time to see if the current refresh rate still
- // matches the content.
-
- const auto averageFrameTime =
- static_cast<float>(missingPresentTime ? totalQueueTimeDeltas : totalPresentTimeDeltas) /
- numFrames;
- return static_cast<nsecs_t>(averageFrameTime);
-}
-
-std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible(nsecs_t now) {
- static constexpr float MARGIN = 1.0f; // 1Hz
- if (!hasEnoughDataForHeuristic()) {
- ALOGV("Not enough data");
- return std::nullopt;
- }
-
- const auto averageFrameTime = calculateAverageFrameTime();
- if (averageFrameTime.has_value()) {
- const auto refreshRate = 1e9f / *averageFrameTime;
- const bool refreshRateConsistent = mRefreshRateHistory.add(refreshRate, now);
- if (refreshRateConsistent) {
- const auto knownRefreshRate =
- sRefreshRateConfigs->findClosestKnownFrameRate(refreshRate);
-
- // To avoid oscillation, use the last calculated refresh rate if it is
- // close enough
- if (std::abs(mLastRefreshRate.calculated - refreshRate) > MARGIN &&
- mLastRefreshRate.reported != knownRefreshRate) {
- mLastRefreshRate.calculated = refreshRate;
- mLastRefreshRate.reported = knownRefreshRate;
- }
-
- ALOGV("%s %.2fHz rounded to nearest known frame rate %.2fHz", mName.c_str(),
- refreshRate, mLastRefreshRate.reported);
- } else {
- ALOGV("%s Not stable (%.2fHz) returning last known frame rate %.2fHz", mName.c_str(),
- refreshRate, mLastRefreshRate.reported);
- }
- }
-
- return mLastRefreshRate.reported == 0 ? std::nullopt
- : std::make_optional(mLastRefreshRate.reported);
-}
-
-std::pair<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_t now) {
- if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
- ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type));
- return {mLayerVote.type, mLayerVote.fps};
- }
-
- if (isAnimating(now)) {
- ALOGV("%s is animating", mName.c_str());
- mLastRefreshRate.animatingOrInfrequent = true;
- return {LayerHistory::LayerVoteType::Max, 0};
- }
-
- if (!isFrequent(now)) {
- ALOGV("%s is infrequent", mName.c_str());
- mLastRefreshRate.animatingOrInfrequent = true;
- return {LayerHistory::LayerVoteType::Min, 0};
- }
-
- // If the layer was previously tagged as animating or infrequent, we clear
- // the history as it is likely the layer just changed its behavior
- // and we should not look at stale data
- if (mLastRefreshRate.animatingOrInfrequent) {
- clearHistory(now);
- }
-
- auto refreshRate = calculateRefreshRateIfPossible(now);
- if (refreshRate.has_value()) {
- ALOGV("%s calculated refresh rate: %.2f", mName.c_str(), refreshRate.value());
- return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
- }
-
- ALOGV("%s Max (can't resolve refresh rate)", mName.c_str());
- return {LayerHistory::LayerVoteType::Max, 0};
-}
-
-const char* LayerInfoV2::getTraceTag(android::scheduler::LayerHistory::LayerVoteType type) const {
- if (mTraceTags.count(type) == 0) {
- const auto tag = "LFPS " + mName + " " + RefreshRateConfigs::layerVoteTypeString(type);
- mTraceTags.emplace(type, tag);
- }
-
- return mTraceTags.at(type).c_str();
-}
-
-LayerInfoV2::RefreshRateHistory::HeuristicTraceTagData
-LayerInfoV2::RefreshRateHistory::makeHeuristicTraceTagData() const {
- const std::string prefix = "LFPS ";
- const std::string suffix = "Heuristic ";
- return {.min = prefix + mName + suffix + "min",
- .max = prefix + mName + suffix + "max",
- .consistent = prefix + mName + suffix + "consistent",
- .average = prefix + mName + suffix + "average"};
-}
-
-void LayerInfoV2::RefreshRateHistory::clear() {
- mRefreshRates.clear();
-}
-
-bool LayerInfoV2::RefreshRateHistory::add(float refreshRate, nsecs_t now) {
- mRefreshRates.push_back({refreshRate, now});
- while (mRefreshRates.size() >= HISTORY_SIZE ||
- now - mRefreshRates.front().timestamp > HISTORY_DURATION.count()) {
- mRefreshRates.pop_front();
- }
-
- if (CC_UNLIKELY(sTraceEnabled)) {
- if (!mHeuristicTraceTagData.has_value()) {
- mHeuristicTraceTagData = makeHeuristicTraceTagData();
- }
-
- ATRACE_INT(mHeuristicTraceTagData->average.c_str(), static_cast<int>(refreshRate));
- }
-
- return isConsistent();
-}
-
-bool LayerInfoV2::RefreshRateHistory::isConsistent() const {
- if (mRefreshRates.empty()) return true;
-
- const auto max = std::max_element(mRefreshRates.begin(), mRefreshRates.end());
- const auto min = std::min_element(mRefreshRates.begin(), mRefreshRates.end());
- const auto consistent = max->refreshRate - min->refreshRate <= MARGIN_FPS;
-
- if (CC_UNLIKELY(sTraceEnabled)) {
- if (!mHeuristicTraceTagData.has_value()) {
- mHeuristicTraceTagData = makeHeuristicTraceTagData();
- }
-
- ATRACE_INT(mHeuristicTraceTagData->max.c_str(), static_cast<int>(max->refreshRate));
- ATRACE_INT(mHeuristicTraceTagData->min.c_str(), static_cast<int>(min->refreshRate));
- ATRACE_INT(mHeuristicTraceTagData->consistent.c_str(), consistent);
- }
-
- return consistent;
-}
-
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.h b/services/surfaceflinger/Scheduler/LayerInfoV2.h
deleted file mode 100644
index 33dc66f..0000000
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.h
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * Copyright 2020 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.
- */
-
-#pragma once
-
-#include <utils/Timers.h>
-
-#include <chrono>
-#include <deque>
-
-#include "LayerHistory.h"
-#include "RefreshRateConfigs.h"
-#include "SchedulerUtils.h"
-
-namespace android {
-
-class Layer;
-
-namespace scheduler {
-
-using namespace std::chrono_literals;
-
-// Maximum period between presents for a layer to be considered active.
-constexpr std::chrono::nanoseconds MAX_ACTIVE_LAYER_PERIOD_NS = 1200ms;
-
-// Earliest present time for a layer to be considered active.
-constexpr nsecs_t getActiveLayerThreshold(nsecs_t now) {
- return now - MAX_ACTIVE_LAYER_PERIOD_NS.count();
-}
-
-// Stores history of present times and refresh rates for a layer.
-class LayerInfoV2 {
- using LayerUpdateType = LayerHistory::LayerUpdateType;
-
- // Layer is considered frequent if the earliest value in the window of most recent present times
- // is within a threshold. If a layer is infrequent, its average refresh rate is disregarded in
- // favor of a low refresh rate.
- static constexpr size_t FREQUENT_LAYER_WINDOW_SIZE = 3;
- static constexpr float MIN_FPS_FOR_FREQUENT_LAYER = 10.0f;
- static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS =
- std::chrono::nanoseconds(static_cast<nsecs_t>(1e9f / MIN_FPS_FOR_FREQUENT_LAYER)) + 1ms;
-
- friend class LayerHistoryTestV2;
-
-public:
- static void setTraceEnabled(bool enabled) { sTraceEnabled = enabled; }
-
- static void setRefreshRateConfigs(const RefreshRateConfigs& refreshRateConfigs) {
- sRefreshRateConfigs = &refreshRateConfigs;
- }
-
- LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod,
- LayerHistory::LayerVoteType defaultVote);
-
- LayerInfoV2(const LayerInfo&) = delete;
- LayerInfoV2& operator=(const LayerInfoV2&) = delete;
-
- // Records the last requested present time. It also stores information about when
- // the layer was last updated. If the present time is farther in the future than the
- // updated time, the updated time is the present time.
- void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
- bool pendingConfigChange);
-
- // Sets an explicit layer vote. This usually comes directly from the application via
- // ANativeWindow_setFrameRate API
- void setLayerVote(LayerHistory::LayerVoteType type, float fps) { mLayerVote = {type, fps}; }
-
- // Sets the default layer vote. This will be the layer vote after calling to resetLayerVote().
- // This is used for layers that called to setLayerVote() and then removed the vote, so that the
- // layer can go back to whatever vote it had before the app voted for it.
- void setDefaultLayerVote(LayerHistory::LayerVoteType type) { mDefaultVote = type; }
-
- // Resets the layer vote to its default.
- void resetLayerVote() { mLayerVote = {mDefaultVote, 0.0f}; }
-
- std::pair<LayerHistory::LayerVoteType, float> getRefreshRate(nsecs_t now);
-
- // Return the last updated time. If the present time is farther in the future than the
- // updated time, the updated time is the present time.
- nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; }
-
- // Returns a C string for tracing a vote
- const char* getTraceTag(LayerHistory::LayerVoteType type) const;
-
- void onLayerInactive(nsecs_t now) {
- // Mark mFrameTimeValidSince to now to ignore all previous frame times.
- // We are not deleting the old frame to keep track of whether we should treat the first
- // buffer as Max as we don't know anything about this layer or Min as this layer is
- // posting infrequent updates.
- const auto timePoint = std::chrono::nanoseconds(now);
- mFrameTimeValidSince = std::chrono::time_point<std::chrono::steady_clock>(timePoint);
- mLastRefreshRate = {};
- mRefreshRateHistory.clear();
- }
-
- void clearHistory(nsecs_t now) {
- onLayerInactive(now);
- mFrameTimes.clear();
- }
-
-private:
- // Used to store the layer timestamps
- struct FrameTimeData {
- nsecs_t presetTime; // desiredPresentTime, if provided
- nsecs_t queueTime; // buffer queue time
- bool pendingConfigChange;
- };
-
- // Holds information about the calculated and reported refresh rate
- struct RefreshRateHeuristicData {
- // Rate calculated on the layer
- float calculated = 0.0f;
- // Last reported rate for LayerInfoV2::getRefreshRate()
- float reported = 0.0f;
- // Whether the last reported rate for LayerInfoV2::getRefreshRate()
- // was due to animation or infrequent updates
- bool animatingOrInfrequent = false;
- };
-
- // Holds information about the layer vote
- struct LayerVote {
- LayerHistory::LayerVoteType type = LayerHistory::LayerVoteType::Heuristic;
- float fps = 0.0f;
- };
-
- // Class to store past calculated refresh rate and determine whether
- // the refresh rate calculated is consistent with past values
- class RefreshRateHistory {
- public:
- static constexpr auto HISTORY_SIZE = 90;
- static constexpr std::chrono::nanoseconds HISTORY_DURATION = 2s;
-
- RefreshRateHistory(const std::string& name) : mName(name) {}
-
- // Clears History
- void clear();
-
- // Adds a new refresh rate and returns true if it is consistent
- bool add(float refreshRate, nsecs_t now);
-
- private:
- friend class LayerHistoryTestV2;
-
- // Holds the refresh rate when it was calculated
- struct RefreshRateData {
- float refreshRate = 0.0f;
- nsecs_t timestamp = 0;
-
- bool operator<(const RefreshRateData& other) const {
- return refreshRate < other.refreshRate;
- }
- };
-
- // Holds tracing strings
- struct HeuristicTraceTagData {
- std::string min;
- std::string max;
- std::string consistent;
- std::string average;
- };
-
- bool isConsistent() const;
- HeuristicTraceTagData makeHeuristicTraceTagData() const;
-
- const std::string mName;
- mutable std::optional<HeuristicTraceTagData> mHeuristicTraceTagData;
- std::deque<RefreshRateData> mRefreshRates;
- static constexpr float MARGIN_FPS = 1.0;
- };
-
- bool isFrequent(nsecs_t now) const;
- bool isAnimating(nsecs_t now) const;
- bool hasEnoughDataForHeuristic() const;
- std::optional<float> calculateRefreshRateIfPossible(nsecs_t now);
- std::optional<nsecs_t> calculateAverageFrameTime() const;
- bool isFrameTimeValid(const FrameTimeData&) const;
-
- const std::string mName;
-
- // Used for sanitizing the heuristic data
- const nsecs_t mHighRefreshRatePeriod;
- LayerHistory::LayerVoteType mDefaultVote;
-
- LayerVote mLayerVote;
-
- nsecs_t mLastUpdatedTime = 0;
-
- nsecs_t mLastAnimationTime = 0;
-
- RefreshRateHeuristicData mLastRefreshRate;
-
- std::deque<FrameTimeData> mFrameTimes;
- std::chrono::time_point<std::chrono::steady_clock> mFrameTimeValidSince =
- std::chrono::steady_clock::now();
- static constexpr size_t HISTORY_SIZE = RefreshRateHistory::HISTORY_SIZE;
- static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s;
-
- RefreshRateHistory mRefreshRateHistory;
-
- mutable std::unordered_map<LayerHistory::LayerVoteType, std::string> mTraceTags;
-
- // Shared for all LayerInfo instances
- static const RefreshRateConfigs* sRefreshRateConfigs;
- static bool sTraceEnabled;
-};
-
-} // namespace scheduler
-} // namespace android
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 6067e69..7ff0ddf 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <binder/IPCThreadState.h>
@@ -28,6 +26,7 @@
#include <gui/IDisplayEventConnection.h>
#include "EventThread.h"
+#include "FrameTimeline.h"
#include "MessageQueue.h"
#include "SurfaceFlinger.h"
@@ -39,8 +38,9 @@
}
}
-void MessageQueue::Handler::dispatchInvalidate(nsecs_t expectedVSyncTimestamp) {
+void MessageQueue::Handler::dispatchInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTimestamp) {
if ((android_atomic_or(eventMaskInvalidate, &mEventMask) & eventMaskInvalidate) == 0) {
+ mVsyncId = vsyncId;
mExpectedVSyncTime = expectedVSyncTimestamp;
mQueue.mLooper->sendMessage(this, Message(MessageQueue::INVALIDATE));
}
@@ -50,11 +50,11 @@
switch (message.what) {
case INVALIDATE:
android_atomic_and(~eventMaskInvalidate, &mEventMask);
- mQueue.mFlinger->onMessageReceived(message.what, mExpectedVSyncTime);
+ mQueue.mFlinger->onMessageReceived(message.what, mVsyncId, mExpectedVSyncTime);
break;
case REFRESH:
android_atomic_and(~eventMaskRefresh, &mEventMask);
- mQueue.mFlinger->onMessageReceived(message.what, mExpectedVSyncTime);
+ mQueue.mFlinger->onMessageReceived(message.what, mVsyncId, mExpectedVSyncTime);
break;
}
}
@@ -67,15 +67,74 @@
mHandler = new Handler(*this);
}
-void MessageQueue::setEventConnection(const sp<EventThreadConnection>& connection) {
- if (mEventTube.getFd() >= 0) {
- mLooper->removeFd(mEventTube.getFd());
+// TODO(b/169865816): refactor VSyncInjections to use MessageQueue directly
+// and remove the EventThread from MessageQueue
+void MessageQueue::setInjector(sp<EventThreadConnection> connection) {
+ auto& tube = mInjector.tube;
+
+ if (const int fd = tube.getFd(); fd >= 0) {
+ mLooper->removeFd(fd);
}
- mEvents = connection;
- mEvents->stealReceiveChannel(&mEventTube);
- mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver,
- this);
+ if (connection) {
+ // The EventThreadConnection is retained when disabling injection, so avoid subsequently
+ // stealing invalid FDs. Note that the stolen FDs are kept open.
+ if (tube.getFd() < 0) {
+ connection->stealReceiveChannel(&tube);
+ } else {
+ ALOGW("Recycling channel for VSYNC injection.");
+ }
+
+ mLooper->addFd(
+ tube.getFd(), 0, Looper::EVENT_INPUT,
+ [](int, int, void* data) {
+ reinterpret_cast<MessageQueue*>(data)->injectorCallback();
+ return 1; // Keep registration.
+ },
+ this);
+ }
+
+ std::lock_guard lock(mInjector.mutex);
+ mInjector.connection = std::move(connection);
+}
+
+void MessageQueue::vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime) {
+ ATRACE_CALL();
+ // Trace VSYNC-sf
+ mVsync.value = (mVsync.value + 1) % 2;
+
+ {
+ std::lock_guard lock(mVsync.mutex);
+ mVsync.lastCallbackTime = std::chrono::nanoseconds(vsyncTime);
+ mVsync.mScheduled = false;
+ }
+ mHandler->dispatchInvalidate(mVsync.tokenManager->generateTokenForPredictions(
+ {targetWakeupTime, readyTime, vsyncTime}),
+ vsyncTime);
+}
+
+void MessageQueue::initVsync(scheduler::VSyncDispatch& dispatch,
+ frametimeline::TokenManager& tokenManager,
+ std::chrono::nanoseconds workDuration) {
+ setDuration(workDuration);
+ mVsync.tokenManager = &tokenManager;
+ mVsync.registration = std::make_unique<
+ scheduler::VSyncCallbackRegistration>(dispatch,
+ std::bind(&MessageQueue::vsyncCallback, this,
+ std::placeholders::_1,
+ std::placeholders::_2,
+ std::placeholders::_3),
+ "sf");
+}
+
+void MessageQueue::setDuration(std::chrono::nanoseconds workDuration) {
+ ATRACE_CALL();
+ std::lock_guard lock(mVsync.mutex);
+ mVsync.workDuration = workDuration;
+ if (mVsync.mScheduled) {
+ mVsync.registration->schedule({mVsync.workDuration.get().count(), /*readyDuration=*/0,
+ mVsync.lastCallbackTime.count()});
+ }
}
void MessageQueue::waitMessage() {
@@ -105,33 +164,40 @@
}
void MessageQueue::invalidate() {
- mEvents->requestNextVsync();
+ ATRACE_CALL();
+
+ {
+ std::lock_guard lock(mInjector.mutex);
+ if (CC_UNLIKELY(mInjector.connection)) {
+ ALOGD("%s while injecting VSYNC", __FUNCTION__);
+ mInjector.connection->requestNextVsync();
+ return;
+ }
+ }
+
+ std::lock_guard lock(mVsync.mutex);
+ mVsync.mScheduled = true;
+ mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
+ .readyDuration = 0,
+ .earliestVsync = mVsync.lastCallbackTime.count()});
}
void MessageQueue::refresh() {
mHandler->dispatchRefresh();
}
-int MessageQueue::cb_eventReceiver(int fd, int events, void* data) {
- MessageQueue* queue = reinterpret_cast<MessageQueue*>(data);
- return queue->eventReceiver(fd, events);
-}
-
-int MessageQueue::eventReceiver(int /*fd*/, int /*events*/) {
+void MessageQueue::injectorCallback() {
ssize_t n;
DisplayEventReceiver::Event buffer[8];
- while ((n = DisplayEventReceiver::getEvents(&mEventTube, buffer, 8)) > 0) {
+ while ((n = DisplayEventReceiver::getEvents(&mInjector.tube, buffer, 8)) > 0) {
for (int i = 0; i < n; i++) {
if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
- mHandler->dispatchInvalidate(buffer[i].vsync.expectedVSyncTimestamp);
+ mHandler->dispatchInvalidate(buffer[i].vsync.vsyncId,
+ buffer[i].vsync.expectedVSyncTimestamp);
break;
}
}
}
- return 1;
}
} // namespace android::impl
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 132b416..2934af0 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -21,14 +21,15 @@
#include <type_traits>
#include <utility>
-#include <utils/Looper.h>
-#include <utils/Timers.h>
-#include <utils/threads.h>
-
+#include <android-base/thread_annotations.h>
#include <gui/IDisplayEventConnection.h>
#include <private/gui/BitTube.h>
+#include <utils/Looper.h>
+#include <utils/Timers.h>
#include "EventThread.h"
+#include "TracedOrdinal.h"
+#include "VSyncDispatch.h"
namespace android {
@@ -63,7 +64,10 @@
virtual ~MessageQueue() = default;
virtual void init(const sp<SurfaceFlinger>& flinger) = 0;
- virtual void setEventConnection(const sp<EventThreadConnection>& connection) = 0;
+ virtual void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
+ std::chrono::nanoseconds workDuration) = 0;
+ virtual void setDuration(std::chrono::nanoseconds workDuration) = 0;
+ virtual void setInjector(sp<EventThreadConnection>) = 0;
virtual void waitMessage() = 0;
virtual void postMessage(sp<MessageHandler>&&) = 0;
virtual void invalidate() = 0;
@@ -74,35 +78,60 @@
namespace impl {
-class MessageQueue final : public android::MessageQueue {
+class MessageQueue : public android::MessageQueue {
+protected:
class Handler : public MessageHandler {
enum { eventMaskInvalidate = 0x1, eventMaskRefresh = 0x2, eventMaskTransaction = 0x4 };
MessageQueue& mQueue;
int32_t mEventMask;
+ std::atomic<int64_t> mVsyncId;
std::atomic<nsecs_t> mExpectedVSyncTime;
public:
explicit Handler(MessageQueue& queue) : mQueue(queue), mEventMask(0) {}
- virtual void handleMessage(const Message& message);
- void dispatchRefresh();
- void dispatchInvalidate(nsecs_t expectedVSyncTimestamp);
+ void handleMessage(const Message& message) override;
+ virtual void dispatchRefresh();
+ virtual void dispatchInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTimestamp);
};
friend class Handler;
sp<SurfaceFlinger> mFlinger;
sp<Looper> mLooper;
- sp<EventThreadConnection> mEvents;
- gui::BitTube mEventTube;
+
+ struct Vsync {
+ frametimeline::TokenManager* tokenManager = nullptr;
+ std::unique_ptr<scheduler::VSyncCallbackRegistration> registration;
+
+ std::mutex mutex;
+ TracedOrdinal<std::chrono::nanoseconds> workDuration
+ GUARDED_BY(mutex) = {"VsyncWorkDuration-sf", std::chrono::nanoseconds(0)};
+ std::chrono::nanoseconds lastCallbackTime GUARDED_BY(mutex) = std::chrono::nanoseconds{0};
+ bool mScheduled GUARDED_BY(mutex) = false;
+ TracedOrdinal<int> value = {"VSYNC-sf", 0};
+ };
+
+ struct Injector {
+ gui::BitTube tube;
+ std::mutex mutex;
+ sp<EventThreadConnection> connection GUARDED_BY(mutex);
+ };
+
+ Vsync mVsync;
+ Injector mInjector;
+
sp<Handler> mHandler;
- static int cb_eventReceiver(int fd, int events, void* data);
- int eventReceiver(int fd, int events);
+ void vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime);
+ void injectorCallback();
public:
~MessageQueue() override = default;
void init(const sp<SurfaceFlinger>& flinger) override;
- void setEventConnection(const sp<EventThreadConnection>& connection) override;
+ void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
+ std::chrono::nanoseconds workDuration) override;
+ void setDuration(std::chrono::nanoseconds workDuration) override;
+ void setInjector(sp<EventThreadConnection>) override;
void waitMessage() override;
void postMessage(sp<MessageHandler>&&) override;
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.cpp b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
index a90d05e..16f041a 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.cpp
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
@@ -15,98 +15,152 @@
*/
#include "OneShotTimer.h"
-
+#include <utils/Log.h>
+#include <utils/Timers.h>
#include <chrono>
#include <sstream>
#include <thread>
+namespace {
+using namespace std::chrono_literals;
+
+constexpr int64_t kNsToSeconds = std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
+
+// The syscall interface uses a pair of integers for the timestamp. The first
+// (tv_sec) is the whole count of seconds. The second (tv_nsec) is the
+// nanosecond part of the count. This function takes care of translation.
+void calculateTimeoutTime(std::chrono::nanoseconds timestamp, timespec* spec) {
+ const nsecs_t timeout = systemTime(CLOCK_MONOTONIC) + timestamp.count();
+ spec->tv_sec = static_cast<__kernel_time_t>(timeout / kNsToSeconds);
+ spec->tv_nsec = timeout % kNsToSeconds;
+}
+} // namespace
+
namespace android {
namespace scheduler {
-OneShotTimer::OneShotTimer(const Interval& interval, const ResetCallback& resetCallback,
- const TimeoutCallback& timeoutCallback)
- : mInterval(interval), mResetCallback(resetCallback), mTimeoutCallback(timeoutCallback) {}
+OneShotTimer::OneShotTimer(std::string name, const Interval& interval,
+ const ResetCallback& resetCallback,
+ const TimeoutCallback& timeoutCallback, std::unique_ptr<Clock> clock)
+ : mClock(std::move(clock)),
+ mName(std::move(name)),
+ mInterval(interval),
+ mResetCallback(resetCallback),
+ mTimeoutCallback(timeoutCallback) {
+ LOG_ALWAYS_FATAL_IF(!mClock, "Clock must not be provided");
+}
OneShotTimer::~OneShotTimer() {
stop();
}
void OneShotTimer::start() {
- {
- std::lock_guard<std::mutex> lock(mMutex);
- mState = TimerState::RESET;
+ int result = sem_init(&mSemaphore, 0, 0);
+ LOG_ALWAYS_FATAL_IF(result, "sem_init failed");
+
+ if (!mThread.joinable()) {
+ // Only create thread if it has not been created.
+ mThread = std::thread(&OneShotTimer::loop, this);
}
- mThread = std::thread(&OneShotTimer::loop, this);
}
void OneShotTimer::stop() {
- {
- std::lock_guard<std::mutex> lock(mMutex);
- mState = TimerState::STOPPED;
- }
- mCondition.notify_all();
+ mStopTriggered = true;
+ int result = sem_post(&mSemaphore);
+ LOG_ALWAYS_FATAL_IF(result, "sem_post failed");
+
if (mThread.joinable()) {
mThread.join();
+ result = sem_destroy(&mSemaphore);
+ LOG_ALWAYS_FATAL_IF(result, "sem_destroy failed");
}
}
void OneShotTimer::loop() {
+ if (pthread_setname_np(pthread_self(), mName.c_str())) {
+ ALOGW("Failed to set thread name on dispatch thread");
+ }
+
+ TimerState state = TimerState::RESET;
while (true) {
bool triggerReset = false;
bool triggerTimeout = false;
- {
- std::lock_guard<std::mutex> lock(mMutex);
- if (mState == TimerState::STOPPED) {
- break;
- }
- if (mState == TimerState::IDLE) {
- mCondition.wait(mMutex);
- continue;
- }
-
- if (mState == TimerState::RESET) {
- triggerReset = true;
- }
+ state = checkForResetAndStop(state);
+ if (state == TimerState::STOPPED) {
+ break;
}
+
+ if (state == TimerState::IDLE) {
+ int result = sem_wait(&mSemaphore);
+ if (result && errno != EINTR) {
+ std::stringstream ss;
+ ss << "sem_wait failed (" << errno << ")";
+ LOG_ALWAYS_FATAL("%s", ss.str().c_str());
+ }
+ continue;
+ }
+
+ if (state == TimerState::RESET) {
+ triggerReset = true;
+ }
+
if (triggerReset && mResetCallback) {
mResetCallback();
}
- { // lock the mutex again. someone might have called stop meanwhile
- std::lock_guard<std::mutex> lock(mMutex);
- if (mState == TimerState::STOPPED) {
- break;
+ state = checkForResetAndStop(state);
+ if (state == TimerState::STOPPED) {
+ break;
+ }
+
+ auto triggerTime = mClock->now() + mInterval;
+ state = TimerState::WAITING;
+ while (state == TimerState::WAITING) {
+ constexpr auto zero = std::chrono::steady_clock::duration::zero();
+ // Wait for mInterval time for semaphore signal.
+ struct timespec ts;
+ calculateTimeoutTime(std::chrono::nanoseconds(mInterval), &ts);
+ int result = sem_clockwait(&mSemaphore, CLOCK_MONOTONIC, &ts);
+ if (result && errno != ETIMEDOUT && errno != EINTR) {
+ std::stringstream ss;
+ ss << "sem_clockwait failed (" << errno << ")";
+ LOG_ALWAYS_FATAL("%s", ss.str().c_str());
}
- auto triggerTime = std::chrono::steady_clock::now() + mInterval;
- mState = TimerState::WAITING;
- while (mState == TimerState::WAITING) {
- constexpr auto zero = std::chrono::steady_clock::duration::zero();
- auto waitTime = triggerTime - std::chrono::steady_clock::now();
- if (waitTime > zero) mCondition.wait_for(mMutex, waitTime);
- if (mState == TimerState::RESET) {
- triggerTime = std::chrono::steady_clock::now() + mInterval;
- mState = TimerState::WAITING;
- } else if (mState == TimerState::WAITING &&
- (triggerTime - std::chrono::steady_clock::now()) <= zero) {
- triggerTimeout = true;
- mState = TimerState::IDLE;
- }
+ state = checkForResetAndStop(state);
+ if (state == TimerState::RESET) {
+ triggerTime = mClock->now() + mInterval;
+ state = TimerState::WAITING;
+ } else if (state == TimerState::WAITING && (triggerTime - mClock->now()) <= zero) {
+ triggerTimeout = true;
+ state = TimerState::IDLE;
}
}
+
if (triggerTimeout && mTimeoutCallback) {
mTimeoutCallback();
}
}
}
-void OneShotTimer::reset() {
- {
- std::lock_guard<std::mutex> lock(mMutex);
- mState = TimerState::RESET;
+OneShotTimer::TimerState OneShotTimer::checkForResetAndStop(TimerState state) {
+ // Stop takes precedence of the reset.
+ if (mStopTriggered.exchange(false)) {
+ return TimerState::STOPPED;
}
- mCondition.notify_all();
+ // If the state was stopped, the thread was joined, and we cannot reset
+ // the timer anymore.
+ if (state != TimerState::STOPPED && mResetTriggered.exchange(false)) {
+ return TimerState::RESET;
+ }
+ return state;
+}
+
+void OneShotTimer::reset() {
+ mResetTriggered = true;
+ int result = sem_post(&mSemaphore);
+ LOG_ALWAYS_FATAL_IF(result, "sem_post failed");
}
std::string OneShotTimer::dump() const {
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.h b/services/surfaceflinger/Scheduler/OneShotTimer.h
index b005754..09265bb 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.h
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.h
@@ -16,9 +16,11 @@
#pragma once
+#include <semaphore.h>
#include <chrono>
#include <condition_variable>
#include <thread>
+#include "../Clock.h"
#include <android-base/thread_annotations.h>
@@ -35,8 +37,9 @@
using ResetCallback = std::function<void()>;
using TimeoutCallback = std::function<void()>;
- OneShotTimer(const Interval& interval, const ResetCallback& resetCallback,
- const TimeoutCallback& timeoutCallback);
+ OneShotTimer(std::string name, const Interval& interval, const ResetCallback& resetCallback,
+ const TimeoutCallback& timeoutCallback,
+ std::unique_ptr<Clock> clock = std::make_unique<SteadyClock>());
~OneShotTimer();
// Initializes and turns on the idle timer.
@@ -70,17 +73,21 @@
// Function that loops until the condition for stopping is met.
void loop();
+ // Checks whether mResetTriggered and mStopTriggered were set and updates
+ // mState if so.
+ TimerState checkForResetAndStop(TimerState state);
+
// Thread waiting for timer to expire.
std::thread mThread;
- // Condition used to notify mThread.
- std::condition_variable_any mCondition;
+ // Clock object for the timer. Mocked in unit tests.
+ std::unique_ptr<Clock> mClock;
- // Lock used for synchronizing the waiting thread with the application thread.
- std::mutex mMutex;
+ // Semaphore to keep mThread synchronized.
+ sem_t mSemaphore;
- // Current timer state
- TimerState mState GUARDED_BY(mMutex) = TimerState::RESET;
+ // Timer's name.
+ std::string mName;
// Interval after which timer expires.
const Interval mInterval;
@@ -90,6 +97,12 @@
// Callback that happens when timer expires.
const TimeoutCallback mTimeoutCallback;
+
+ // After removing lock guarding mState, the state can be now accessed at
+ // any time. Keep a bool if the reset or stop were requested, and occasionally
+ // check in the main loop if they were.
+ std::atomic<bool> mResetTriggered = false;
+ std::atomic<bool> mStopTriggered = false;
};
} // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
deleted file mode 100644
index fe2e406..0000000
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * Copyright 2019 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 "PhaseOffsets.h"
-
-#include <cutils/properties.h>
-
-#include <optional>
-
-#include "SurfaceFlingerProperties.h"
-
-namespace {
-
-std::optional<nsecs_t> getProperty(const char* name) {
- char value[PROPERTY_VALUE_MAX];
- property_get(name, value, "-1");
- if (const int i = atoi(value); i != -1) return i;
- return std::nullopt;
-}
-
-bool fpsEqualsWithMargin(float fpsA, float fpsB) {
- static constexpr float MARGIN = 0.01f;
- return std::abs(fpsA - fpsB) <= MARGIN;
-}
-
-std::vector<float> getRefreshRatesFromConfigs(
- const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
- const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
- std::vector<float> refreshRates;
- refreshRates.reserve(allRefreshRates.size());
-
- for (const auto& [ignored, refreshRate] : allRefreshRates) {
- refreshRates.emplace_back(refreshRate->getFps());
- }
-
- return refreshRates;
-}
-
-} // namespace
-
-namespace android::scheduler {
-
-PhaseConfiguration::~PhaseConfiguration() = default;
-
-namespace impl {
-
-PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs)
- : PhaseOffsets(getRefreshRatesFromConfigs(refreshRateConfigs),
- refreshRateConfigs.getCurrentRefreshRate().getFps(),
- sysprop::vsync_event_phase_offset_ns(1000000),
- sysprop::vsync_sf_event_phase_offset_ns(1000000),
- getProperty("debug.sf.early_phase_offset_ns"),
- getProperty("debug.sf.early_gl_phase_offset_ns"),
- getProperty("debug.sf.early_app_phase_offset_ns"),
- getProperty("debug.sf.early_gl_app_phase_offset_ns"),
- // Below defines the threshold when an offset is considered to be negative,
- // i.e. targeting for the N+2 vsync instead of N+1. This means that: For offset
- // < threshold, SF wake up (vsync_duration - offset) before HW vsync. For
- // offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW
- // vsync.
- getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
- .value_or(std::numeric_limits<nsecs_t>::max())) {}
-
-PhaseOffsets::PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
- nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
- std::optional<nsecs_t> earlySfOffsetNs,
- std::optional<nsecs_t> earlyGlSfOffsetNs,
- std::optional<nsecs_t> earlyAppOffsetNs,
- std::optional<nsecs_t> earlyGlAppOffsetNs, nsecs_t thresholdForNextVsync)
- : mVSyncPhaseOffsetNs(vsyncPhaseOffsetNs),
- mSfVSyncPhaseOffsetNs(sfVSyncPhaseOffsetNs),
- mEarlySfOffsetNs(earlySfOffsetNs),
- mEarlyGlSfOffsetNs(earlyGlSfOffsetNs),
- mEarlyAppOffsetNs(earlyAppOffsetNs),
- mEarlyGlAppOffsetNs(earlyGlAppOffsetNs),
- mThresholdForNextVsync(thresholdForNextVsync),
- mOffsets(initializeOffsets(refreshRates)),
- mRefreshRateFps(currentFps) {}
-
-void PhaseOffsets::dump(std::string& result) const {
- const auto [early, earlyGl, late] = getCurrentOffsets();
- using base::StringAppendF;
- StringAppendF(&result,
- " app phase: %9" PRId64 " ns\t SF phase: %9" PRId64 " ns\n"
- " early app phase: %9" PRId64 " ns\t early SF phase: %9" PRId64 " ns\n"
- " GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64 " ns\n"
- "next VSYNC threshold: %9" PRId64 " ns\n",
- late.app, late.sf, early.app, early.sf, earlyGl.app, earlyGl.sf,
- mThresholdForNextVsync);
-}
-
-std::unordered_map<float, PhaseOffsets::Offsets> PhaseOffsets::initializeOffsets(
- const std::vector<float>& refreshRates) const {
- std::unordered_map<float, Offsets> offsets;
-
- for (const auto& refreshRate : refreshRates) {
- offsets.emplace(refreshRate,
- getPhaseOffsets(refreshRate, static_cast<nsecs_t>(1e9f / refreshRate)));
- }
- return offsets;
-}
-
-PhaseOffsets::Offsets PhaseOffsets::getPhaseOffsets(float fps, nsecs_t vsyncPeriod) const {
- if (fps > 65.0f) {
- return getHighFpsOffsets(vsyncPeriod);
- } else {
- return getDefaultOffsets(vsyncPeriod);
- }
-}
-
-PhaseOffsets::Offsets PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const {
- return {
- {
- mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
- ? mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs)
- : mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration,
-
- mEarlyAppOffsetNs.value_or(mVSyncPhaseOffsetNs),
- },
- {
- mEarlyGlSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
- ? mEarlyGlSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs)
- : mEarlyGlSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration,
-
- mEarlyGlAppOffsetNs.value_or(mVSyncPhaseOffsetNs),
- },
- {
- mSfVSyncPhaseOffsetNs < mThresholdForNextVsync
- ? mSfVSyncPhaseOffsetNs
- : mSfVSyncPhaseOffsetNs - vsyncDuration,
-
- mVSyncPhaseOffsetNs,
- },
- };
-}
-
-PhaseOffsets::Offsets PhaseOffsets::getHighFpsOffsets(nsecs_t vsyncDuration) const {
- const auto highFpsLateAppOffsetNs =
- getProperty("debug.sf.high_fps_late_app_phase_offset_ns").value_or(2000000);
- const auto highFpsLateSfOffsetNs =
- getProperty("debug.sf.high_fps_late_sf_phase_offset_ns").value_or(1000000);
-
- const auto highFpsEarlySfOffsetNs = getProperty("debug.sf.high_fps_early_phase_offset_ns");
- const auto highFpsEarlyGlSfOffsetNs = getProperty("debug.sf.high_fps_early_gl_phase_offset_ns");
- const auto highFpsEarlyAppOffsetNs = getProperty("debug.sf.high_fps_early_app_phase_offset_ns");
- const auto highFpsEarlyGlAppOffsetNs =
- getProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
-
- return {
- {
- highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) < mThresholdForNextVsync
- ? highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs)
- : highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) -
- vsyncDuration,
-
- highFpsEarlyAppOffsetNs.value_or(highFpsLateAppOffsetNs),
- },
- {
- highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) <
- mThresholdForNextVsync
- ? highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs)
- : highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) -
- vsyncDuration,
-
- highFpsEarlyGlAppOffsetNs.value_or(highFpsLateAppOffsetNs),
- },
- {
- highFpsLateSfOffsetNs < mThresholdForNextVsync
- ? highFpsLateSfOffsetNs
- : highFpsLateSfOffsetNs - vsyncDuration,
-
- highFpsLateAppOffsetNs,
- },
- };
-}
-
-PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate(float fps) const {
- const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(),
- [&fps](const std::pair<float, Offsets>& candidateFps) {
- return fpsEqualsWithMargin(fps, candidateFps.first);
- });
-
- if (iter != mOffsets.end()) {
- return iter->second;
- }
-
- // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
- // In this case just construct the offset.
- ALOGW("Can't find offset for %.2f fps", fps);
- return getPhaseOffsets(fps, static_cast<nsecs_t>(1e9f / fps));
-}
-
-static void validateSysprops() {
- const auto validatePropertyBool = [](const char* prop) {
- LOG_ALWAYS_FATAL_IF(!property_get_bool(prop, false), "%s is false", prop);
- };
-
- validatePropertyBool("debug.sf.use_phase_offsets_as_durations");
-
- LOG_ALWAYS_FATAL_IF(sysprop::vsync_event_phase_offset_ns(-1) != -1,
- "ro.surface_flinger.vsync_event_phase_offset_ns is set but expecting "
- "duration");
-
- LOG_ALWAYS_FATAL_IF(sysprop::vsync_sf_event_phase_offset_ns(-1) != -1,
- "ro.surface_flinger.vsync_sf_event_phase_offset_ns is set but expecting "
- "duration");
-
- const auto validateProperty = [](const char* prop) {
- LOG_ALWAYS_FATAL_IF(getProperty(prop).has_value(),
- "%s is set to %" PRId64 " but expecting duration", prop,
- getProperty(prop).value_or(-1));
- };
-
- validateProperty("debug.sf.early_phase_offset_ns");
- validateProperty("debug.sf.early_gl_phase_offset_ns");
- validateProperty("debug.sf.early_app_phase_offset_ns");
- validateProperty("debug.sf.early_gl_app_phase_offset_ns");
- validateProperty("debug.sf.high_fps_late_app_phase_offset_ns");
- validateProperty("debug.sf.high_fps_late_sf_phase_offset_ns");
- validateProperty("debug.sf.high_fps_early_phase_offset_ns");
- validateProperty("debug.sf.high_fps_early_gl_phase_offset_ns");
- validateProperty("debug.sf.high_fps_early_app_phase_offset_ns");
- validateProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
-}
-
-static nsecs_t sfDurationToOffset(nsecs_t sfDuration, nsecs_t vsyncDuration) {
- return sfDuration == -1 ? 1'000'000 : vsyncDuration - sfDuration % vsyncDuration;
-}
-
-static nsecs_t appDurationToOffset(nsecs_t appDuration, nsecs_t sfDuration, nsecs_t vsyncDuration) {
- return sfDuration == -1 ? 1'000'000
- : vsyncDuration - (appDuration + sfDuration) % vsyncDuration;
-}
-
-PhaseDurations::Offsets PhaseDurations::constructOffsets(nsecs_t vsyncDuration) const {
- return Offsets{
- {
- mSfEarlyDuration < vsyncDuration
- ? sfDurationToOffset(mSfEarlyDuration, vsyncDuration)
- : sfDurationToOffset(mSfEarlyDuration, vsyncDuration) - vsyncDuration,
-
- appDurationToOffset(mAppEarlyDuration, mSfEarlyDuration, vsyncDuration),
- },
- {
- mSfEarlyGlDuration < vsyncDuration
- ? sfDurationToOffset(mSfEarlyGlDuration, vsyncDuration)
- : sfDurationToOffset(mSfEarlyGlDuration, vsyncDuration) - vsyncDuration,
-
- appDurationToOffset(mAppEarlyGlDuration, mSfEarlyGlDuration, vsyncDuration),
- },
- {
- mSfDuration < vsyncDuration
- ? sfDurationToOffset(mSfDuration, vsyncDuration)
- : sfDurationToOffset(mSfDuration, vsyncDuration) - vsyncDuration,
-
- appDurationToOffset(mAppDuration, mSfDuration, vsyncDuration),
- },
- };
-}
-
-std::unordered_map<float, PhaseDurations::Offsets> PhaseDurations::initializeOffsets(
- const std::vector<float>& refreshRates) const {
- std::unordered_map<float, Offsets> offsets;
-
- for (const auto fps : refreshRates) {
- offsets.emplace(fps, constructOffsets(static_cast<nsecs_t>(1e9f / fps)));
- }
- return offsets;
-}
-
-PhaseDurations::PhaseDurations(const scheduler::RefreshRateConfigs& refreshRateConfigs)
- : PhaseDurations(getRefreshRatesFromConfigs(refreshRateConfigs),
- refreshRateConfigs.getCurrentRefreshRate().getFps(),
- getProperty("debug.sf.late.sf.duration").value_or(-1),
- getProperty("debug.sf.late.app.duration").value_or(-1),
- getProperty("debug.sf.early.sf.duration").value_or(mSfDuration),
- getProperty("debug.sf.early.app.duration").value_or(mAppDuration),
- getProperty("debug.sf.earlyGl.sf.duration").value_or(mSfDuration),
- getProperty("debug.sf.earlyGl.app.duration").value_or(mAppDuration)) {
- validateSysprops();
-}
-
-PhaseDurations::PhaseDurations(const std::vector<float>& refreshRates, float currentFps,
- nsecs_t sfDuration, nsecs_t appDuration, nsecs_t sfEarlyDuration,
- nsecs_t appEarlyDuration, nsecs_t sfEarlyGlDuration,
- nsecs_t appEarlyGlDuration)
- : mSfDuration(sfDuration),
- mAppDuration(appDuration),
- mSfEarlyDuration(sfEarlyDuration),
- mAppEarlyDuration(appEarlyDuration),
- mSfEarlyGlDuration(sfEarlyGlDuration),
- mAppEarlyGlDuration(appEarlyGlDuration),
- mOffsets(initializeOffsets(refreshRates)),
- mRefreshRateFps(currentFps) {}
-
-PhaseOffsets::Offsets PhaseDurations::getOffsetsForRefreshRate(float fps) const {
- const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(), [=](const auto& candidateFps) {
- return fpsEqualsWithMargin(fps, candidateFps.first);
- });
-
- if (iter != mOffsets.end()) {
- return iter->second;
- }
-
- // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
- // In this case just construct the offset.
- ALOGW("Can't find offset for %.2f fps", fps);
- return constructOffsets(static_cast<nsecs_t>(1e9f / fps));
-}
-
-void PhaseDurations::dump(std::string& result) const {
- const auto [early, earlyGl, late] = getCurrentOffsets();
- using base::StringAppendF;
- StringAppendF(&result,
- " app phase: %9" PRId64 " ns\t SF phase: %9" PRId64
- " ns\n"
- " app duration: %9" PRId64 " ns\t SF duration: %9" PRId64
- " ns\n"
- " early app phase: %9" PRId64 " ns\t early SF phase: %9" PRId64
- " ns\n"
- " early app duration: %9" PRId64 " ns\t early SF duration: %9" PRId64
- " ns\n"
- " GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64
- " ns\n"
- " GL early app duration: %9" PRId64 " ns\tGL early SF duration: %9" PRId64
- " ns\n",
- late.app,
-
- late.sf,
-
- mAppDuration, mSfDuration,
-
- early.app, early.sf,
-
- mAppEarlyDuration, mSfEarlyDuration,
-
- earlyGl.app,
-
- earlyGl.sf,
-
- mAppEarlyGlDuration, mSfEarlyGlDuration);
-}
-
-} // namespace impl
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
deleted file mode 100644
index 9ec6d56..0000000
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.h
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright 2019 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.
- */
-
-#pragma once
-
-#include <unordered_map>
-
-#include "RefreshRateConfigs.h"
-#include "VSyncModulator.h"
-
-namespace android::scheduler {
-
-/*
- * This class encapsulates offsets for different refresh rates. Depending
- * on what refresh rate we are using, and wheter we are composing in GL,
- * different offsets will help us with latency. This class keeps track of
- * which mode the device is on, and returns approprate offsets when needed.
- */
-class PhaseConfiguration {
-public:
- using Offsets = VSyncModulator::OffsetsConfig;
-
- virtual ~PhaseConfiguration();
-
- virtual Offsets getCurrentOffsets() const = 0;
- virtual Offsets getOffsetsForRefreshRate(float fps) const = 0;
-
- virtual void setRefreshRateFps(float fps) = 0;
-
- virtual void dump(std::string& result) const = 0;
-};
-
-namespace impl {
-
-/*
- * This is the old implementation of phase offsets and considered as deprecated.
- * PhaseDurations is the new implementation.
- */
-class PhaseOffsets : public scheduler::PhaseConfiguration {
-public:
- PhaseOffsets(const scheduler::RefreshRateConfigs&);
-
- // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
- Offsets getOffsetsForRefreshRate(float fps) const override;
-
- // Returns early, early GL, and late offsets for Apps and SF.
- Offsets getCurrentOffsets() const override { return getOffsetsForRefreshRate(mRefreshRateFps); }
-
- // This function should be called when the device is switching between different
- // refresh rates, to properly update the offsets.
- void setRefreshRateFps(float fps) override { mRefreshRateFps = fps; }
-
- // Returns current offsets in human friendly format.
- void dump(std::string& result) const override;
-
-protected:
- // Used for unit tests
- PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
- nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
- std::optional<nsecs_t> earlySfOffsetNs, std::optional<nsecs_t> earlyGlSfOffsetNs,
- std::optional<nsecs_t> earlyAppOffsetNs, std::optional<nsecs_t> earlyGlAppOffsetNs,
- nsecs_t thresholdForNextVsync);
- std::unordered_map<float, Offsets> initializeOffsets(
- const std::vector<float>& refreshRates) const;
- Offsets getDefaultOffsets(nsecs_t vsyncPeriod) const;
- Offsets getHighFpsOffsets(nsecs_t vsyncPeriod) const;
- Offsets getPhaseOffsets(float fps, nsecs_t vsyncPeriod) const;
-
- const nsecs_t mVSyncPhaseOffsetNs;
- const nsecs_t mSfVSyncPhaseOffsetNs;
- const std::optional<nsecs_t> mEarlySfOffsetNs;
- const std::optional<nsecs_t> mEarlyGlSfOffsetNs;
- const std::optional<nsecs_t> mEarlyAppOffsetNs;
- const std::optional<nsecs_t> mEarlyGlAppOffsetNs;
- const nsecs_t mThresholdForNextVsync;
- const std::unordered_map<float, Offsets> mOffsets;
-
- std::atomic<float> mRefreshRateFps;
-};
-
-/*
- * Class that encapsulates the phase offsets for SurfaceFlinger and App.
- * The offsets are calculated from durations for each one of the (late, early, earlyGL)
- * offset types.
- */
-class PhaseDurations : public scheduler::PhaseConfiguration {
-public:
- PhaseDurations(const scheduler::RefreshRateConfigs&);
-
- // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
- Offsets getOffsetsForRefreshRate(float fps) const override;
-
- // Returns early, early GL, and late offsets for Apps and SF.
- Offsets getCurrentOffsets() const override { return getOffsetsForRefreshRate(mRefreshRateFps); }
-
- // This function should be called when the device is switching between different
- // refresh rates, to properly update the offsets.
- void setRefreshRateFps(float fps) override { mRefreshRateFps = fps; }
-
- // Returns current offsets in human friendly format.
- void dump(std::string& result) const override;
-
-protected:
- // Used for unit tests
- PhaseDurations(const std::vector<float>& refreshRates, float currentFps, nsecs_t sfDuration,
- nsecs_t appDuration, nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
- nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration);
-
-private:
- std::unordered_map<float, Offsets> initializeOffsets(const std::vector<float>&) const;
- PhaseDurations::Offsets constructOffsets(nsecs_t vsyncDuration) const;
-
- const nsecs_t mSfDuration;
- const nsecs_t mAppDuration;
-
- const nsecs_t mSfEarlyDuration;
- const nsecs_t mAppEarlyDuration;
-
- const nsecs_t mSfEarlyGlDuration;
- const nsecs_t mAppEarlyGlDuration;
-
- const std::unordered_map<float, Offsets> mOffsets;
-
- std::atomic<float> mRefreshRateFps;
-};
-
-} // namespace impl
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 8661b6e..b062acd 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -17,20 +17,58 @@
// #define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wextra"
+
#include "RefreshRateConfigs.h"
#include <android-base/stringprintf.h>
#include <utils/Trace.h>
#include <chrono>
#include <cmath>
+#include "../SurfaceFlingerProperties.h"
#undef LOG_TAG
#define LOG_TAG "RefreshRateConfigs"
namespace android::scheduler {
+namespace {
+std::string formatLayerInfo(const RefreshRateConfigs::LayerRequirement& layer, float weight) {
+ return base::StringPrintf("%s (type=%s, weight=%.2f seamlessness=%s) %s", layer.name.c_str(),
+ RefreshRateConfigs::layerVoteTypeString(layer.vote).c_str(), weight,
+ toString(layer.seamlessness).c_str(),
+ to_string(layer.desiredRefreshRate).c_str());
+}
+
+std::vector<Fps> constructKnownFrameRates(const DisplayModes& modes) {
+ std::vector<Fps> knownFrameRates = {Fps(24.0f), Fps(30.0f), Fps(45.0f), Fps(60.0f), Fps(72.0f)};
+ knownFrameRates.reserve(knownFrameRates.size() + modes.size());
+
+ // Add all supported refresh rates to the set
+ for (const auto& mode : modes) {
+ const auto refreshRate = Fps::fromPeriodNsecs(mode->getVsyncPeriod());
+ knownFrameRates.emplace_back(refreshRate);
+ }
+
+ // Sort and remove duplicates
+ std::sort(knownFrameRates.begin(), knownFrameRates.end(), Fps::comparesLess);
+ knownFrameRates.erase(std::unique(knownFrameRates.begin(), knownFrameRates.end(),
+ Fps::EqualsWithMargin()),
+ knownFrameRates.end());
+ return knownFrameRates;
+}
+
+} // namespace
using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType;
using RefreshRate = RefreshRateConfigs::RefreshRate;
+std::string RefreshRate::toString() const {
+ return base::StringPrintf("{id=%d, hwcId=%d, fps=%.2f, width=%d, height=%d group=%d}",
+ getModeId().value(), mode->getHwcId(), getFps().getValue(),
+ mode->getWidth(), mode->getHeight(), getModeGroup());
+}
+
std::string RefreshRateConfigs::layerVoteTypeString(LayerVoteType vote) {
switch (vote) {
case LayerVoteType::NoVote:
@@ -45,80 +83,115 @@
return "ExplicitDefault";
case LayerVoteType::ExplicitExactOrMultiple:
return "ExplicitExactOrMultiple";
+ case LayerVoteType::ExplicitExact:
+ return "ExplicitExact";
}
}
-const RefreshRate& RefreshRateConfigs::getRefreshRateForContent(
- const std::vector<LayerRequirement>& layers) const {
- std::lock_guard lock(mLock);
- int contentFramerate = 0;
- int explicitContentFramerate = 0;
- for (const auto& layer : layers) {
- const auto desiredRefreshRateRound = round<int>(layer.desiredRefreshRate);
- if (layer.vote == LayerVoteType::ExplicitDefault ||
- layer.vote == LayerVoteType::ExplicitExactOrMultiple) {
- if (desiredRefreshRateRound > explicitContentFramerate) {
- explicitContentFramerate = desiredRefreshRateRound;
- }
- } else {
- if (desiredRefreshRateRound > contentFramerate) {
- contentFramerate = desiredRefreshRateRound;
- }
- }
- }
-
- if (explicitContentFramerate != 0) {
- contentFramerate = explicitContentFramerate;
- } else if (contentFramerate == 0) {
- contentFramerate = round<int>(mMaxSupportedRefreshRate->getFps());
- }
- ATRACE_INT("ContentFPS", contentFramerate);
-
- // Find the appropriate refresh rate with minimal error
- auto iter = min_element(mPrimaryRefreshRates.cbegin(), mPrimaryRefreshRates.cend(),
- [contentFramerate](const auto& lhs, const auto& rhs) -> bool {
- return std::abs(lhs->fps - contentFramerate) <
- std::abs(rhs->fps - contentFramerate);
- });
-
- // Some content aligns better on higher refresh rate. For example for 45fps we should choose
- // 90Hz config. However we should still prefer a lower refresh rate if the content doesn't
- // align well with both
- const RefreshRate* bestSoFar = *iter;
- constexpr float MARGIN = 0.05f;
- float ratio = (*iter)->fps / contentFramerate;
- if (std::abs(std::round(ratio) - ratio) > MARGIN) {
- while (iter != mPrimaryRefreshRates.cend()) {
- ratio = (*iter)->fps / contentFramerate;
-
- if (std::abs(std::round(ratio) - ratio) <= MARGIN) {
- bestSoFar = *iter;
- break;
- }
- ++iter;
- }
- }
-
- return *bestSoFar;
+std::string RefreshRateConfigs::Policy::toString() const {
+ return base::StringPrintf("default mode ID: %d, allowGroupSwitching = %d"
+ ", primary range: %s, app request range: %s",
+ defaultMode.value(), allowGroupSwitching,
+ primaryRange.toString().c_str(), appRequestRange.toString().c_str());
}
std::pair<nsecs_t, nsecs_t> RefreshRateConfigs::getDisplayFrames(nsecs_t layerPeriod,
nsecs_t displayPeriod) const {
- auto [displayFramesQuot, displayFramesRem] = std::div(layerPeriod, displayPeriod);
- if (displayFramesRem <= MARGIN_FOR_PERIOD_CALCULATION ||
- std::abs(displayFramesRem - displayPeriod) <= MARGIN_FOR_PERIOD_CALCULATION) {
- displayFramesQuot++;
- displayFramesRem = 0;
+ auto [quotient, remainder] = std::div(layerPeriod, displayPeriod);
+ if (remainder <= MARGIN_FOR_PERIOD_CALCULATION ||
+ std::abs(remainder - displayPeriod) <= MARGIN_FOR_PERIOD_CALCULATION) {
+ quotient++;
+ remainder = 0;
}
- return {displayFramesQuot, displayFramesRem};
+ return {quotient, remainder};
}
-const RefreshRate& RefreshRateConfigs::getBestRefreshRate(
- const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
- GlobalSignals* outSignalsConsidered) const {
+float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer,
+ const RefreshRate& refreshRate,
+ bool isSeamlessSwitch) const {
+ // Slightly prefer seamless switches.
+ constexpr float kSeamedSwitchPenalty = 0.95f;
+ const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
+
+ // If the layer wants Max, give higher score to the higher refresh rate
+ if (layer.vote == LayerVoteType::Max) {
+ const auto ratio =
+ refreshRate.fps.getValue() / mAppRequestRefreshRates.back()->fps.getValue();
+ // use ratio^2 to get a lower score the more we get further from peak
+ return ratio * ratio;
+ }
+
+ const auto displayPeriod = refreshRate.getVsyncPeriod();
+ const auto layerPeriod = layer.desiredRefreshRate.getPeriodNsecs();
+ if (layer.vote == LayerVoteType::ExplicitDefault) {
+ // Find the actual rate the layer will render, assuming
+ // that layerPeriod is the minimal time to render a frame
+ auto actualLayerPeriod = displayPeriod;
+ int multiplier = 1;
+ while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) {
+ multiplier++;
+ actualLayerPeriod = displayPeriod * multiplier;
+ }
+ return std::min(1.0f,
+ static_cast<float>(layerPeriod) / static_cast<float>(actualLayerPeriod));
+ }
+
+ if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
+ layer.vote == LayerVoteType::Heuristic) {
+ // Calculate how many display vsyncs we need to present a single frame for this
+ // layer
+ const auto [displayFramesQuotient, displayFramesRemainder] =
+ getDisplayFrames(layerPeriod, displayPeriod);
+ static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1
+ if (displayFramesRemainder == 0) {
+ // Layer desired refresh rate matches the display rate.
+ return 1.0f * seamlessness;
+ }
+
+ if (displayFramesQuotient == 0) {
+ // Layer desired refresh rate is higher than the display rate.
+ return (static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod)) *
+ (1.0f / (MAX_FRAMES_TO_FIT + 1));
+ }
+
+ // Layer desired refresh rate is lower than the display rate. Check how well it fits
+ // the cadence.
+ auto diff = std::abs(displayFramesRemainder - (displayPeriod - displayFramesRemainder));
+ int iter = 2;
+ while (diff > MARGIN_FOR_PERIOD_CALCULATION && iter < MAX_FRAMES_TO_FIT) {
+ diff = diff - (displayPeriod - diff);
+ iter++;
+ }
+
+ return (1.0f / iter) * seamlessness;
+ }
+
+ if (layer.vote == LayerVoteType::ExplicitExact) {
+ const int divider = getFrameRateDivider(refreshRate.getFps(), layer.desiredRefreshRate);
+ if (mSupportsFrameRateOverride) {
+ // Since we support frame rate override, allow refresh rates which are
+ // multiples of the layer's request, as those apps would be throttled
+ // down to run at the desired refresh rate.
+ return divider > 0;
+ }
+
+ return divider == 1;
+ }
+
+ return 0;
+}
+
+struct RefreshRateScore {
+ const RefreshRate* refreshRate;
+ float score;
+};
+
+RefreshRate RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequirement>& layers,
+ const GlobalSignals& globalSignals,
+ GlobalSignals* outSignalsConsidered) const {
ATRACE_CALL();
- ALOGV("getRefreshRateForContent %zu layers", layers.size());
+ ALOGV("getBestRefreshRate %zu layers", layers.size());
if (outSignalsConsidered) *outSignalsConsidered = {};
const auto setTouchConsidered = [&] {
@@ -140,25 +213,43 @@
int maxVoteLayers = 0;
int explicitDefaultVoteLayers = 0;
int explicitExactOrMultipleVoteLayers = 0;
+ int explicitExact = 0;
float maxExplicitWeight = 0;
+ int seamedFocusedLayers = 0;
for (const auto& layer : layers) {
- if (layer.vote == LayerVoteType::NoVote) {
- noVoteLayers++;
- } else if (layer.vote == LayerVoteType::Min) {
- minVoteLayers++;
- } else if (layer.vote == LayerVoteType::Max) {
- maxVoteLayers++;
- } else if (layer.vote == LayerVoteType::ExplicitDefault) {
- explicitDefaultVoteLayers++;
- maxExplicitWeight = std::max(maxExplicitWeight, layer.weight);
- } else if (layer.vote == LayerVoteType::ExplicitExactOrMultiple) {
- explicitExactOrMultipleVoteLayers++;
- maxExplicitWeight = std::max(maxExplicitWeight, layer.weight);
+ switch (layer.vote) {
+ case LayerVoteType::NoVote:
+ noVoteLayers++;
+ break;
+ case LayerVoteType::Min:
+ minVoteLayers++;
+ break;
+ case LayerVoteType::Max:
+ maxVoteLayers++;
+ break;
+ case LayerVoteType::ExplicitDefault:
+ explicitDefaultVoteLayers++;
+ maxExplicitWeight = std::max(maxExplicitWeight, layer.weight);
+ break;
+ case LayerVoteType::ExplicitExactOrMultiple:
+ explicitExactOrMultipleVoteLayers++;
+ maxExplicitWeight = std::max(maxExplicitWeight, layer.weight);
+ break;
+ case LayerVoteType::ExplicitExact:
+ explicitExact++;
+ maxExplicitWeight = std::max(maxExplicitWeight, layer.weight);
+ break;
+ case LayerVoteType::Heuristic:
+ break;
+ }
+
+ if (layer.seamlessness == Seamlessness::SeamedAndSeamless && layer.focused) {
+ seamedFocusedLayers++;
}
}
- const bool hasExplicitVoteLayers =
- explicitDefaultVoteLayers > 0 || explicitExactOrMultipleVoteLayers > 0;
+ const bool hasExplicitVoteLayers = explicitDefaultVoteLayers > 0 ||
+ explicitExactOrMultipleVoteLayers > 0 || explicitExact > 0;
// Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've
// selected a refresh rate to see if we should apply touch boost.
@@ -172,7 +263,8 @@
// move out the of range if layers explicitly request a different refresh
// rate.
const Policy* policy = getCurrentPolicyLocked();
- const bool primaryRangeIsSingleRate = policy->primaryRange.min == policy->primaryRange.max;
+ const bool primaryRangeIsSingleRate =
+ policy->primaryRange.min.equalsWithMargin(policy->primaryRange.max);
if (!globalSignals.touch && globalSignals.idle &&
!(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
@@ -192,13 +284,15 @@
}
// Find the best refresh rate based on score
- std::vector<std::pair<const RefreshRate*, float>> scores;
+ std::vector<RefreshRateScore> scores;
scores.reserve(mAppRequestRefreshRates.size());
for (const auto refreshRate : mAppRequestRefreshRates) {
- scores.emplace_back(refreshRate, 0.0f);
+ scores.emplace_back(RefreshRateScore{refreshRate, 0.0f});
}
+ const auto& defaultMode = mRefreshRates.at(policy->defaultMode);
+
for (const auto& layer : layers) {
ALOGV("Calculating score for %s (%s, weight %.2f)", layer.name.c_str(),
layerVoteTypeString(layer.vote).c_str(), layer.weight);
@@ -209,88 +303,59 @@
auto weight = layer.weight;
for (auto i = 0u; i < scores.size(); i++) {
- bool inPrimaryRange =
- scores[i].first->inPolicy(policy->primaryRange.min, policy->primaryRange.max);
+ const bool isSeamlessSwitch =
+ scores[i].refreshRate->getModeGroup() == mCurrentRefreshRate->getModeGroup();
+
+ if (layer.seamlessness == Seamlessness::OnlySeamless && !isSeamlessSwitch) {
+ ALOGV("%s ignores %s to avoid non-seamless switch. Current mode = %s",
+ formatLayerInfo(layer, weight).c_str(),
+ scores[i].refreshRate->toString().c_str(),
+ mCurrentRefreshRate->toString().c_str());
+ continue;
+ }
+
+ if (layer.seamlessness == Seamlessness::SeamedAndSeamless && !isSeamlessSwitch &&
+ !layer.focused) {
+ ALOGV("%s ignores %s because it's not focused and the switch is going to be seamed."
+ " Current mode = %s",
+ formatLayerInfo(layer, weight).c_str(),
+ scores[i].refreshRate->toString().c_str(),
+ mCurrentRefreshRate->toString().c_str());
+ continue;
+ }
+
+ // Layers with default seamlessness vote for the current mode group if
+ // there are layers with seamlessness=SeamedAndSeamless and for the default
+ // mode group otherwise. In second case, if the current mode group is different
+ // from the default, this means a layer with seamlessness=SeamedAndSeamless has just
+ // disappeared.
+ const bool isInPolicyForDefault = seamedFocusedLayers > 0
+ ? scores[i].refreshRate->getModeGroup() == mCurrentRefreshRate->getModeGroup()
+ : scores[i].refreshRate->getModeGroup() == defaultMode->getModeGroup();
+
+ if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault) {
+ ALOGV("%s ignores %s. Current mode = %s", formatLayerInfo(layer, weight).c_str(),
+ scores[i].refreshRate->toString().c_str(),
+ mCurrentRefreshRate->toString().c_str());
+ continue;
+ }
+
+ bool inPrimaryRange = scores[i].refreshRate->inPolicy(policy->primaryRange.min,
+ policy->primaryRange.max);
if ((primaryRangeIsSingleRate || !inPrimaryRange) &&
- !(layer.focused && layer.vote == LayerVoteType::ExplicitDefault)) {
+ !(layer.focused &&
+ (layer.vote == LayerVoteType::ExplicitDefault ||
+ layer.vote == LayerVoteType::ExplicitExact))) {
// Only focused layers with ExplicitDefault frame rate settings are allowed to score
// refresh rates outside the primary range.
continue;
}
- // If the layer wants Max, give higher score to the higher refresh rate
- if (layer.vote == LayerVoteType::Max) {
- const auto ratio = scores[i].first->fps / scores.back().first->fps;
- // use ratio^2 to get a lower score the more we get further from peak
- const auto layerScore = ratio * ratio;
- ALOGV("%s (Max, weight %.2f) gives %s score of %.2f", layer.name.c_str(), weight,
- scores[i].first->name.c_str(), layerScore);
- scores[i].second += weight * layerScore;
- continue;
- }
-
- const auto displayPeriod = scores[i].first->hwcConfig->getVsyncPeriod();
- const auto layerPeriod = round<nsecs_t>(1e9f / layer.desiredRefreshRate);
- if (layer.vote == LayerVoteType::ExplicitDefault) {
- const auto layerScore = [&]() {
- // Find the actual rate the layer will render, assuming
- // that layerPeriod is the minimal time to render a frame
- auto actualLayerPeriod = displayPeriod;
- int multiplier = 1;
- while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) {
- multiplier++;
- actualLayerPeriod = displayPeriod * multiplier;
- }
- return std::min(1.0f,
- static_cast<float>(layerPeriod) /
- static_cast<float>(actualLayerPeriod));
- }();
-
- ALOGV("%s (ExplicitDefault, weight %.2f) %.2fHz gives %s score of %.2f",
- layer.name.c_str(), weight, 1e9f / layerPeriod, scores[i].first->name.c_str(),
- layerScore);
- scores[i].second += weight * layerScore;
- continue;
- }
-
- if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
- layer.vote == LayerVoteType::Heuristic) {
- const auto layerScore = [&] {
- // Calculate how many display vsyncs we need to present a single frame for this
- // layer
- const auto [displayFramesQuot, displayFramesRem] =
- getDisplayFrames(layerPeriod, displayPeriod);
- static constexpr size_t MAX_FRAMES_TO_FIT =
- 10; // Stop calculating when score < 0.1
- if (displayFramesRem == 0) {
- // Layer desired refresh rate matches the display rate.
- return 1.0f;
- }
-
- if (displayFramesQuot == 0) {
- // Layer desired refresh rate is higher the display rate.
- return (static_cast<float>(layerPeriod) /
- static_cast<float>(displayPeriod)) *
- (1.0f / (MAX_FRAMES_TO_FIT + 1));
- }
-
- // Layer desired refresh rate is lower the display rate. Check how well it fits
- // the cadence
- auto diff = std::abs(displayFramesRem - (displayPeriod - displayFramesRem));
- int iter = 2;
- while (diff > MARGIN_FOR_PERIOD_CALCULATION && iter < MAX_FRAMES_TO_FIT) {
- diff = diff - (displayPeriod - diff);
- iter++;
- }
-
- return 1.0f / iter;
- }();
- ALOGV("%s (%s, weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(),
- layerVoteTypeString(layer.vote).c_str(), weight, 1e9f / layerPeriod,
- scores[i].first->name.c_str(), layerScore);
- scores[i].second += weight * layerScore;
- continue;
- }
+ const auto layerScore =
+ calculateLayerScoreLocked(layer, *scores[i].refreshRate, isSeamlessSwitch);
+ ALOGV("%s gives %s score of %.2f", formatLayerInfo(layer, weight).c_str(),
+ scores[i].refreshRate->getName().c_str(), layerScore);
+ scores[i].score += weight * layerScore;
}
}
@@ -305,7 +370,7 @@
// If we never scored any layers, then choose the rate from the primary
// range instead of picking a random score from the app range.
if (std::all_of(scores.begin(), scores.end(),
- [](std::pair<const RefreshRate*, float> p) { return p.second == 0; })) {
+ [](RefreshRateScore score) { return score.score == 0; })) {
ALOGV("layers not scored - choose %s",
getMaxRefreshRateByPolicyLocked().getName().c_str());
return getMaxRefreshRateByPolicyLocked();
@@ -320,8 +385,9 @@
// actually increase the refresh rate over the normal selection.
const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked();
- if (globalSignals.touch && explicitDefaultVoteLayers == 0 &&
- bestRefreshRate->fps < touchRefreshRate.fps) {
+ bool touchBoostForExplicitExact = explicitExact == 0 || mSupportsFrameRateOverride;
+ if (globalSignals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact &&
+ bestRefreshRate->fps.lessThanWithMargin(touchRefreshRate.fps)) {
setTouchConsidered();
ALOGV("TouchBoost - choose %s", touchRefreshRate.getName().c_str());
return touchRefreshRate;
@@ -330,16 +396,124 @@
return *bestRefreshRate;
}
+std::unordered_map<uid_t, std::vector<const RefreshRateConfigs::LayerRequirement*>>
+groupLayersByUid(const std::vector<RefreshRateConfigs::LayerRequirement>& layers) {
+ std::unordered_map<uid_t, std::vector<const RefreshRateConfigs::LayerRequirement*>> layersByUid;
+ for (const auto& layer : layers) {
+ auto iter = layersByUid.emplace(layer.ownerUid,
+ std::vector<const RefreshRateConfigs::LayerRequirement*>());
+ auto& layersWithSameUid = iter.first->second;
+ layersWithSameUid.push_back(&layer);
+ }
+
+ // Remove uids that can't have a frame rate override
+ for (auto iter = layersByUid.begin(); iter != layersByUid.end();) {
+ const auto& layersWithSameUid = iter->second;
+ bool skipUid = false;
+ for (const auto& layer : layersWithSameUid) {
+ if (layer->vote == RefreshRateConfigs::LayerVoteType::Max ||
+ layer->vote == RefreshRateConfigs::LayerVoteType::Heuristic) {
+ skipUid = true;
+ break;
+ }
+ }
+ if (skipUid) {
+ iter = layersByUid.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+
+ return layersByUid;
+}
+
+std::vector<RefreshRateScore> initializeScoresForAllRefreshRates(
+ const AllRefreshRatesMapType& refreshRates) {
+ std::vector<RefreshRateScore> scores;
+ scores.reserve(refreshRates.size());
+ for (const auto& [ignored, refreshRate] : refreshRates) {
+ scores.emplace_back(RefreshRateScore{refreshRate.get(), 0.0f});
+ }
+ std::sort(scores.begin(), scores.end(),
+ [](const auto& a, const auto& b) { return *a.refreshRate < *b.refreshRate; });
+ return scores;
+}
+
+RefreshRateConfigs::UidToFrameRateOverride RefreshRateConfigs::getFrameRateOverrides(
+ const std::vector<LayerRequirement>& layers, Fps displayFrameRate, bool touch) const {
+ ATRACE_CALL();
+ if (!mSupportsFrameRateOverride) return {};
+
+ ALOGV("getFrameRateOverrides %zu layers", layers.size());
+ std::lock_guard lock(mLock);
+ std::vector<RefreshRateScore> scores = initializeScoresForAllRefreshRates(mRefreshRates);
+ std::unordered_map<uid_t, std::vector<const LayerRequirement*>> layersByUid =
+ groupLayersByUid(layers);
+ UidToFrameRateOverride frameRateOverrides;
+ for (const auto& [uid, layersWithSameUid] : layersByUid) {
+ // Layers with ExplicitExactOrMultiple expect touch boost
+ const bool hasExplicitExactOrMultiple =
+ std::any_of(layersWithSameUid.cbegin(), layersWithSameUid.cend(),
+ [](const auto& layer) {
+ return layer->vote == LayerVoteType::ExplicitExactOrMultiple;
+ });
+
+ if (touch && hasExplicitExactOrMultiple) {
+ continue;
+ }
+
+ for (auto& score : scores) {
+ score.score = 0;
+ }
+
+ for (const auto& layer : layersWithSameUid) {
+ if (layer->vote == LayerVoteType::NoVote || layer->vote == LayerVoteType::Min) {
+ continue;
+ }
+
+ LOG_ALWAYS_FATAL_IF(layer->vote != LayerVoteType::ExplicitDefault &&
+ layer->vote != LayerVoteType::ExplicitExactOrMultiple &&
+ layer->vote != LayerVoteType::ExplicitExact);
+ for (RefreshRateScore& score : scores) {
+ const auto layerScore = calculateLayerScoreLocked(*layer, *score.refreshRate,
+ /*isSeamlessSwitch*/ true);
+ score.score += layer->weight * layerScore;
+ }
+ }
+
+ // We just care about the refresh rates which are a divider of the
+ // display refresh rate
+ auto iter =
+ std::remove_if(scores.begin(), scores.end(), [&](const RefreshRateScore& score) {
+ return getFrameRateDivider(displayFrameRate, score.refreshRate->getFps()) == 0;
+ });
+ scores.erase(iter, scores.end());
+
+ // If we never scored any layers, we don't have a preferred frame rate
+ if (std::all_of(scores.begin(), scores.end(),
+ [](const RefreshRateScore& score) { return score.score == 0; })) {
+ continue;
+ }
+
+ // Now that we scored all the refresh rates we need to pick the one that got the highest
+ // score.
+ const RefreshRate* bestRefreshRate = getBestRefreshRate(scores.begin(), scores.end());
+ frameRateOverrides.emplace(uid, bestRefreshRate->getFps());
+ }
+
+ return frameRateOverrides;
+}
+
template <typename Iter>
const RefreshRate* RefreshRateConfigs::getBestRefreshRate(Iter begin, Iter end) const {
constexpr auto EPSILON = 0.001f;
- const RefreshRate* bestRefreshRate = begin->first;
- float max = begin->second;
+ const RefreshRate* bestRefreshRate = begin->refreshRate;
+ float max = begin->score;
for (auto i = begin; i != end; ++i) {
const auto [refreshRate, score] = *i;
- ALOGV("%s scores %.2f", refreshRate->name.c_str(), score);
+ ALOGV("%s scores %.2f", refreshRate->getName().c_str(), score);
- ATRACE_INT(refreshRate->name.c_str(), round<int>(score * 100));
+ ATRACE_INT(refreshRate->getName().c_str(), round<int>(score * 100));
if (score > max * (1 + EPSILON)) {
max = score;
@@ -350,34 +524,60 @@
return bestRefreshRate;
}
-const AllRefreshRatesMapType& RefreshRateConfigs::getAllRefreshRates() const {
- return mRefreshRates;
-}
-
-const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicy() const {
+std::optional<Fps> RefreshRateConfigs::onKernelTimerChanged(
+ std::optional<DisplayModeId> desiredActiveConfigId, bool timerExpired) const {
std::lock_guard lock(mLock);
- return getMinRefreshRateByPolicyLocked();
+
+ const auto& current = desiredActiveConfigId ? *mRefreshRates.at(*desiredActiveConfigId)
+ : *mCurrentRefreshRate;
+ const auto& min = *mMinSupportedRefreshRate;
+
+ if (current != min) {
+ const auto& refreshRate = timerExpired ? min : current;
+ return refreshRate.getFps();
+ }
+
+ return {};
}
const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicyLocked() const {
+ for (auto refreshRate : mPrimaryRefreshRates) {
+ if (mCurrentRefreshRate->getModeGroup() == refreshRate->getModeGroup()) {
+ return *refreshRate;
+ }
+ }
+ ALOGE("Can't find min refresh rate by policy with the same mode group"
+ " as the current mode %s",
+ mCurrentRefreshRate->toString().c_str());
+ // Defaulting to the lowest refresh rate
return *mPrimaryRefreshRates.front();
}
-const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicy() const {
+RefreshRate RefreshRateConfigs::getMaxRefreshRateByPolicy() const {
std::lock_guard lock(mLock);
return getMaxRefreshRateByPolicyLocked();
}
const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked() const {
+ for (auto it = mPrimaryRefreshRates.rbegin(); it != mPrimaryRefreshRates.rend(); it++) {
+ const auto& refreshRate = (**it);
+ if (mCurrentRefreshRate->getModeGroup() == refreshRate.getModeGroup()) {
+ return refreshRate;
+ }
+ }
+ ALOGE("Can't find max refresh rate by policy with the same mode group"
+ " as the current mode %s",
+ mCurrentRefreshRate->toString().c_str());
+ // Defaulting to the highest refresh rate
return *mPrimaryRefreshRates.back();
}
-const RefreshRate& RefreshRateConfigs::getCurrentRefreshRate() const {
+RefreshRate RefreshRateConfigs::getCurrentRefreshRate() const {
std::lock_guard lock(mLock);
return *mCurrentRefreshRate;
}
-const RefreshRate& RefreshRateConfigs::getCurrentRefreshRateByPolicy() const {
+RefreshRate RefreshRateConfigs::getCurrentRefreshRateByPolicy() const {
std::lock_guard lock(mLock);
return getCurrentRefreshRateByPolicyLocked();
}
@@ -387,58 +587,83 @@
mCurrentRefreshRate) != mAppRequestRefreshRates.end()) {
return *mCurrentRefreshRate;
}
- return *mRefreshRates.at(getCurrentPolicyLocked()->defaultConfig);
+ return *mRefreshRates.at(getCurrentPolicyLocked()->defaultMode);
}
-void RefreshRateConfigs::setCurrentConfigId(HwcConfigIndexType configId) {
+void RefreshRateConfigs::setCurrentModeId(DisplayModeId modeId) {
std::lock_guard lock(mLock);
- mCurrentRefreshRate = mRefreshRates.at(configId).get();
+ mCurrentRefreshRate = mRefreshRates.at(modeId).get();
}
-RefreshRateConfigs::RefreshRateConfigs(
- const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
- HwcConfigIndexType currentConfigId)
- : mKnownFrameRates(constructKnownFrameRates(configs)) {
- LOG_ALWAYS_FATAL_IF(configs.empty());
- LOG_ALWAYS_FATAL_IF(currentConfigId.value() >= configs.size());
+RefreshRateConfigs::RefreshRateConfigs(const DisplayModes& modes, DisplayModeId currentModeId,
+ bool enableFrameRateOverride)
+ : mKnownFrameRates(constructKnownFrameRates(modes)),
+ mEnableFrameRateOverride(enableFrameRateOverride) {
+ updateDisplayModes(modes, currentModeId);
+}
- for (auto configId = HwcConfigIndexType(0); configId.value() < configs.size(); configId++) {
- const auto& config = configs.at(static_cast<size_t>(configId.value()));
- const float fps = 1e9f / config->getVsyncPeriod();
- mRefreshRates.emplace(configId,
- std::make_unique<RefreshRate>(configId, config,
- base::StringPrintf("%.0ffps", fps), fps,
+void RefreshRateConfigs::updateDisplayModes(const DisplayModes& modes,
+ DisplayModeId currentModeId) {
+ std::lock_guard lock(mLock);
+ // The current mode should be supported
+ LOG_ALWAYS_FATAL_IF(std::none_of(modes.begin(), modes.end(), [&](DisplayModePtr mode) {
+ return mode->getId() == currentModeId;
+ }));
+
+ mRefreshRates.clear();
+ for (const auto& mode : modes) {
+ const auto modeId = mode->getId();
+ mRefreshRates.emplace(modeId,
+ std::make_unique<RefreshRate>(modeId, mode, mode->getFps(),
RefreshRate::ConstructorTag(0)));
- if (configId == currentConfigId) {
- mCurrentRefreshRate = mRefreshRates.at(configId).get();
+ if (modeId == currentModeId) {
+ mCurrentRefreshRate = mRefreshRates.at(modeId).get();
}
}
- std::vector<const RefreshRate*> sortedConfigs;
- getSortedRefreshRateList([](const RefreshRate&) { return true; }, &sortedConfigs);
- mDisplayManagerPolicy.defaultConfig = currentConfigId;
- mMinSupportedRefreshRate = sortedConfigs.front();
- mMaxSupportedRefreshRate = sortedConfigs.back();
+ std::vector<const RefreshRate*> sortedModes;
+ getSortedRefreshRateListLocked([](const RefreshRate&) { return true; }, &sortedModes);
+ // Reset the policy because the old one may no longer be valid.
+ mDisplayManagerPolicy = {};
+ mDisplayManagerPolicy.defaultMode = currentModeId;
+ mMinSupportedRefreshRate = sortedModes.front();
+ mMaxSupportedRefreshRate = sortedModes.back();
+
+ mSupportsFrameRateOverride = false;
+ if (mEnableFrameRateOverride) {
+ for (const auto& mode1 : sortedModes) {
+ for (const auto& mode2 : sortedModes) {
+ if (getFrameRateDivider(mode1->getFps(), mode2->getFps()) >= 2) {
+ mSupportsFrameRateOverride = true;
+ break;
+ }
+ }
+ }
+ }
+
constructAvailableRefreshRates();
}
-bool RefreshRateConfigs::isPolicyValid(const Policy& policy) {
- // defaultConfig must be a valid config, and within the given refresh rate range.
- auto iter = mRefreshRates.find(policy.defaultConfig);
+bool RefreshRateConfigs::isPolicyValidLocked(const Policy& policy) const {
+ // defaultMode must be a valid mode, and within the given refresh rate range.
+ auto iter = mRefreshRates.find(policy.defaultMode);
if (iter == mRefreshRates.end()) {
+ ALOGE("Default mode is not found.");
return false;
}
const RefreshRate& refreshRate = *iter->second;
if (!refreshRate.inPolicy(policy.primaryRange.min, policy.primaryRange.max)) {
+ ALOGE("Default mode is not in the primary range.");
return false;
}
- return policy.appRequestRange.min <= policy.primaryRange.min &&
- policy.appRequestRange.max >= policy.primaryRange.max;
+ return policy.appRequestRange.min.lessThanOrEqualWithMargin(policy.primaryRange.min) &&
+ policy.appRequestRange.max.greaterThanOrEqualWithMargin(policy.primaryRange.max);
}
status_t RefreshRateConfigs::setDisplayManagerPolicy(const Policy& policy) {
std::lock_guard lock(mLock);
- if (!isPolicyValid(policy)) {
+ if (!isPolicyValidLocked(policy)) {
+ ALOGE("Invalid refresh rate policy: %s", policy.toString().c_str());
return BAD_VALUE;
}
Policy previousPolicy = *getCurrentPolicyLocked();
@@ -452,7 +677,7 @@
status_t RefreshRateConfigs::setOverridePolicy(const std::optional<Policy>& policy) {
std::lock_guard lock(mLock);
- if (policy && !isPolicyValid(*policy)) {
+ if (policy && !isPolicyValidLocked(*policy)) {
return BAD_VALUE;
}
Policy previousPolicy = *getCurrentPolicyLocked();
@@ -478,79 +703,76 @@
return mDisplayManagerPolicy;
}
-bool RefreshRateConfigs::isConfigAllowed(HwcConfigIndexType config) const {
+bool RefreshRateConfigs::isModeAllowed(DisplayModeId modeId) const {
std::lock_guard lock(mLock);
for (const RefreshRate* refreshRate : mAppRequestRefreshRates) {
- if (refreshRate->configId == config) {
+ if (refreshRate->modeId == modeId) {
return true;
}
}
return false;
}
-void RefreshRateConfigs::getSortedRefreshRateList(
+void RefreshRateConfigs::getSortedRefreshRateListLocked(
const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
std::vector<const RefreshRate*>* outRefreshRates) {
outRefreshRates->clear();
outRefreshRates->reserve(mRefreshRates.size());
for (const auto& [type, refreshRate] : mRefreshRates) {
if (shouldAddRefreshRate(*refreshRate)) {
- ALOGV("getSortedRefreshRateList: config %d added to list policy",
- refreshRate->configId.value());
+ ALOGV("getSortedRefreshRateListLocked: mode %d added to list policy",
+ refreshRate->modeId.value());
outRefreshRates->push_back(refreshRate.get());
}
}
std::sort(outRefreshRates->begin(), outRefreshRates->end(),
[](const auto refreshRate1, const auto refreshRate2) {
- if (refreshRate1->hwcConfig->getVsyncPeriod() !=
- refreshRate2->hwcConfig->getVsyncPeriod()) {
- return refreshRate1->hwcConfig->getVsyncPeriod() >
- refreshRate2->hwcConfig->getVsyncPeriod();
+ if (refreshRate1->mode->getVsyncPeriod() !=
+ refreshRate2->mode->getVsyncPeriod()) {
+ return refreshRate1->mode->getVsyncPeriod() >
+ refreshRate2->mode->getVsyncPeriod();
} else {
- return refreshRate1->hwcConfig->getConfigGroup() >
- refreshRate2->hwcConfig->getConfigGroup();
+ return refreshRate1->mode->getGroup() > refreshRate2->mode->getGroup();
}
});
}
void RefreshRateConfigs::constructAvailableRefreshRates() {
- // Filter configs based on current policy and sort based on vsync period
+ // Filter modes based on current policy and sort based on vsync period
const Policy* policy = getCurrentPolicyLocked();
- const auto& defaultConfig = mRefreshRates.at(policy->defaultConfig)->hwcConfig;
- ALOGV("constructAvailableRefreshRates: default %d group %d primaryRange=[%.2f %.2f]"
- " appRequestRange=[%.2f %.2f]",
- policy->defaultConfig.value(), defaultConfig->getConfigGroup(), policy->primaryRange.min,
- policy->primaryRange.max, policy->appRequestRange.min, policy->appRequestRange.max);
+ const auto& defaultMode = mRefreshRates.at(policy->defaultMode)->mode;
+ ALOGV("constructAvailableRefreshRates: %s ", policy->toString().c_str());
- auto filterRefreshRates = [&](float min, float max, const char* listName,
- std::vector<const RefreshRate*>* outRefreshRates) {
- getSortedRefreshRateList(
- [&](const RefreshRate& refreshRate) REQUIRES(mLock) {
- const auto& hwcConfig = refreshRate.hwcConfig;
+ auto filterRefreshRates =
+ [&](Fps min, Fps max, const char* listName,
+ std::vector<const RefreshRate*>* outRefreshRates) REQUIRES(mLock) {
+ getSortedRefreshRateListLocked(
+ [&](const RefreshRate& refreshRate) REQUIRES(mLock) {
+ const auto& mode = refreshRate.mode;
- return hwcConfig->getHeight() == defaultConfig->getHeight() &&
- hwcConfig->getWidth() == defaultConfig->getWidth() &&
- hwcConfig->getDpiX() == defaultConfig->getDpiX() &&
- hwcConfig->getDpiY() == defaultConfig->getDpiY() &&
- (policy->allowGroupSwitching ||
- hwcConfig->getConfigGroup() == defaultConfig->getConfigGroup()) &&
- refreshRate.inPolicy(min, max);
- },
- outRefreshRates);
+ return mode->getHeight() == defaultMode->getHeight() &&
+ mode->getWidth() == defaultMode->getWidth() &&
+ mode->getDpiX() == defaultMode->getDpiX() &&
+ mode->getDpiY() == defaultMode->getDpiY() &&
+ (policy->allowGroupSwitching ||
+ mode->getGroup() == defaultMode->getGroup()) &&
+ refreshRate.inPolicy(min, max);
+ },
+ outRefreshRates);
- LOG_ALWAYS_FATAL_IF(outRefreshRates->empty(),
- "No matching configs for %s range: min=%.0f max=%.0f", listName, min,
- max);
- auto stringifyRefreshRates = [&]() -> std::string {
- std::string str;
- for (auto refreshRate : *outRefreshRates) {
- base::StringAppendF(&str, "%s ", refreshRate->name.c_str());
- }
- return str;
- };
- ALOGV("%s refresh rates: %s", listName, stringifyRefreshRates().c_str());
- };
+ LOG_ALWAYS_FATAL_IF(outRefreshRates->empty(),
+ "No matching modes for %s range: min=%s max=%s", listName,
+ to_string(min).c_str(), to_string(max).c_str());
+ auto stringifyRefreshRates = [&]() -> std::string {
+ std::string str;
+ for (auto refreshRate : *outRefreshRates) {
+ base::StringAppendF(&str, "%s ", refreshRate->getName().c_str());
+ }
+ return str;
+ };
+ ALOGV("%s refresh rates: %s", listName, stringifyRefreshRates().c_str());
+ };
filterRefreshRates(policy->primaryRange.min, policy->primaryRange.max, "primary",
&mPrimaryRefreshRates);
@@ -558,45 +780,26 @@
&mAppRequestRefreshRates);
}
-std::vector<float> RefreshRateConfigs::constructKnownFrameRates(
- const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
- std::vector<float> knownFrameRates = {24.0f, 30.0f, 45.0f, 60.0f, 72.0f};
- knownFrameRates.reserve(knownFrameRates.size() + configs.size());
-
- // Add all supported refresh rates to the set
- for (const auto& config : configs) {
- const auto refreshRate = 1e9f / config->getVsyncPeriod();
- knownFrameRates.emplace_back(refreshRate);
- }
-
- // Sort and remove duplicates
- const auto frameRatesEqual = [](float a, float b) { return std::abs(a - b) <= 0.01f; };
- std::sort(knownFrameRates.begin(), knownFrameRates.end());
- knownFrameRates.erase(std::unique(knownFrameRates.begin(), knownFrameRates.end(),
- frameRatesEqual),
- knownFrameRates.end());
- return knownFrameRates;
-}
-
-float RefreshRateConfigs::findClosestKnownFrameRate(float frameRate) const {
- if (frameRate <= *mKnownFrameRates.begin()) {
+Fps RefreshRateConfigs::findClosestKnownFrameRate(Fps frameRate) const {
+ if (frameRate.lessThanOrEqualWithMargin(*mKnownFrameRates.begin())) {
return *mKnownFrameRates.begin();
}
- if (frameRate >= *std::prev(mKnownFrameRates.end())) {
+ if (frameRate.greaterThanOrEqualWithMargin(*std::prev(mKnownFrameRates.end()))) {
return *std::prev(mKnownFrameRates.end());
}
- auto lowerBound = std::lower_bound(mKnownFrameRates.begin(), mKnownFrameRates.end(), frameRate);
+ auto lowerBound = std::lower_bound(mKnownFrameRates.begin(), mKnownFrameRates.end(), frameRate,
+ Fps::comparesLess);
- const auto distance1 = std::abs(frameRate - *lowerBound);
- const auto distance2 = std::abs(frameRate - *std::prev(lowerBound));
+ const auto distance1 = std::abs((frameRate.getValue() - lowerBound->getValue()));
+ const auto distance2 = std::abs((frameRate.getValue() - std::prev(lowerBound)->getValue()));
return distance1 < distance2 ? *lowerBound : *std::prev(lowerBound);
}
RefreshRateConfigs::KernelIdleTimerAction RefreshRateConfigs::getIdleTimerAction() const {
std::lock_guard lock(mLock);
- const auto& deviceMin = getMinRefreshRate();
+ const auto& deviceMin = *mMinSupportedRefreshRate;
const auto& minByPolicy = getMinRefreshRateByPolicyLocked();
const auto& maxByPolicy = getMaxRefreshRateByPolicyLocked();
@@ -618,4 +821,44 @@
return RefreshRateConfigs::KernelIdleTimerAction::TurnOn;
}
+int RefreshRateConfigs::getFrameRateDivider(Fps displayFrameRate, Fps layerFrameRate) {
+ // This calculation needs to be in sync with the java code
+ // in DisplayManagerService.getDisplayInfoForFrameRateOverride
+ constexpr float kThreshold = 0.1f;
+ const auto numPeriods = displayFrameRate.getValue() / layerFrameRate.getValue();
+ const auto numPeriodsRounded = std::round(numPeriods);
+ if (std::abs(numPeriods - numPeriodsRounded) > kThreshold) {
+ return 0;
+ }
+
+ return static_cast<int>(numPeriodsRounded);
+}
+
+void RefreshRateConfigs::dump(std::string& result) const {
+ std::lock_guard lock(mLock);
+ base::StringAppendF(&result, "DesiredDisplayModeSpecs (DisplayManager): %s\n\n",
+ mDisplayManagerPolicy.toString().c_str());
+ scheduler::RefreshRateConfigs::Policy currentPolicy = *getCurrentPolicyLocked();
+ if (mOverridePolicy && currentPolicy != mDisplayManagerPolicy) {
+ base::StringAppendF(&result, "DesiredDisplayModeSpecs (Override): %s\n\n",
+ currentPolicy.toString().c_str());
+ }
+
+ auto mode = mCurrentRefreshRate->mode;
+ base::StringAppendF(&result, "Current mode: %s\n", mCurrentRefreshRate->toString().c_str());
+
+ result.append("Refresh rates:\n");
+ for (const auto& [id, refreshRate] : mRefreshRates) {
+ mode = refreshRate->mode;
+ base::StringAppendF(&result, "\t%s\n", refreshRate->toString().c_str());
+ }
+
+ base::StringAppendF(&result, "Supports Frame Rate Override: %s\n",
+ mSupportsFrameRateOverride ? "yes" : "no");
+ result.append("\n");
+}
+
} // namespace android::scheduler
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wextra"
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 27bf0ec..ee89149 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -17,15 +17,18 @@
#pragma once
#include <android-base/stringprintf.h>
+#include <gui/DisplayEventReceiver.h>
#include <algorithm>
#include <numeric>
#include <optional>
#include <type_traits>
+#include "DisplayHardware/DisplayMode.h"
#include "DisplayHardware/HWComposer.h"
-#include "HwcStrongTypes.h"
+#include "Fps.h"
#include "Scheduler/SchedulerUtils.h"
+#include "Scheduler/Seamlessness.h"
#include "Scheduler/StrongTyping.h"
namespace android::scheduler {
@@ -39,6 +42,8 @@
return static_cast<RefreshRateConfigEvent>(static_cast<T>(lhs) | static_cast<T>(rhs));
}
+using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
+
/**
* This class is used to encapsulate configuration for refresh rates. It holds information
* about available refresh rates on the device, and the mapping between the numbers and human
@@ -59,99 +64,114 @@
};
public:
- RefreshRate(HwcConfigIndexType configId,
- std::shared_ptr<const HWC2::Display::Config> config, std::string name,
- float fps, ConstructorTag)
- : configId(configId), hwcConfig(config), name(std::move(name)), fps(fps) {}
+ RefreshRate(DisplayModeId modeId, DisplayModePtr mode, Fps fps, ConstructorTag)
+ : modeId(modeId), mode(mode), fps(std::move(fps)) {}
- RefreshRate(const RefreshRate&) = delete;
-
- HwcConfigIndexType getConfigId() const { return configId; }
- nsecs_t getVsyncPeriod() const { return hwcConfig->getVsyncPeriod(); }
- int32_t getConfigGroup() const { return hwcConfig->getConfigGroup(); }
- const std::string& getName() const { return name; }
- float getFps() const { return fps; }
+ DisplayModeId getModeId() const { return modeId; }
+ nsecs_t getVsyncPeriod() const { return mode->getVsyncPeriod(); }
+ int32_t getModeGroup() const { return mode->getGroup(); }
+ std::string getName() const { return to_string(fps); }
+ Fps getFps() const { return fps; }
// Checks whether the fps of this RefreshRate struct is within a given min and max refresh
- // rate passed in. FPS_EPSILON is applied to the boundaries for approximation.
- bool inPolicy(float minRefreshRate, float maxRefreshRate) const {
- return (fps >= (minRefreshRate - FPS_EPSILON) && fps <= (maxRefreshRate + FPS_EPSILON));
+ // rate passed in. Margin of error is applied to the boundaries for approximation.
+ bool inPolicy(Fps minRefreshRate, Fps maxRefreshRate) const {
+ return minRefreshRate.lessThanOrEqualWithMargin(fps) &&
+ fps.lessThanOrEqualWithMargin(maxRefreshRate);
}
bool operator!=(const RefreshRate& other) const {
- return configId != other.configId || hwcConfig != other.hwcConfig;
+ return modeId != other.modeId || mode != other.mode;
}
- bool operator<(const RefreshRate& other) const { return getFps() < other.getFps(); }
+ bool operator<(const RefreshRate& other) const {
+ return getFps().getValue() < other.getFps().getValue();
+ }
bool operator==(const RefreshRate& other) const { return !(*this != other); }
+ std::string toString() const;
+ friend std::ostream& operator<<(std::ostream& os, const RefreshRate& refreshRate) {
+ return os << refreshRate.toString();
+ }
+
private:
friend RefreshRateConfigs;
friend class RefreshRateConfigsTest;
- // The tolerance within which we consider FPS approximately equals.
- static constexpr float FPS_EPSILON = 0.001f;
-
- // This config ID corresponds to the position of the config in the vector that is stored
- // on the device.
- const HwcConfigIndexType configId;
- // The config itself
- std::shared_ptr<const HWC2::Display::Config> hwcConfig;
- // Human readable name of the refresh rate.
- const std::string name;
+ const DisplayModeId modeId;
+ DisplayModePtr mode;
// Refresh rate in frames per second
- const float fps = 0;
+ const Fps fps{0.0f};
};
using AllRefreshRatesMapType =
- std::unordered_map<HwcConfigIndexType, std::unique_ptr<const RefreshRate>>;
+ std::unordered_map<DisplayModeId, std::unique_ptr<const RefreshRate>>;
+
+ struct FpsRange {
+ Fps min{0.0f};
+ Fps max{std::numeric_limits<float>::max()};
+
+ bool operator==(const FpsRange& other) const {
+ return min.equalsWithMargin(other.min) && max.equalsWithMargin(other.max);
+ }
+
+ bool operator!=(const FpsRange& other) const { return !(*this == other); }
+
+ std::string toString() const {
+ return base::StringPrintf("[%s %s]", to_string(min).c_str(), to_string(max).c_str());
+ }
+ };
struct Policy {
- struct Range {
- float min = 0;
- float max = std::numeric_limits<float>::max();
+ private:
+ static constexpr int kAllowGroupSwitchingDefault = false;
- bool operator==(const Range& other) const {
- return min == other.min && max == other.max;
- }
-
- bool operator!=(const Range& other) const { return !(*this == other); }
- };
-
- // The default config, used to ensure we only initiate display config switches within the
- // same config group as defaultConfigId's group.
- HwcConfigIndexType defaultConfig;
+ public:
+ // The default mode, used to ensure we only initiate display mode switches within the
+ // same mode group as defaultMode's group.
+ DisplayModeId defaultMode;
+ // Whether or not we switch mode groups to get the best frame rate.
+ bool allowGroupSwitching = kAllowGroupSwitchingDefault;
// The primary refresh rate range represents display manager's general guidance on the
- // display configs we'll consider when switching refresh rates. Unless we get an explicit
+ // display modes we'll consider when switching refresh rates. Unless we get an explicit
// signal from an app, we should stay within this range.
- Range primaryRange;
- // The app request refresh rate range allows us to consider more display configs when
+ FpsRange primaryRange;
+ // The app request refresh rate range allows us to consider more display modes when
// switching refresh rates. Although we should generally stay within the primary range,
// specific considerations, such as layer frame rate settings specified via the
// setFrameRate() api, may cause us to go outside the primary range. We never go outside the
// app request range. The app request range will be greater than or equal to the primary
// refresh rate range, never smaller.
- Range appRequestRange;
- // Whether or not we switch config groups to get the best frame rate. Only used by tests.
- bool allowGroupSwitching = false;
+ FpsRange appRequestRange;
Policy() = default;
- Policy(HwcConfigIndexType defaultConfig, const Range& range)
- : Policy(defaultConfig, range, range) {}
- Policy(HwcConfigIndexType defaultConfig, const Range& primaryRange,
- const Range& appRequestRange)
- : defaultConfig(defaultConfig),
+
+ Policy(DisplayModeId defaultMode, const FpsRange& range)
+ : Policy(defaultMode, kAllowGroupSwitchingDefault, range, range) {}
+
+ Policy(DisplayModeId defaultMode, bool allowGroupSwitching, const FpsRange& range)
+ : Policy(defaultMode, allowGroupSwitching, range, range) {}
+
+ Policy(DisplayModeId defaultMode, const FpsRange& primaryRange,
+ const FpsRange& appRequestRange)
+ : Policy(defaultMode, kAllowGroupSwitchingDefault, primaryRange, appRequestRange) {}
+
+ Policy(DisplayModeId defaultMode, bool allowGroupSwitching, const FpsRange& primaryRange,
+ const FpsRange& appRequestRange)
+ : defaultMode(defaultMode),
+ allowGroupSwitching(allowGroupSwitching),
primaryRange(primaryRange),
appRequestRange(appRequestRange) {}
bool operator==(const Policy& other) const {
- return defaultConfig == other.defaultConfig && primaryRange == other.primaryRange &&
+ return defaultMode == other.defaultMode && primaryRange == other.primaryRange &&
appRequestRange == other.appRequestRange &&
allowGroupSwitching == other.allowGroupSwitching;
}
bool operator!=(const Policy& other) const { return !(*this == other); }
+ std::string toString() const;
};
// Return code set*Policy() to indicate the current policy is unchanged.
@@ -177,8 +197,8 @@
// Gets the display manager policy, regardless of whether an override policy is active.
Policy getDisplayManagerPolicy() const EXCLUDES(mLock);
- // Returns true if config is allowed by the current policy.
- bool isConfigAllowed(HwcConfigIndexType config) const EXCLUDES(mLock);
+ // Returns true if mode is allowed by the current policy.
+ bool isModeAllowed(DisplayModeId) const EXCLUDES(mLock);
// Describes the different options the layer voted for refresh rate
enum class LayerVoteType {
@@ -188,8 +208,11 @@
Heuristic, // Specific refresh rate that was calculated by platform using a heuristic
ExplicitDefault, // Specific refresh rate that was provided by the app with Default
// compatibility
- ExplicitExactOrMultiple // Specific refresh rate that was provided by the app with
- // ExactOrMultiple compatibility
+ ExplicitExactOrMultiple, // Specific refresh rate that was provided by the app with
+ // ExactOrMultiple compatibility
+ ExplicitExact, // Specific refresh rate that was provided by the app with
+ // Exact compatibility
+
};
// Captures the layer requirements for a refresh rate. This will be used to determine the
@@ -197,10 +220,14 @@
struct LayerRequirement {
// Layer's name. Used for debugging purposes.
std::string name;
+ // Layer's owner uid
+ uid_t ownerUid = static_cast<uid_t>(-1);
// Layer vote type.
LayerVoteType vote = LayerVoteType::NoVote;
// Layer's desired refresh rate, if applicable.
- float desiredRefreshRate = 0.0f;
+ Fps desiredRefreshRate{0.0f};
+ // If a seamless mode switch is required.
+ Seamlessness seamlessness = Seamlessness::Default;
// Layer's weight in the range of [0, 1]. The higher the weight the more impact this layer
// would have on choosing the refresh rate.
float weight = 0.0f;
@@ -209,17 +236,14 @@
bool operator==(const LayerRequirement& other) const {
return name == other.name && vote == other.vote &&
- desiredRefreshRate == other.desiredRefreshRate && weight == other.weight &&
+ desiredRefreshRate.equalsWithMargin(other.desiredRefreshRate) &&
+ seamlessness == other.seamlessness && weight == other.weight &&
focused == other.focused;
}
bool operator!=(const LayerRequirement& other) const { return !(*this == other); }
};
- // Returns the refresh rate that fits best to the given layers.
- const RefreshRate& getRefreshRateForContent(const std::vector<LayerRequirement>& layers) const
- EXCLUDES(mLock);
-
// Global state describing signals that affect refresh rate choice.
struct GlobalSignals {
// Whether the user touched the screen recently. Used to apply touch boost.
@@ -233,52 +257,59 @@
// globalSignals - global state of touch and idle
// outSignalsConsidered - An output param that tells the caller whether the refresh rate was
// chosen based on touch boost and/or idle timer.
- const RefreshRate& getBestRefreshRate(const std::vector<LayerRequirement>& layers,
- const GlobalSignals& globalSignals,
- GlobalSignals* outSignalsConsidered = nullptr) const
+ RefreshRate getBestRefreshRate(const std::vector<LayerRequirement>& layers,
+ const GlobalSignals& globalSignals,
+ GlobalSignals* outSignalsConsidered = nullptr) const
EXCLUDES(mLock);
- // Returns all the refresh rates supported by the device. This won't change at runtime.
- const AllRefreshRatesMapType& getAllRefreshRates() const EXCLUDES(mLock);
+ FpsRange getSupportedRefreshRateRange() const EXCLUDES(mLock) {
+ std::lock_guard lock(mLock);
+ return {mMinSupportedRefreshRate->getFps(), mMaxSupportedRefreshRate->getFps()};
+ }
- // Returns the lowest refresh rate supported by the device. This won't change at runtime.
- const RefreshRate& getMinRefreshRate() const { return *mMinSupportedRefreshRate; }
-
- // Returns the lowest refresh rate according to the current policy. May change at runtime. Only
- // uses the primary range, not the app request range.
- const RefreshRate& getMinRefreshRateByPolicy() const EXCLUDES(mLock);
-
- // Returns the highest refresh rate supported by the device. This won't change at runtime.
- const RefreshRate& getMaxRefreshRate() const { return *mMaxSupportedRefreshRate; }
+ std::optional<Fps> onKernelTimerChanged(std::optional<DisplayModeId> desiredActiveModeId,
+ bool timerExpired) const EXCLUDES(mLock);
// Returns the highest refresh rate according to the current policy. May change at runtime. Only
// uses the primary range, not the app request range.
- const RefreshRate& getMaxRefreshRateByPolicy() const EXCLUDES(mLock);
+ RefreshRate getMaxRefreshRateByPolicy() const EXCLUDES(mLock);
// Returns the current refresh rate
- const RefreshRate& getCurrentRefreshRate() const EXCLUDES(mLock);
+ RefreshRate getCurrentRefreshRate() const EXCLUDES(mLock);
// Returns the current refresh rate, if allowed. Otherwise the default that is allowed by
// the policy.
- const RefreshRate& getCurrentRefreshRateByPolicy() const;
+ RefreshRate getCurrentRefreshRateByPolicy() const;
- // Returns the refresh rate that corresponds to a HwcConfigIndexType. This won't change at
+ // Returns the refresh rate that corresponds to a DisplayModeId. This may change at
// runtime.
- const RefreshRate& getRefreshRateFromConfigId(HwcConfigIndexType configId) const {
- return *mRefreshRates.at(configId);
+ // TODO(b/159590486) An invalid mode id may be given here if the dipslay modes have changed.
+ RefreshRate getRefreshRateFromModeId(DisplayModeId modeId) const EXCLUDES(mLock) {
+ std::lock_guard lock(mLock);
+ return *mRefreshRates.at(modeId);
};
- // Stores the current configId the device operates at
- void setCurrentConfigId(HwcConfigIndexType configId) EXCLUDES(mLock);
+ // Stores the current modeId the device operates at
+ void setCurrentModeId(DisplayModeId) EXCLUDES(mLock);
// Returns a string that represents the layer vote type
static std::string layerVoteTypeString(LayerVoteType vote);
// Returns a known frame rate that is the closest to frameRate
- float findClosestKnownFrameRate(float frameRate) const;
+ Fps findClosestKnownFrameRate(Fps frameRate) const;
- RefreshRateConfigs(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
- HwcConfigIndexType currentConfigId);
+ RefreshRateConfigs(const DisplayModes& modes, DisplayModeId currentModeId,
+ bool enableFrameRateOverride = false);
+
+ void updateDisplayModes(const DisplayModes& mode, DisplayModeId currentModeId) EXCLUDES(mLock);
+
+ // Returns whether switching modes (refresh rate or resolution) is possible.
+ // TODO(b/158780872): Consider HAL support, and skip frame rate detection if the modes only
+ // differ in resolution.
+ bool canSwitch() const EXCLUDES(mLock) {
+ std::lock_guard lock(mLock);
+ return mRefreshRates.size() > 1;
+ }
// Class to enumerate options around toggling the kernel timer on and off. We have an option
// for no change to avoid extra calls to kernel.
@@ -291,16 +322,33 @@
// refresh rates.
KernelIdleTimerAction getIdleTimerAction() const;
+ bool supportsFrameRateOverride() const { return mSupportsFrameRateOverride; }
+
+ // Return the display refresh rate divider to match the layer
+ // frame rate, or 0 if the display refresh rate is not a multiple of the
+ // layer refresh rate.
+ static int getFrameRateDivider(Fps displayFrameRate, Fps layerFrameRate);
+
+ using UidToFrameRateOverride = std::map<uid_t, Fps>;
+ // Returns the frame rate override for each uid.
+ //
+ // @param layers list of visible layers
+ // @param displayFrameRate the display frame rate
+ // @param touch whether touch timer is active (i.e. user touched the screen recently)
+ UidToFrameRateOverride getFrameRateOverrides(const std::vector<LayerRequirement>& layers,
+ Fps displayFrameRate, bool touch) const
+ EXCLUDES(mLock);
+
+ void dump(std::string& result) const EXCLUDES(mLock);
+
private:
friend class RefreshRateConfigsTest;
void constructAvailableRefreshRates() REQUIRES(mLock);
- static std::vector<float> constructKnownFrameRates(
- const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs);
- void getSortedRefreshRateList(
+ void getSortedRefreshRateListLocked(
const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
- std::vector<const RefreshRate*>* outRefreshRates);
+ std::vector<const RefreshRate*>* outRefreshRates) REQUIRES(mLock);
// Returns the refresh rate with the highest score in the collection specified from begin
// to end. If there are more than one with the same highest refresh rate, the first one is
@@ -325,11 +373,16 @@
const RefreshRate& getCurrentRefreshRateByPolicyLocked() const REQUIRES(mLock);
const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
- bool isPolicyValid(const Policy& policy);
+ bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock);
- // The list of refresh rates, indexed by display config ID. This must not change after this
+ // calculates a score for a layer. Used to determine the display refresh rate
+ // and the frame rate override for certains applications.
+ float calculateLayerScoreLocked(const LayerRequirement&, const RefreshRate&,
+ bool isSeamlessSwitch) const REQUIRES(mLock);
+
+ // The list of refresh rates, indexed by display modes ID. This may change after this
// object is initialized.
- AllRefreshRatesMapType mRefreshRates;
+ AllRefreshRatesMapType mRefreshRates GUARDED_BY(mLock);
// The list of refresh rates in the primary range of the current policy, ordered by vsyncPeriod
// (the first element is the lowest refresh rate).
@@ -339,7 +392,7 @@
// vsyncPeriod (the first element is the lowest refresh rate).
std::vector<const RefreshRate*> mAppRequestRefreshRates GUARDED_BY(mLock);
- // The current config. This will change at runtime. This is set by SurfaceFlinger on
+ // The current display mode. This will change at runtime. This is set by SurfaceFlinger on
// the main thread, and read by the Scheduler (and other objects) on other threads.
const RefreshRate* mCurrentRefreshRate GUARDED_BY(mLock);
@@ -349,15 +402,18 @@
std::optional<Policy> mOverridePolicy GUARDED_BY(mLock);
// The min and max refresh rates supported by the device.
- // This will not change at runtime.
- const RefreshRate* mMinSupportedRefreshRate;
- const RefreshRate* mMaxSupportedRefreshRate;
+ // This may change at runtime.
+ const RefreshRate* mMinSupportedRefreshRate GUARDED_BY(mLock);
+ const RefreshRate* mMaxSupportedRefreshRate GUARDED_BY(mLock);
mutable std::mutex mLock;
// A sorted list of known frame rates that a Heuristic layer will choose
// from based on the closest value.
- const std::vector<float> mKnownFrameRates;
+ const std::vector<Fps> mKnownFrameRates;
+
+ const bool mEnableFrameRateOverride;
+ bool mSupportsFrameRateOverride;
};
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index d9e7b37..80f4665 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -18,7 +18,7 @@
#include <numeric>
-#include "Scheduler/RefreshRateConfigs.h"
+#include "Fps.h"
#include "Scheduler/SchedulerUtils.h"
#include "TimeStats/TimeStats.h"
@@ -40,12 +40,10 @@
static constexpr int64_t MS_PER_DAY = 24 * MS_PER_HOUR;
public:
- RefreshRateStats(const RefreshRateConfigs& refreshRateConfigs, TimeStats& timeStats,
- HwcConfigIndexType currentConfigId,
+ RefreshRateStats(TimeStats& timeStats, Fps currentRefreshRate,
android::hardware::graphics::composer::hal::PowerMode currentPowerMode)
- : mRefreshRateConfigs(refreshRateConfigs),
- mTimeStats(timeStats),
- mCurrentConfigMode(currentConfigId),
+ : mTimeStats(timeStats),
+ mCurrentRefreshRate(currentRefreshRate),
mCurrentPowerMode(currentPowerMode) {}
// Sets power mode.
@@ -59,12 +57,12 @@
// Sets config mode. If the mode has changed, it records how much time was spent in the previous
// mode.
- void setConfigMode(HwcConfigIndexType configId) {
- if (mCurrentConfigMode == configId) {
+ void setRefreshRate(Fps currRefreshRate) {
+ if (mCurrentRefreshRate.equalsWithMargin(currRefreshRate)) {
return;
}
flushTime();
- mCurrentConfigMode = configId;
+ mCurrentRefreshRate = currRefreshRate;
}
// Returns a map between human readable refresh rate and number of seconds the device spent in
@@ -79,10 +77,10 @@
// Multiple configs may map to the same name, e.g. "60fps". Add the
// times for such configs together.
for (const auto& [configId, time] : mConfigModesTotalTime) {
- totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(configId).getName()] = 0;
+ totalTime[to_string(configId)] = 0;
}
for (const auto& [configId, time] : mConfigModesTotalTime) {
- totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(configId).getName()] += time;
+ totalTime[to_string(configId)] += time;
}
totalTime["ScreenOff"] = mScreenOffTime;
return totalTime;
@@ -111,12 +109,11 @@
uint32_t fps = 0;
if (mCurrentPowerMode == android::hardware::graphics::composer::hal::PowerMode::ON) {
// Normal power mode is counted under different config modes.
- if (mConfigModesTotalTime.find(mCurrentConfigMode) == mConfigModesTotalTime.end()) {
- mConfigModesTotalTime[mCurrentConfigMode] = 0;
+ if (mConfigModesTotalTime.find(mCurrentRefreshRate) == mConfigModesTotalTime.end()) {
+ mConfigModesTotalTime[mCurrentRefreshRate] = 0;
}
- mConfigModesTotalTime[mCurrentConfigMode] += timeElapsedMs;
- fps = static_cast<uint32_t>(std::round(
- mRefreshRateConfigs.getRefreshRateFromConfigId(mCurrentConfigMode).getFps()));
+ mConfigModesTotalTime[mCurrentRefreshRate] += timeElapsedMs;
+ fps = static_cast<uint32_t>(mCurrentRefreshRate.getIntValue());
} else {
mScreenOffTime += timeElapsedMs;
}
@@ -134,16 +131,13 @@
days, hours, mins, sec, secRemainderMs);
}
- // Keeps information about refresh rate configs that device has.
- const RefreshRateConfigs& mRefreshRateConfigs;
-
// Aggregate refresh rate statistics for telemetry.
TimeStats& mTimeStats;
- HwcConfigIndexType mCurrentConfigMode;
+ Fps mCurrentRefreshRate;
android::hardware::graphics::composer::hal::PowerMode mCurrentPowerMode;
- std::unordered_map<HwcConfigIndexType /* configId */, int64_t /* duration in ms */>
+ std::unordered_map<Fps, int64_t /* duration in ms */, std::hash<Fps>, Fps::EqualsInBuckets>
mConfigModesTotalTime;
int64_t mScreenOffTime = 0;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 5c0ba01..fac2c65 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -20,17 +20,18 @@
#include "Scheduler.h"
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <configstore/Utils.h>
-#include <cutils/properties.h>
#include <input/InputWindow.h>
#include <system/window.h>
#include <ui/DisplayStatInfo.h>
#include <utils/Timers.h>
#include <utils/Trace.h>
+#include <FrameTimeline/FrameTimeline.h>
#include <algorithm>
#include <cinttypes>
#include <cstdint>
@@ -39,9 +40,7 @@
#include <numeric>
#include "../Layer.h"
-#include "DispSync.h"
#include "DispSyncSource.h"
-#include "EventControlThread.h"
#include "EventThread.h"
#include "InjectVSyncSource.h"
#include "OneShotTimer.h"
@@ -51,6 +50,7 @@
#include "VSyncDispatchTimerQueue.h"
#include "VSyncPredictor.h"
#include "VSyncReactor.h"
+#include "VsyncController.h"
#define RETURN_IF_INVALID_HANDLE(handle, ...) \
do { \
@@ -60,69 +60,80 @@
} \
} while (false)
+using namespace std::string_literals;
+
namespace android {
-std::unique_ptr<DispSync> createDispSync(bool supportKernelTimer) {
- // TODO (140302863) remove this and use the vsync_reactor system.
- if (property_get_bool("debug.sf.vsync_reactor", true)) {
- // TODO (144707443) tune Predictor tunables.
- static constexpr int defaultRate = 60;
- static constexpr auto initialPeriod =
- std::chrono::duration<nsecs_t, std::ratio<1, defaultRate>>(1);
- static constexpr size_t vsyncTimestampHistorySize = 20;
- static constexpr size_t minimumSamplesForPrediction = 6;
- static constexpr uint32_t discardOutlierPercent = 20;
- auto tracker = std::make_unique<
- scheduler::VSyncPredictor>(std::chrono::duration_cast<std::chrono::nanoseconds>(
- initialPeriod)
- .count(),
- vsyncTimestampHistorySize, minimumSamplesForPrediction,
- discardOutlierPercent);
+namespace {
- static constexpr auto vsyncMoveThreshold =
- std::chrono::duration_cast<std::chrono::nanoseconds>(3ms);
- static constexpr auto timerSlack =
- std::chrono::duration_cast<std::chrono::nanoseconds>(500us);
- auto dispatch = std::make_unique<
- scheduler::VSyncDispatchTimerQueue>(std::make_unique<scheduler::Timer>(), *tracker,
- timerSlack.count(), vsyncMoveThreshold.count());
-
- static constexpr size_t pendingFenceLimit = 20;
- return std::make_unique<scheduler::VSyncReactor>(std::make_unique<scheduler::SystemClock>(),
- std::move(dispatch), std::move(tracker),
- pendingFenceLimit, supportKernelTimer);
- } else {
- return std::make_unique<impl::DispSync>("SchedulerDispSync",
- sysprop::running_without_sync_framework(true));
- }
+std::unique_ptr<scheduler::VSyncTracker> createVSyncTracker() {
+ // TODO(b/144707443): Tune constants.
+ constexpr int kDefaultRate = 60;
+ constexpr auto initialPeriod = std::chrono::duration<nsecs_t, std::ratio<1, kDefaultRate>>(1);
+ constexpr nsecs_t idealPeriod =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(initialPeriod).count();
+ constexpr size_t vsyncTimestampHistorySize = 20;
+ constexpr size_t minimumSamplesForPrediction = 6;
+ constexpr uint32_t discardOutlierPercent = 20;
+ return std::make_unique<scheduler::VSyncPredictor>(idealPeriod, vsyncTimestampHistorySize,
+ minimumSamplesForPrediction,
+ discardOutlierPercent);
}
-Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,
- const scheduler::RefreshRateConfigs& refreshRateConfig,
- ISchedulerCallback& schedulerCallback, bool useContentDetectionV2,
- bool useContentDetection)
- : mSupportKernelTimer(sysprop::support_kernel_idle_timer(false)),
- mPrimaryDispSync(createDispSync(mSupportKernelTimer)),
- mEventControlThread(new impl::EventControlThread(std::move(function))),
- mSchedulerCallback(schedulerCallback),
- mRefreshRateConfigs(refreshRateConfig),
- mUseContentDetection(useContentDetection),
- mUseContentDetectionV2(useContentDetectionV2) {
- using namespace sysprop;
+std::unique_ptr<scheduler::VSyncDispatch> createVSyncDispatch(scheduler::VSyncTracker& tracker) {
+ // TODO(b/144707443): Tune constants.
+ constexpr std::chrono::nanoseconds vsyncMoveThreshold = 3ms;
+ constexpr std::chrono::nanoseconds timerSlack = 500us;
+ return std::make_unique<
+ scheduler::VSyncDispatchTimerQueue>(std::make_unique<scheduler::Timer>(), tracker,
+ timerSlack.count(), vsyncMoveThreshold.count());
+}
- if (mUseContentDetectionV2) {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>(refreshRateConfig);
- } else {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
+const char* toContentDetectionString(bool useContentDetection) {
+ return useContentDetection ? "on" : "off";
+}
+
+} // namespace
+
+class PredictedVsyncTracer {
+public:
+ PredictedVsyncTracer(scheduler::VSyncDispatch& dispatch)
+ : mRegistration(dispatch, std::bind(&PredictedVsyncTracer::callback, this),
+ "PredictedVsyncTracer") {
+ scheduleRegistration();
}
- const int setIdleTimerMs = property_get_int32("debug.sf.set_idle_timer_ms", 0);
+private:
+ TracedOrdinal<bool> mParity = {"VSYNC-predicted", 0};
+ scheduler::VSyncCallbackRegistration mRegistration;
+
+ void scheduleRegistration() { mRegistration.schedule({0, 0, 0}); }
+
+ void callback() {
+ mParity = !mParity;
+ scheduleRegistration();
+ }
+};
+
+Scheduler::Scheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback)
+ : Scheduler(configs, callback,
+ {.supportKernelTimer = sysprop::support_kernel_idle_timer(false),
+ .useContentDetection = sysprop::use_content_detection_for_refresh_rate(false)}) {
+}
+
+Scheduler::Scheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback,
+ Options options)
+ : Scheduler(createVsyncSchedule(options.supportKernelTimer), configs, callback,
+ createLayerHistory(configs), options) {
+ using namespace sysprop;
+
+ const int setIdleTimerMs = base::GetIntProperty("debug.sf.set_idle_timer_ms"s, 0);
if (const auto millis = setIdleTimerMs ? setIdleTimerMs : set_idle_timer_ms(0); millis > 0) {
- const auto callback = mSupportKernelTimer ? &Scheduler::kernelIdleTimerCallback
- : &Scheduler::idleTimerCallback;
+ const auto callback = mOptions.supportKernelTimer ? &Scheduler::kernelIdleTimerCallback
+ : &Scheduler::idleTimerCallback;
mIdleTimer.emplace(
- std::chrono::milliseconds(millis),
+ "IdleTimer", std::chrono::milliseconds(millis),
[this, callback] { std::invoke(callback, this, TimerState::Reset); },
[this, callback] { std::invoke(callback, this, TimerState::Expired); });
mIdleTimer->start();
@@ -131,7 +142,7 @@
if (const int64_t millis = set_touch_timer_ms(0); millis > 0) {
// Touch events are coming to SF every 100ms, so the timer needs to be higher than that
mTouchTimer.emplace(
- std::chrono::milliseconds(millis),
+ "TouchTimer", std::chrono::milliseconds(millis),
[this] { touchTimerCallback(TimerState::Reset); },
[this] { touchTimerCallback(TimerState::Expired); });
mTouchTimer->start();
@@ -139,25 +150,27 @@
if (const int64_t millis = set_display_power_timer_ms(0); millis > 0) {
mDisplayPowerTimer.emplace(
- std::chrono::milliseconds(millis),
+ "DisplayPowerTimer", std::chrono::milliseconds(millis),
[this] { displayPowerTimerCallback(TimerState::Reset); },
[this] { displayPowerTimerCallback(TimerState::Expired); });
mDisplayPowerTimer->start();
}
}
-Scheduler::Scheduler(std::unique_ptr<DispSync> primaryDispSync,
- std::unique_ptr<EventControlThread> eventControlThread,
- const scheduler::RefreshRateConfigs& configs,
- ISchedulerCallback& schedulerCallback, bool useContentDetectionV2,
- bool useContentDetection)
- : mSupportKernelTimer(false),
- mPrimaryDispSync(std::move(primaryDispSync)),
- mEventControlThread(std::move(eventControlThread)),
+Scheduler::Scheduler(VsyncSchedule schedule, const scheduler::RefreshRateConfigs& configs,
+ ISchedulerCallback& schedulerCallback,
+ std::unique_ptr<LayerHistory> layerHistory, Options options)
+ : mOptions(options),
+ mVsyncSchedule(std::move(schedule)),
+ mLayerHistory(std::move(layerHistory)),
mSchedulerCallback(schedulerCallback),
mRefreshRateConfigs(configs),
- mUseContentDetection(useContentDetection),
- mUseContentDetectionV2(useContentDetectionV2) {}
+ mPredictedVsyncTracer(
+ base::GetBoolProperty("debug.sf.show_predicted_vsync", false)
+ ? std::make_unique<PredictedVsyncTracer>(*mVsyncSchedule.dispatch)
+ : nullptr) {
+ mSchedulerCallback.setVsyncEnabled(false);
+}
Scheduler::~Scheduler() {
// Ensure the OneShotTimer threads are joined before we start destroying state.
@@ -166,22 +179,103 @@
mIdleTimer.reset();
}
-DispSync& Scheduler::getPrimaryDispSync() {
- return *mPrimaryDispSync;
+Scheduler::VsyncSchedule Scheduler::createVsyncSchedule(bool supportKernelTimer) {
+ auto clock = std::make_unique<scheduler::SystemClock>();
+ auto tracker = createVSyncTracker();
+ auto dispatch = createVSyncDispatch(*tracker);
+
+ // TODO(b/144707443): Tune constants.
+ constexpr size_t pendingFenceLimit = 20;
+ auto controller =
+ std::make_unique<scheduler::VSyncReactor>(std::move(clock), *tracker, pendingFenceLimit,
+ supportKernelTimer);
+ return {std::move(controller), std::move(tracker), std::move(dispatch)};
}
-std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource(const char* name,
- nsecs_t phaseOffsetNs) {
- return std::make_unique<DispSyncSource>(mPrimaryDispSync.get(), phaseOffsetNs,
- true /* traceVsync */, name);
+std::unique_ptr<LayerHistory> Scheduler::createLayerHistory(
+ const scheduler::RefreshRateConfigs& configs) {
+ if (!configs.canSwitch()) return nullptr;
+
+ return std::make_unique<scheduler::LayerHistory>(configs);
+}
+
+std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource(
+ const char* name, std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration, bool traceVsync) {
+ return std::make_unique<scheduler::DispSyncSource>(*mVsyncSchedule.dispatch, workDuration,
+ readyDuration, traceVsync, name);
+}
+
+std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const {
+ std::lock_guard lock(mFrameRateOverridesMutex);
+ {
+ const auto iter = mFrameRateOverridesFromBackdoor.find(uid);
+ if (iter != mFrameRateOverridesFromBackdoor.end()) {
+ return std::make_optional<Fps>(iter->second);
+ }
+ }
+
+ {
+ const auto iter = mFrameRateOverridesByContent.find(uid);
+ if (iter != mFrameRateOverridesByContent.end()) {
+ return std::make_optional<Fps>(iter->second);
+ }
+ }
+
+ return std::nullopt;
+}
+
+bool Scheduler::isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const {
+ if (!mRefreshRateConfigs.supportsFrameRateOverride()) {
+ return true;
+ }
+
+ const auto frameRate = getFrameRateOverride(uid);
+ if (!frameRate.has_value()) {
+ return true;
+ }
+
+ return mVsyncSchedule.tracker->isVSyncInPhase(expectedVsyncTimestamp, *frameRate);
+}
+
+impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const {
+ if (!mRefreshRateConfigs.supportsFrameRateOverride()) {
+ return {};
+ }
+
+ return [this](nsecs_t expectedVsyncTimestamp, uid_t uid) {
+ return !isVsyncValid(expectedVsyncTimestamp, uid);
+ };
+}
+
+impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction() const {
+ return [this](uid_t uid) {
+ nsecs_t basePeriod = mRefreshRateConfigs.getCurrentRefreshRate().getVsyncPeriod();
+ const auto frameRate = getFrameRateOverride(uid);
+ if (!frameRate.has_value()) {
+ return basePeriod;
+ }
+
+ const auto divider = scheduler::RefreshRateConfigs::getFrameRateDivider(
+ mRefreshRateConfigs.getCurrentRefreshRate().getFps(), *frameRate);
+ if (divider <= 1) {
+ return basePeriod;
+ }
+ return basePeriod * divider;
+ };
}
Scheduler::ConnectionHandle Scheduler::createConnection(
- const char* connectionName, nsecs_t phaseOffsetNs,
+ const char* connectionName, frametimeline::TokenManager* tokenManager,
+ std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration,
impl::EventThread::InterceptVSyncsCallback interceptCallback) {
- auto vsyncSource = makePrimaryDispSyncSource(connectionName, phaseOffsetNs);
- auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource),
- std::move(interceptCallback));
+ auto vsyncSource = makePrimaryDispSyncSource(connectionName, workDuration, readyDuration);
+ auto throttleVsync = makeThrottleVsyncCallback();
+ auto getVsyncPeriod = makeGetVsyncPeriodFunction();
+ auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource), tokenManager,
+ std::move(interceptCallback),
+ std::move(throttleVsync),
+ std::move(getVsyncPeriod));
return createConnection(std::move(eventThread));
}
@@ -189,97 +283,169 @@
const ConnectionHandle handle = ConnectionHandle{mNextConnectionHandleId++};
ALOGV("Creating a connection handle with ID %" PRIuPTR, handle.id);
- auto connection =
- createConnectionInternal(eventThread.get(), ISurfaceComposer::eConfigChangedSuppress);
+ auto connection = createConnectionInternal(eventThread.get());
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
mConnections.emplace(handle, Connection{connection, std::move(eventThread)});
return handle;
}
sp<EventThreadConnection> Scheduler::createConnectionInternal(
- EventThread* eventThread, ISurfaceComposer::ConfigChanged configChanged) {
- return eventThread->createEventConnection([&] { resync(); }, configChanged);
+ EventThread* eventThread, ISurfaceComposer::EventRegistrationFlags eventRegistration) {
+ return eventThread->createEventConnection([&] { resync(); }, eventRegistration);
}
sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
- ConnectionHandle handle, ISurfaceComposer::ConfigChanged configChanged) {
+ ConnectionHandle handle, ISurfaceComposer::EventRegistrationFlags eventRegistration) {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
RETURN_IF_INVALID_HANDLE(handle, nullptr);
- return createConnectionInternal(mConnections[handle].thread.get(), configChanged);
+ return createConnectionInternal(mConnections[handle].thread.get(), eventRegistration);
}
sp<EventThreadConnection> Scheduler::getEventConnection(ConnectionHandle handle) {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
RETURN_IF_INVALID_HANDLE(handle, nullptr);
return mConnections[handle].connection;
}
void Scheduler::onHotplugReceived(ConnectionHandle handle, PhysicalDisplayId displayId,
bool connected) {
- RETURN_IF_INVALID_HANDLE(handle);
- mConnections[handle].thread->onHotplugReceived(displayId, connected);
+ android::EventThread* thread;
+ {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
+ RETURN_IF_INVALID_HANDLE(handle);
+ thread = mConnections[handle].thread.get();
+ }
+
+ thread->onHotplugReceived(displayId, connected);
}
void Scheduler::onScreenAcquired(ConnectionHandle handle) {
- RETURN_IF_INVALID_HANDLE(handle);
- mConnections[handle].thread->onScreenAcquired();
+ android::EventThread* thread;
+ {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
+ RETURN_IF_INVALID_HANDLE(handle);
+ thread = mConnections[handle].thread.get();
+ }
+ thread->onScreenAcquired();
}
void Scheduler::onScreenReleased(ConnectionHandle handle) {
- RETURN_IF_INVALID_HANDLE(handle);
- mConnections[handle].thread->onScreenReleased();
+ android::EventThread* thread;
+ {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
+ RETURN_IF_INVALID_HANDLE(handle);
+ thread = mConnections[handle].thread.get();
+ }
+ thread->onScreenReleased();
}
-void Scheduler::onPrimaryDisplayConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
- HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
- std::lock_guard<std::mutex> lock(mFeatureStateLock);
- // Cache the last reported config for primary display.
- mFeatures.cachedConfigChangedParams = {handle, displayId, configId, vsyncPeriod};
- onNonPrimaryDisplayConfigChanged(handle, displayId, configId, vsyncPeriod);
+void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) {
+ std::vector<FrameRateOverride> overrides;
+ {
+ std::lock_guard lock(mFrameRateOverridesMutex);
+ for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) {
+ overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
+ }
+ for (const auto& [uid, frameRate] : mFrameRateOverridesByContent) {
+ if (mFrameRateOverridesFromBackdoor.count(uid) == 0) {
+ overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
+ }
+ }
+ }
+ android::EventThread* thread;
+ {
+ std::lock_guard lock(mConnectionsLock);
+ RETURN_IF_INVALID_HANDLE(handle);
+ thread = mConnections[handle].thread.get();
+ }
+ thread->onFrameRateOverridesChanged(displayId, std::move(overrides));
}
-void Scheduler::dispatchCachedReportedConfig() {
- const auto configId = *mFeatures.configId;
- const auto vsyncPeriod =
- mRefreshRateConfigs.getRefreshRateFromConfigId(configId).getVsyncPeriod();
+void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
+ DisplayModeId modeId, nsecs_t vsyncPeriod) {
+ {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ // Cache the last reported modes for primary display.
+ mFeatures.cachedModeChangedParams = {handle, displayId, modeId, vsyncPeriod};
- // If there is no change from cached config, there is no need to dispatch an event
- if (configId == mFeatures.cachedConfigChangedParams->configId &&
- vsyncPeriod == mFeatures.cachedConfigChangedParams->vsyncPeriod) {
+ // Invalidate content based refresh rate selection so it could be calculated
+ // again for the new refresh rate.
+ mFeatures.contentRequirements.clear();
+ }
+ onNonPrimaryDisplayModeChanged(handle, displayId, modeId, vsyncPeriod);
+}
+
+void Scheduler::dispatchCachedReportedMode() {
+ // Check optional fields first.
+ if (!mFeatures.modeId.has_value()) {
+ ALOGW("No mode ID found, not dispatching cached mode.");
+ return;
+ }
+ if (!mFeatures.cachedModeChangedParams.has_value()) {
+ ALOGW("No mode changed params found, not dispatching cached mode.");
return;
}
- mFeatures.cachedConfigChangedParams->configId = configId;
- mFeatures.cachedConfigChangedParams->vsyncPeriod = vsyncPeriod;
- onNonPrimaryDisplayConfigChanged(mFeatures.cachedConfigChangedParams->handle,
- mFeatures.cachedConfigChangedParams->displayId,
- mFeatures.cachedConfigChangedParams->configId,
- mFeatures.cachedConfigChangedParams->vsyncPeriod);
+ const auto modeId = *mFeatures.modeId;
+ const auto vsyncPeriod = mRefreshRateConfigs.getRefreshRateFromModeId(modeId).getVsyncPeriod();
+
+ // If there is no change from cached mode, there is no need to dispatch an event
+ if (modeId == mFeatures.cachedModeChangedParams->modeId &&
+ vsyncPeriod == mFeatures.cachedModeChangedParams->vsyncPeriod) {
+ return;
+ }
+
+ mFeatures.cachedModeChangedParams->modeId = modeId;
+ mFeatures.cachedModeChangedParams->vsyncPeriod = vsyncPeriod;
+ onNonPrimaryDisplayModeChanged(mFeatures.cachedModeChangedParams->handle,
+ mFeatures.cachedModeChangedParams->displayId,
+ mFeatures.cachedModeChangedParams->modeId,
+ mFeatures.cachedModeChangedParams->vsyncPeriod);
}
-void Scheduler::onNonPrimaryDisplayConfigChanged(ConnectionHandle handle,
- PhysicalDisplayId displayId,
- HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
- RETURN_IF_INVALID_HANDLE(handle);
- mConnections[handle].thread->onConfigChanged(displayId, configId, vsyncPeriod);
+void Scheduler::onNonPrimaryDisplayModeChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
+ DisplayModeId modeId, nsecs_t vsyncPeriod) {
+ android::EventThread* thread;
+ {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
+ RETURN_IF_INVALID_HANDLE(handle);
+ thread = mConnections[handle].thread.get();
+ }
+ thread->onModeChanged(displayId, modeId, vsyncPeriod);
}
size_t Scheduler::getEventThreadConnectionCount(ConnectionHandle handle) {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
RETURN_IF_INVALID_HANDLE(handle, 0);
return mConnections[handle].thread->getEventThreadConnectionCount();
}
void Scheduler::dump(ConnectionHandle handle, std::string& result) const {
- RETURN_IF_INVALID_HANDLE(handle);
- mConnections.at(handle).thread->dump(result);
+ android::EventThread* thread;
+ {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
+ RETURN_IF_INVALID_HANDLE(handle);
+ thread = mConnections.at(handle).thread.get();
+ }
+ thread->dump(result);
}
-void Scheduler::setPhaseOffset(ConnectionHandle handle, nsecs_t phaseOffset) {
- RETURN_IF_INVALID_HANDLE(handle);
- mConnections[handle].thread->setPhaseOffset(phaseOffset);
+void Scheduler::setDuration(ConnectionHandle handle, std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration) {
+ android::EventThread* thread;
+ {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
+ RETURN_IF_INVALID_HANDLE(handle);
+ thread = mConnections[handle].thread.get();
+ }
+ thread->setDuration(workDuration, readyDuration);
}
-void Scheduler::getDisplayStatInfo(DisplayStatInfo* stats) {
- stats->vsyncTime = mPrimaryDispSync->computeNextRefresh(0, systemTime());
- stats->vsyncPeriod = mPrimaryDispSync->getPeriod();
+DisplayStatInfo Scheduler::getDisplayStatInfo(nsecs_t now) {
+ const auto vsyncTime = mVsyncSchedule.tracker->nextAnticipatedVSyncTimeFrom(now);
+ const auto vsyncPeriod = mVsyncSchedule.tracker->currentPeriod();
+ return DisplayStatInfo{.vsyncTime = vsyncTime, .vsyncPeriod = vsyncPeriod};
}
Scheduler::ConnectionHandle Scheduler::enableVSyncInjection(bool enable) {
@@ -295,7 +461,14 @@
auto eventThread =
std::make_unique<impl::EventThread>(std::move(vsyncSource),
- impl::EventThread::InterceptVSyncsCallback());
+ /*tokenManager=*/nullptr,
+ impl::EventThread::InterceptVSyncsCallback(),
+ impl::EventThread::ThrottleVsyncCallback(),
+ impl::EventThread::GetVsyncPeriodFunction());
+
+ // EventThread does not dispatch VSYNC unless the display is connected and powered on.
+ eventThread->onHotplugReceived(PhysicalDisplayId::fromPort(0), true);
+ eventThread->onScreenAcquired();
mInjectorConnectionHandle = createConnection(std::move(eventThread));
}
@@ -304,20 +477,20 @@
return mInjectorConnectionHandle;
}
-bool Scheduler::injectVSync(nsecs_t when, nsecs_t expectedVSyncTime) {
+bool Scheduler::injectVSync(nsecs_t when, nsecs_t expectedVSyncTime, nsecs_t deadlineTimestamp) {
if (!mInjectVSyncs || !mVSyncInjector) {
return false;
}
- mVSyncInjector->onInjectSyncEvent(when, expectedVSyncTime);
+ mVSyncInjector->onInjectSyncEvent(when, expectedVSyncTime, deadlineTimestamp);
return true;
}
void Scheduler::enableHardwareVsync() {
std::lock_guard<std::mutex> lock(mHWVsyncLock);
if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
- mPrimaryDispSync->beginResync();
- mEventControlThread->setVsyncEnabled(true);
+ mVsyncSchedule.tracker->resetModel();
+ mSchedulerCallback.setVsyncEnabled(true);
mPrimaryHWVsyncEnabled = true;
}
}
@@ -325,8 +498,7 @@
void Scheduler::disableHardwareVsync(bool makeUnavailable) {
std::lock_guard<std::mutex> lock(mHWVsyncLock);
if (mPrimaryHWVsyncEnabled) {
- mEventControlThread->setVsyncEnabled(false);
- mPrimaryDispSync->endResync();
+ mSchedulerCallback.setVsyncEnabled(false);
mPrimaryHWVsyncEnabled = false;
}
if (makeUnavailable) {
@@ -366,11 +538,11 @@
void Scheduler::setVsyncPeriod(nsecs_t period) {
std::lock_guard<std::mutex> lock(mHWVsyncLock);
- mPrimaryDispSync->setPeriod(period);
+ mVsyncSchedule.controller->startPeriodTransition(period);
if (!mPrimaryHWVsyncEnabled) {
- mPrimaryDispSync->beginResync();
- mEventControlThread->setVsyncEnabled(true);
+ mVsyncSchedule.tracker->resetModel();
+ mSchedulerCallback.setVsyncEnabled(true);
mPrimaryHWVsyncEnabled = true;
}
}
@@ -382,8 +554,8 @@
{ // Scope for the lock
std::lock_guard<std::mutex> lock(mHWVsyncLock);
if (mPrimaryHWVsyncEnabled) {
- needsHwVsync =
- mPrimaryDispSync->addResyncSample(timestamp, hwcVsyncPeriod, periodFlushed);
+ needsHwVsync = mVsyncSchedule.controller->addHwVsyncTimestamp(timestamp, hwcVsyncPeriod,
+ periodFlushed);
}
}
@@ -395,7 +567,7 @@
}
void Scheduler::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) {
- if (mPrimaryDispSync->addPresentFence(fenceTime)) {
+ if (mVsyncSchedule.controller->addPresentFence(fenceTime)) {
enableHardwareVsync();
} else {
disableHardwareVsync(false);
@@ -403,45 +575,33 @@
}
void Scheduler::setIgnorePresentFences(bool ignore) {
- mPrimaryDispSync->setIgnorePresentFences(ignore);
-}
-
-nsecs_t Scheduler::getDispSyncExpectedPresentTime(nsecs_t now) {
- return mPrimaryDispSync->expectedPresentTime(now);
+ mVsyncSchedule.controller->setIgnorePresentFences(ignore);
}
void Scheduler::registerLayer(Layer* layer) {
if (!mLayerHistory) return;
- const auto minFps = mRefreshRateConfigs.getMinRefreshRate().getFps();
- const auto maxFps = mRefreshRateConfigs.getMaxRefreshRate().getFps();
+ scheduler::LayerHistory::LayerVoteType voteType;
- if (layer->getWindowType() == InputWindowInfo::TYPE_STATUS_BAR) {
- mLayerHistory->registerLayer(layer, minFps, maxFps,
- scheduler::LayerHistory::LayerVoteType::NoVote);
- } else if (!mUseContentDetection) {
- // If the content detection feature is off, all layers are registered at Max. We still keep
- // the layer history, since we use it for other features (like Frame Rate API), so layers
- // still need to be registered.
- mLayerHistory->registerLayer(layer, minFps, maxFps,
- scheduler::LayerHistory::LayerVoteType::Max);
- } else if (!mUseContentDetectionV2) {
- // In V1 of content detection, all layers are registered as Heuristic (unless it's
- // wallpaper).
- const auto highFps =
- layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER ? minFps : maxFps;
-
- mLayerHistory->registerLayer(layer, minFps, highFps,
- scheduler::LayerHistory::LayerVoteType::Heuristic);
+ if (!mOptions.useContentDetection ||
+ layer->getWindowType() == InputWindowInfo::Type::STATUS_BAR) {
+ voteType = scheduler::LayerHistory::LayerVoteType::NoVote;
+ } else if (layer->getWindowType() == InputWindowInfo::Type::WALLPAPER) {
+ // Running Wallpaper at Min is considered as part of content detection.
+ voteType = scheduler::LayerHistory::LayerVoteType::Min;
} else {
- if (layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER) {
- // Running Wallpaper at Min is considered as part of content detection.
- mLayerHistory->registerLayer(layer, minFps, maxFps,
- scheduler::LayerHistory::LayerVoteType::Min);
- } else {
- mLayerHistory->registerLayer(layer, minFps, maxFps,
- scheduler::LayerHistory::LayerVoteType::Heuristic);
- }
+ voteType = scheduler::LayerHistory::LayerVoteType::Heuristic;
+ }
+
+ // If the content detection feature is off, we still keep the layer history,
+ // since we use it for other features (like Frame Rate API), so layers
+ // still need to be registered.
+ mLayerHistory->registerLayer(layer, voteType);
+}
+
+void Scheduler::deregisterLayer(Layer* layer) {
+ if (mLayerHistory) {
+ mLayerHistory->deregisterLayer(layer);
}
}
@@ -452,9 +612,9 @@
}
}
-void Scheduler::setConfigChangePending(bool pending) {
+void Scheduler::setModeChangePending(bool pending) {
if (mLayerHistory) {
- mLayerHistory->setConfigChangePending(pending);
+ mLayerHistory->setModeChangePending(pending);
}
}
@@ -464,31 +624,42 @@
ATRACE_CALL();
scheduler::LayerHistory::Summary summary = mLayerHistory->summarize(systemTime());
- HwcConfigIndexType newConfigId;
+ scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
+ DisplayModeId newModeId;
+ bool frameRateChanged;
+ bool frameRateOverridesChanged;
{
std::lock_guard<std::mutex> lock(mFeatureStateLock);
if (mFeatures.contentRequirements == summary) {
return;
}
mFeatures.contentRequirements = summary;
- mFeatures.contentDetectionV1 =
- !summary.empty() ? ContentDetectionState::On : ContentDetectionState::Off;
- scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
- newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
- if (mFeatures.configId == newConfigId) {
- // We don't need to change the config, but we might need to send an event
- // about a config change, since it was suppressed due to a previous idleConsidered
+ newModeId = calculateRefreshRateModeId(&consideredSignals);
+ auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromModeId(newModeId);
+ frameRateOverridesChanged =
+ updateFrameRateOverrides(consideredSignals, newRefreshRate.getFps());
+
+ if (mFeatures.modeId == newModeId) {
+ // We don't need to change the display mode, but we might need to send an event
+ // about a mode change, since it was suppressed due to a previous idleConsidered
if (!consideredSignals.idle) {
- dispatchCachedReportedConfig();
+ dispatchCachedReportedMode();
}
- return;
+ frameRateChanged = false;
+ } else {
+ mFeatures.modeId = newModeId;
+ frameRateChanged = true;
}
- mFeatures.configId = newConfigId;
- auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+ }
+ if (frameRateChanged) {
+ auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromModeId(newModeId);
mSchedulerCallback.changeRefreshRate(newRefreshRate,
- consideredSignals.idle ? ConfigEvent::None
- : ConfigEvent::Changed);
+ consideredSignals.idle ? ModeEvent::None
+ : ModeEvent::Changed);
+ }
+ if (frameRateOverridesChanged) {
+ mSchedulerCallback.triggerOnFrameRateOverridesChanged();
}
}
@@ -499,19 +670,10 @@
}
void Scheduler::notifyTouchEvent() {
- if (!mTouchTimer) return;
-
- // Touch event will boost the refresh rate to performance.
- // Clear Layer History to get fresh FPS detection.
- // NOTE: Instead of checking all the layers, we should be checking the layer
- // that is currently on top. b/142507166 will give us this capability.
- std::lock_guard<std::mutex> lock(mFeatureStateLock);
- if (mLayerHistory) {
- // Layer History will be cleared based on RefreshRateConfigs::getBestRefreshRate
-
+ if (mTouchTimer) {
mTouchTimer->reset();
- if (mSupportKernelTimer && mIdleTimer) {
+ if (mOptions.supportKernelTimer && mIdleTimer) {
mIdleTimer->reset();
}
}
@@ -540,17 +702,18 @@
// TODO(145561154): cleanup the kernel idle timer implementation and the refresh rate
// magic number
const auto& refreshRate = mRefreshRateConfigs.getCurrentRefreshRate();
- constexpr float FPS_THRESHOLD_FOR_KERNEL_TIMER = 65.0f;
- if (state == TimerState::Reset && refreshRate.getFps() > FPS_THRESHOLD_FOR_KERNEL_TIMER) {
+ constexpr Fps FPS_THRESHOLD_FOR_KERNEL_TIMER{65.0f};
+ if (state == TimerState::Reset &&
+ refreshRate.getFps().greaterThanWithMargin(FPS_THRESHOLD_FOR_KERNEL_TIMER)) {
// If we're not in performance mode then the kernel timer shouldn't do
// anything, as the refresh rate during DPU power collapse will be the
// same.
resyncToHardwareVsync(true /* makeAvailable */, refreshRate.getVsyncPeriod());
} else if (state == TimerState::Expired &&
- refreshRate.getFps() <= FPS_THRESHOLD_FOR_KERNEL_TIMER) {
+ refreshRate.getFps().lessThanOrEqualWithMargin(FPS_THRESHOLD_FOR_KERNEL_TIMER)) {
// Disable HW VSYNC if the timer expired, as we don't need it enabled if
// we're not pushing frames, and if we're in PERFORMANCE mode then we'll
- // need to update the DispSync model anyway.
+ // need to update the VsyncController model anyway.
disableHardwareVsync(false /* makeUnavailable */);
}
@@ -564,8 +727,14 @@
void Scheduler::touchTimerCallback(TimerState state) {
const TouchState touch = state == TimerState::Reset ? TouchState::Active : TouchState::Inactive;
+ // Touch event will boost the refresh rate to performance.
+ // Clear layer history to get fresh FPS detection.
+ // NOTE: Instead of checking all the layers, we should be checking the layer
+ // that is currently on top. b/142507166 will give us this capability.
if (handleTimerStateChanged(&mFeatures.touch, touch)) {
- mLayerHistory->clear();
+ if (mLayerHistory) {
+ mLayerHistory->clear();
+ }
}
ATRACE_INT("TouchState", static_cast<int>(touch));
}
@@ -577,19 +746,68 @@
void Scheduler::dump(std::string& result) const {
using base::StringAppendF;
- const char* const states[] = {"off", "on"};
- StringAppendF(&result, "+ Idle timer: %s\n",
- mIdleTimer ? mIdleTimer->dump().c_str() : states[0]);
+ StringAppendF(&result, "+ Idle timer: %s\n", mIdleTimer ? mIdleTimer->dump().c_str() : "off");
StringAppendF(&result, "+ Touch timer: %s\n",
- mTouchTimer ? mTouchTimer->dump().c_str() : states[0]);
- StringAppendF(&result, "+ Use content detection: %s\n\n",
- sysprop::use_content_detection_for_refresh_rate(false) ? "on" : "off");
+ mTouchTimer ? mTouchTimer->dump().c_str() : "off");
+ StringAppendF(&result, "+ Content detection: %s %s\n\n",
+ toContentDetectionString(mOptions.useContentDetection),
+ mLayerHistory ? mLayerHistory->dump().c_str() : "(no layer history)");
+
+ {
+ std::lock_guard lock(mFrameRateOverridesMutex);
+ StringAppendF(&result, "Frame Rate Overrides (backdoor): {");
+ for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) {
+ StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
+ }
+ StringAppendF(&result, "}\n");
+
+ StringAppendF(&result, "Frame Rate Overrides (setFrameRate): {");
+ for (const auto& [uid, frameRate] : mFrameRateOverridesByContent) {
+ StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
+ }
+ StringAppendF(&result, "}\n");
+ }
+}
+
+void Scheduler::dumpVsync(std::string& s) const {
+ using base::StringAppendF;
+
+ StringAppendF(&s, "VSyncReactor:\n");
+ mVsyncSchedule.controller->dump(s);
+ StringAppendF(&s, "VSyncDispatch:\n");
+ mVsyncSchedule.dispatch->dump(s);
+}
+
+bool Scheduler::updateFrameRateOverrides(
+ scheduler::RefreshRateConfigs::GlobalSignals consideredSignals, Fps displayRefreshRate) {
+ if (!mRefreshRateConfigs.supportsFrameRateOverride()) {
+ return false;
+ }
+
+ if (!consideredSignals.idle) {
+ const auto frameRateOverrides =
+ mRefreshRateConfigs.getFrameRateOverrides(mFeatures.contentRequirements,
+ displayRefreshRate,
+ consideredSignals.touch);
+ std::lock_guard lock(mFrameRateOverridesMutex);
+ if (!std::equal(mFrameRateOverridesByContent.begin(), mFrameRateOverridesByContent.end(),
+ frameRateOverrides.begin(), frameRateOverrides.end(),
+ [](const std::pair<uid_t, Fps>& a, const std::pair<uid_t, Fps>& b) {
+ return a.first == b.first && a.second.equalsWithMargin(b.second);
+ })) {
+ mFrameRateOverridesByContent = frameRateOverrides;
+ return true;
+ }
+ }
+ return false;
}
template <class T>
bool Scheduler::handleTimerStateChanged(T* currentState, T newState) {
- HwcConfigIndexType newConfigId;
+ DisplayModeId newModeId;
+ bool refreshRateChanged = false;
+ bool frameRateOverridesChanged;
scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
{
std::lock_guard<std::mutex> lock(mFeatureStateLock);
@@ -597,25 +815,35 @@
return false;
}
*currentState = newState;
- newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
- if (mFeatures.configId == newConfigId) {
- // We don't need to change the config, but we might need to send an event
- // about a config change, since it was suppressed due to a previous idleConsidered
+ newModeId = calculateRefreshRateModeId(&consideredSignals);
+ const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromModeId(newModeId);
+ frameRateOverridesChanged =
+ updateFrameRateOverrides(consideredSignals, newRefreshRate.getFps());
+ if (mFeatures.modeId == newModeId) {
+ // We don't need to change the display mode, but we might need to send an event
+ // about a mode change, since it was suppressed due to a previous idleConsidered
if (!consideredSignals.idle) {
- dispatchCachedReportedConfig();
+ dispatchCachedReportedMode();
}
- return consideredSignals.touch;
+ } else {
+ mFeatures.modeId = newModeId;
+ refreshRateChanged = true;
}
- mFeatures.configId = newConfigId;
}
- const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
- mSchedulerCallback.changeRefreshRate(newRefreshRate,
- consideredSignals.idle ? ConfigEvent::None
- : ConfigEvent::Changed);
+ if (refreshRateChanged) {
+ const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromModeId(newModeId);
+
+ mSchedulerCallback.changeRefreshRate(newRefreshRate,
+ consideredSignals.idle ? ModeEvent::None
+ : ModeEvent::Changed);
+ }
+ if (frameRateOverridesChanged) {
+ mSchedulerCallback.triggerOnFrameRateOverridesChanged();
+ }
return consideredSignals.touch;
}
-HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType(
+DisplayModeId Scheduler::calculateRefreshRateModeId(
scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals) {
ATRACE_CALL();
if (consideredSignals) *consideredSignals = {};
@@ -625,48 +853,25 @@
if (mDisplayPowerTimer &&
(!mFeatures.isDisplayPowerStateNormal ||
mFeatures.displayPowerTimer == TimerState::Reset)) {
- return mRefreshRateConfigs.getMaxRefreshRateByPolicy().getConfigId();
+ return mRefreshRateConfigs.getMaxRefreshRateByPolicy().getModeId();
}
const bool touchActive = mTouchTimer && mFeatures.touch == TouchState::Active;
const bool idle = mIdleTimer && mFeatures.idleTimer == TimerState::Expired;
- if (!mUseContentDetectionV2) {
- // As long as touch is active we want to be in performance mode.
- if (touchActive) {
- return mRefreshRateConfigs.getMaxRefreshRateByPolicy().getConfigId();
- }
-
- // If timer has expired as it means there is no new content on the screen.
- if (idle) {
- if (consideredSignals) consideredSignals->idle = true;
- return mRefreshRateConfigs.getMinRefreshRateByPolicy().getConfigId();
- }
-
- // If content detection is off we choose performance as we don't know the content fps.
- if (mFeatures.contentDetectionV1 == ContentDetectionState::Off) {
- // NOTE: V1 always calls this, but this is not a default behavior for V2.
- return mRefreshRateConfigs.getMaxRefreshRateByPolicy().getConfigId();
- }
-
- // Content detection is on, find the appropriate refresh rate with minimal error
- return mRefreshRateConfigs.getRefreshRateForContent(mFeatures.contentRequirements)
- .getConfigId();
- }
-
return mRefreshRateConfigs
.getBestRefreshRate(mFeatures.contentRequirements, {.touch = touchActive, .idle = idle},
consideredSignals)
- .getConfigId();
+ .getModeId();
}
-std::optional<HwcConfigIndexType> Scheduler::getPreferredConfigId() {
+std::optional<DisplayModeId> Scheduler::getPreferredModeId() {
std::lock_guard<std::mutex> lock(mFeatureStateLock);
- // Make sure that the default config ID is first updated, before returned.
- if (mFeatures.configId.has_value()) {
- mFeatures.configId = calculateRefreshRateConfigIndexType();
+ // Make sure that the default mode ID is first updated, before returned.
+ if (mFeatures.modeId.has_value()) {
+ mFeatures.modeId = calculateRefreshRateModeId();
}
- return mFeatures.configId;
+ return mFeatures.modeId;
}
void Scheduler::onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline) {
@@ -708,4 +913,24 @@
}
}
+void Scheduler::setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride) {
+ if (frameRateOverride.frameRateHz > 0.f && frameRateOverride.frameRateHz < 1.f) {
+ return;
+ }
+
+ std::lock_guard lock(mFrameRateOverridesMutex);
+ if (frameRateOverride.frameRateHz != 0.f) {
+ mFrameRateOverridesFromBackdoor[frameRateOverride.uid] = Fps(frameRateOverride.frameRateHz);
+ } else {
+ mFrameRateOverridesFromBackdoor.erase(frameRateOverride.uid);
+ }
+}
+
+std::chrono::steady_clock::time_point Scheduler::getPreviousVsyncFrom(
+ nsecs_t expectedPresentTime) const {
+ const auto presentTime = std::chrono::nanoseconds(expectedPresentTime);
+ const auto vsyncPeriod = std::chrono::nanoseconds(mVsyncSchedule.tracker->currentPeriod());
+ return std::chrono::steady_clock::time_point(presentTime - vsyncPeriod);
+}
+
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 730ea8f..49d3d93 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -26,10 +26,10 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
#include <ui/GraphicTypes.h>
-#pragma clang diagnostic pop
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
-#include "EventControlThread.h"
#include "EventThread.h"
#include "LayerHistory.h"
#include "OneShotTimer.h"
@@ -41,76 +41,72 @@
using namespace std::chrono_literals;
using scheduler::LayerHistory;
-class DispSync;
class FenceTime;
class InjectVSyncSource;
-struct DisplayStateInfo;
+class PredictedVsyncTracer;
-class ISchedulerCallback {
-public:
- virtual ~ISchedulerCallback() = default;
+namespace scheduler {
+class VsyncController;
+class VSyncDispatch;
+class VSyncTracker;
+} // namespace scheduler
+
+namespace frametimeline {
+class TokenManager;
+} // namespace frametimeline
+
+struct ISchedulerCallback {
+ virtual void setVsyncEnabled(bool) = 0;
virtual void changeRefreshRate(const scheduler::RefreshRateConfigs::RefreshRate&,
scheduler::RefreshRateConfigEvent) = 0;
virtual void repaintEverythingForHWC() = 0;
virtual void kernelTimerChanged(bool expired) = 0;
+ virtual void triggerOnFrameRateOverridesChanged() = 0;
+
+protected:
+ ~ISchedulerCallback() = default;
};
-class IPhaseOffsetControl {
-public:
- virtual ~IPhaseOffsetControl() = default;
- virtual void setPhaseOffset(scheduler::ConnectionHandle, nsecs_t phaseOffset) = 0;
-};
-
-class Scheduler : public IPhaseOffsetControl {
+class Scheduler {
public:
using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
- using ConfigEvent = scheduler::RefreshRateConfigEvent;
+ using ModeEvent = scheduler::RefreshRateConfigEvent;
- // Indicates whether to start the transaction early, or at vsync time.
- enum class TransactionStart {
- Early, // DEPRECATED. Start the transaction early. Times out on its own
- EarlyStart, // Start the transaction early and keep this config until EarlyEnd
- EarlyEnd, // End the early config started at EarlyStart
- Normal // Start the transaction at the normal time
- };
-
- Scheduler(impl::EventControlThread::SetVSyncEnabledFunction,
- const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback,
- bool useContentDetectionV2, bool useContentDetection);
-
- virtual ~Scheduler();
-
- DispSync& getPrimaryDispSync();
+ Scheduler(const scheduler::RefreshRateConfigs&, ISchedulerCallback&);
+ ~Scheduler();
using ConnectionHandle = scheduler::ConnectionHandle;
- ConnectionHandle createConnection(const char* connectionName, nsecs_t phaseOffsetNs,
+ ConnectionHandle createConnection(const char* connectionName, frametimeline::TokenManager*,
+ std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration,
impl::EventThread::InterceptVSyncsCallback);
- sp<IDisplayEventConnection> createDisplayEventConnection(ConnectionHandle,
- ISurfaceComposer::ConfigChanged);
+ sp<IDisplayEventConnection> createDisplayEventConnection(
+ ConnectionHandle, ISurfaceComposer::EventRegistrationFlags eventRegistration = {});
sp<EventThreadConnection> getEventConnection(ConnectionHandle);
void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected);
- void onPrimaryDisplayConfigChanged(ConnectionHandle, PhysicalDisplayId,
- HwcConfigIndexType configId, nsecs_t vsyncPeriod)
- EXCLUDES(mFeatureStateLock);
- void onNonPrimaryDisplayConfigChanged(ConnectionHandle, PhysicalDisplayId,
- HwcConfigIndexType configId, nsecs_t vsyncPeriod);
+ void onPrimaryDisplayModeChanged(ConnectionHandle, PhysicalDisplayId, DisplayModeId,
+ nsecs_t vsyncPeriod) EXCLUDES(mFeatureStateLock);
+ void onNonPrimaryDisplayModeChanged(ConnectionHandle, PhysicalDisplayId, DisplayModeId,
+ nsecs_t vsyncPeriod);
void onScreenAcquired(ConnectionHandle);
void onScreenReleased(ConnectionHandle);
- // Modifies phase offset in the event thread.
- void setPhaseOffset(ConnectionHandle, nsecs_t phaseOffset) override;
+ void onFrameRateOverridesChanged(ConnectionHandle, PhysicalDisplayId)
+ EXCLUDES(mFrameRateOverridesMutex) EXCLUDES(mConnectionsLock);
- void getDisplayStatInfo(DisplayStatInfo* stats);
+ // Modifies work duration in the event thread.
+ void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration);
+
+ DisplayStatInfo getDisplayStatInfo(nsecs_t now);
// Returns injector handle if injection has toggled, or an invalid handle otherwise.
ConnectionHandle enableVSyncInjection(bool enable);
-
// Returns false if injection is disabled.
- bool injectVSync(nsecs_t when, nsecs_t expectedVSyncTime);
-
+ bool injectVSync(nsecs_t when, nsecs_t expectedVSyncTime, nsecs_t deadlineTimestamp);
void enableHardwareVsync();
void disableHardwareVsync(bool makeUnavailable);
@@ -122,18 +118,18 @@
void resyncToHardwareVsync(bool makeAvailable, nsecs_t period);
void resync();
- // Passes a vsync sample to DispSync. periodFlushed will be true if
- // DispSync detected that the vsync period changed, and false otherwise.
+ // Passes a vsync sample to VsyncController. periodFlushed will be true if
+ // VsyncController detected that the vsync period changed, and false otherwise.
void addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
bool* periodFlushed);
void addPresentFence(const std::shared_ptr<FenceTime>&);
void setIgnorePresentFences(bool ignore);
- nsecs_t getDispSyncExpectedPresentTime(nsecs_t now);
// Layers are registered on creation, and unregistered when the weak reference expires.
void registerLayer(Layer*);
void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType);
- void setConfigChangePending(bool pending);
+ void setModeChangePending(bool pending);
+ void deregisterLayer(Layer*);
// Detects content using layer history, and selects a matching refresh rate.
void chooseRefreshRateForContent();
@@ -146,11 +142,21 @@
void setDisplayPowerState(bool normal);
+ scheduler::VSyncDispatch& getVsyncDispatch() { return *mVsyncSchedule.dispatch; }
+
+ // Returns true if a given vsync timestamp is considered valid vsync
+ // for a given uid
+ bool isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const
+ EXCLUDES(mFrameRateOverridesMutex);
+
+ std::chrono::steady_clock::time_point getPreviousVsyncFrom(nsecs_t expectedPresentTime) const;
+
void dump(std::string&) const;
void dump(ConnectionHandle, std::string&) const;
+ void dumpVsync(std::string&) const;
// Get the appropriate refresh for current conditions.
- std::optional<HwcConfigIndexType> getPreferredConfigId();
+ std::optional<DisplayModeId> getPreferredModeId();
// Notifies the scheduler about a refresh rate timeline change.
void onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline);
@@ -163,6 +169,17 @@
size_t getEventThreadConnectionCount(ConnectionHandle handle);
+ std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name,
+ std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration,
+ bool traceVsync = true);
+
+ // Stores the preferred refresh rate that an app should run at.
+ // FrameRateOverride.refreshRateHz == 0 means no preference.
+ void setPreferredRefreshRateForUid(FrameRateOverride) EXCLUDES(mFrameRateOverridesMutex);
+ // Retrieves the overridden refresh rate for a given uid.
+ std::optional<Fps> getFrameRateOverride(uid_t uid) const EXCLUDES(mFrameRateOverridesMutex);
+
private:
friend class TestableScheduler;
@@ -172,17 +189,33 @@
enum class TimerState { Reset, Expired };
enum class TouchState { Inactive, Active };
- // Used by tests to inject mocks.
- Scheduler(std::unique_ptr<DispSync>, std::unique_ptr<EventControlThread>,
- const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback,
- bool useContentDetectionV2, bool useContentDetection);
+ struct Options {
+ // Whether to use idle timer callbacks that support the kernel timer.
+ bool supportKernelTimer;
+ // Whether to use content detection at all.
+ bool useContentDetection;
+ };
- std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name, nsecs_t phaseOffsetNs);
+ struct VsyncSchedule {
+ std::unique_ptr<scheduler::VsyncController> controller;
+ std::unique_ptr<scheduler::VSyncTracker> tracker;
+ std::unique_ptr<scheduler::VSyncDispatch> dispatch;
+ };
+
+ // Unlike the testing constructor, this creates the VsyncSchedule, LayerHistory, and timers.
+ Scheduler(const scheduler::RefreshRateConfigs&, ISchedulerCallback&, Options);
+
+ // Used by tests to inject mocks.
+ Scheduler(VsyncSchedule, const scheduler::RefreshRateConfigs&, ISchedulerCallback&,
+ std::unique_ptr<LayerHistory>, Options);
+
+ static VsyncSchedule createVsyncSchedule(bool supportKernelIdleTimer);
+ static std::unique_ptr<LayerHistory> createLayerHistory(const scheduler::RefreshRateConfigs&);
// Create a connection on the given EventThread.
ConnectionHandle createConnection(std::unique_ptr<EventThread>);
- sp<EventThreadConnection> createConnectionInternal(EventThread*,
- ISurfaceComposer::ConfigChanged);
+ sp<EventThreadConnection> createConnectionInternal(
+ EventThread*, ISurfaceComposer::EventRegistrationFlags eventRegistration = {});
// Update feature state machine to given state when corresponding timer resets or expires.
void kernelIdleTimerCallback(TimerState);
@@ -197,13 +230,19 @@
void setVsyncPeriod(nsecs_t period);
// This function checks whether individual features that are affecting the refresh rate
- // selection were initialized, prioritizes them, and calculates the HwcConfigIndexType
+ // selection were initialized, prioritizes them, and calculates the DisplayModeId
// for the suggested refresh rate.
- HwcConfigIndexType calculateRefreshRateConfigIndexType(
+ DisplayModeId calculateRefreshRateModeId(
scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals = nullptr)
REQUIRES(mFeatureStateLock);
- void dispatchCachedReportedConfig() REQUIRES(mFeatureStateLock);
+ void dispatchCachedReportedMode() REQUIRES(mFeatureStateLock);
+ bool updateFrameRateOverrides(scheduler::RefreshRateConfigs::GlobalSignals consideredSignals,
+ Fps displayRefreshRate) REQUIRES(mFeatureStateLock)
+ EXCLUDES(mFrameRateOverridesMutex);
+
+ impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const;
+ impl::EventThread::GetVsyncPeriodFunction makeGetVsyncPeriodFunction() const;
// Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
struct Connection {
@@ -212,7 +251,8 @@
};
ConnectionHandle::Id mNextConnectionHandleId = 0;
- std::unordered_map<ConnectionHandle, Connection> mConnections;
+ mutable std::mutex mConnectionsLock;
+ std::unordered_map<ConnectionHandle, Connection> mConnections GUARDED_BY(mConnectionsLock);
bool mInjectVSyncs = false;
InjectVSyncSource* mVSyncInjector = nullptr;
@@ -224,14 +264,11 @@
std::atomic<nsecs_t> mLastResyncTime = 0;
- // Whether to use idle timer callbacks that support the kernel timer.
- const bool mSupportKernelTimer;
-
- std::unique_ptr<DispSync> mPrimaryDispSync;
- std::unique_ptr<EventControlThread> mEventControlThread;
+ const Options mOptions;
+ VsyncSchedule mVsyncSchedule;
// Used to choose refresh rate if content detection is enabled.
- std::unique_ptr<LayerHistory> mLayerHistory;
+ const std::unique_ptr<LayerHistory> mLayerHistory;
// Timer that records time between requests for next vsync.
std::optional<scheduler::OneShotTimer> mIdleTimer;
@@ -244,28 +281,27 @@
// In order to make sure that the features don't override themselves, we need a state machine
// to keep track which feature requested the config change.
- std::mutex mFeatureStateLock;
+ mutable std::mutex mFeatureStateLock;
struct {
- ContentDetectionState contentDetectionV1 = ContentDetectionState::Off;
TimerState idleTimer = TimerState::Reset;
TouchState touch = TouchState::Inactive;
TimerState displayPowerTimer = TimerState::Expired;
- std::optional<HwcConfigIndexType> configId;
+ std::optional<DisplayModeId> modeId;
LayerHistory::Summary contentRequirements;
bool isDisplayPowerStateNormal = true;
- // Used to cache the last parameters of onPrimaryDisplayConfigChanged
- struct ConfigChangedParams {
+ // Used to cache the last parameters of onPrimaryDisplayModeChanged
+ struct ModeChangedParams {
ConnectionHandle handle;
PhysicalDisplayId displayId;
- HwcConfigIndexType configId;
+ DisplayModeId modeId;
nsecs_t vsyncPeriod;
};
- std::optional<ConfigChangedParams> cachedConfigChangedParams;
+ std::optional<ModeChangedParams> cachedModeChangedParams;
} mFeatures GUARDED_BY(mFeatureStateLock);
const scheduler::RefreshRateConfigs& mRefreshRateConfigs;
@@ -275,10 +311,18 @@
GUARDED_BY(mVsyncTimelineLock);
static constexpr std::chrono::nanoseconds MAX_VSYNC_APPLIED_TIME = 200ms;
- // This variable indicates whether to use the content detection feature at all.
- const bool mUseContentDetection;
- // This variable indicates whether to use V2 version of the content detection.
- const bool mUseContentDetectionV2;
+ const std::unique_ptr<PredictedVsyncTracer> mPredictedVsyncTracer;
+
+ // The frame rate override lists need their own mutex as they are being read
+ // by SurfaceFlinger, Scheduler and EventThread (as a callback) to prevent deadlocks
+ mutable std::mutex mFrameRateOverridesMutex;
+
+ // mappings between a UID and a preferred refresh rate that this app would
+ // run at.
+ scheduler::RefreshRateConfigs::UidToFrameRateOverride mFrameRateOverridesByContent
+ GUARDED_BY(mFrameRateOverridesMutex);
+ scheduler::RefreshRateConfigs::UidToFrameRateOverride mFrameRateOverridesFromBackdoor
+ GUARDED_BY(mFrameRateOverridesMutex);
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Seamlessness.h b/services/surfaceflinger/Scheduler/Seamlessness.h
new file mode 100644
index 0000000..3e42a4d
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/Seamlessness.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <cstring>
+#include <ostream>
+
+namespace android {
+namespace scheduler {
+
+// The seamlessness requirement of a Layer.
+enum class Seamlessness {
+ // Indicates a requirement for a seamless mode switch.
+ OnlySeamless,
+ // Indicates that both seamless and seamed mode switches are allowed.
+ SeamedAndSeamless,
+ // Indicates no preference for seamlessness. For such layers the system will
+ // prefer seamless switches, but also non-seamless switches to the group of the
+ // default config are allowed.
+ Default
+};
+
+inline std::string toString(Seamlessness seamlessness) {
+ switch (seamlessness) {
+ case Seamlessness::OnlySeamless:
+ return "OnlySeamless";
+ case Seamlessness::SeamedAndSeamless:
+ return "SeamedAndSeamless";
+ case Seamlessness::Default:
+ return "Default";
+ }
+}
+
+// Used by gtest
+inline std::ostream& operator<<(std::ostream& os, Seamlessness val) {
+ return os << toString(val);
+}
+
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/StrongTyping.h b/services/surfaceflinger/Scheduler/StrongTyping.h
index e8ca0ba..a05c123 100644
--- a/services/surfaceflinger/Scheduler/StrongTyping.h
+++ b/services/surfaceflinger/Scheduler/StrongTyping.h
@@ -62,16 +62,20 @@
template <typename T, typename W, template <typename> class... Ability>
struct StrongTyping : Ability<StrongTyping<T, W, Ability...>>... {
- StrongTyping() : mValue(0) {}
- explicit StrongTyping(T const& value) : mValue(value) {}
+ constexpr StrongTyping() = default;
+ constexpr explicit StrongTyping(T const& value) : mValue(value) {}
StrongTyping(StrongTyping const&) = default;
StrongTyping& operator=(StrongTyping const&) = default;
explicit inline operator T() const { return mValue; }
T const& value() const { return mValue; }
T& value() { return mValue; }
+ friend std::ostream& operator<<(std::ostream& os, const StrongTyping<T, W, Ability...>& value) {
+ return os << value.value();
+ }
+
private:
- T mValue;
+ T mValue{0};
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Timer.cpp b/services/surfaceflinger/Scheduler/Timer.cpp
index 59c336a..c9c2d84 100644
--- a/services/surfaceflinger/Scheduler/Timer.cpp
+++ b/services/surfaceflinger/Scheduler/Timer.cpp
@@ -89,7 +89,7 @@
}
void Timer::alarmAt(std::function<void()> const& cb, nsecs_t time) {
- std::lock_guard<decltype(mMutex)> lk(mMutex);
+ std::lock_guard lock(mMutex);
using namespace std::literals;
static constexpr int ns_per_s =
std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
@@ -109,7 +109,7 @@
}
void Timer::alarmCancel() {
- std::lock_guard<decltype(mMutex)> lk(mMutex);
+ std::lock_guard lock(mMutex);
struct itimerspec old_timer;
struct itimerspec new_timer {
@@ -192,7 +192,7 @@
setDebugState(DebugState::Running);
std::function<void()> cb;
{
- std::lock_guard<decltype(mMutex)> lk(mMutex);
+ std::lock_guard lock(mMutex);
cb = mCallback;
}
if (cb) {
@@ -211,7 +211,7 @@
}
void Timer::setDebugState(DebugState state) {
- std::lock_guard lk(mMutex);
+ std::lock_guard lock(mMutex);
mDebugState = state;
}
@@ -233,7 +233,7 @@
}
void Timer::dump(std::string& result) const {
- std::lock_guard lk(mMutex);
+ std::lock_guard lock(mMutex);
StringAppendF(&result, "\t\tDebugState: %s\n", strDebugState(mDebugState));
}
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index 2a2d7c5..9d71103 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -40,11 +40,13 @@
/*
* A callback that can be registered to be awoken at a given time relative to a vsync event.
- * \param [in] vsyncTime The timestamp of the vsync the callback is for.
- * \param [in] targetWakeupTime The timestamp of intended wakeup time of the cb.
- *
+ * \param [in] vsyncTime: The timestamp of the vsync the callback is for.
+ * \param [in] targetWakeupTime: The timestamp of intended wakeup time of the cb.
+ * \param [in] readyTime: The timestamp of intended time where client needs to finish
+ * its work by.
*/
- using Callback = std::function<void(nsecs_t vsyncTime, nsecs_t targetWakeupTime)>;
+ using Callback =
+ std::function<void(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime)>;
/*
* Registers a callback that will be called at designated points on the vsync timeline.
@@ -71,33 +73,61 @@
virtual void unregisterCallback(CallbackToken token) = 0;
/*
+ * Timing information about a scheduled callback
+ *
+ * @workDuration: The time needed for the client to perform its work
+ * @readyDuration: The time needed for the client to be ready before a vsync event.
+ * For external (non-SF) clients, not only do we need to account for their
+ * workDuration, but we also need to account for the time SF will take to
+ * process their buffer/transaction. In this case, readyDuration will be set
+ * to the SF duration in order to provide enough end-to-end time, and to be
+ * able to provide the ready-by time (deadline) on the callback.
+ * For internal clients, we don't need to add additional padding, so
+ * readyDuration will typically be 0.
+ * @earliestVsync: The targeted display time. This will be snapped to the closest
+ * predicted vsync time after earliestVsync.
+ *
+ * callback will be dispatched at 'workDuration + readyDuration' nanoseconds before a vsync
+ * event.
+ */
+ struct ScheduleTiming {
+ nsecs_t workDuration = 0;
+ nsecs_t readyDuration = 0;
+ nsecs_t earliestVsync = 0;
+
+ bool operator==(const ScheduleTiming& other) const {
+ return workDuration == other.workDuration && readyDuration == other.readyDuration &&
+ earliestVsync == other.earliestVsync;
+ }
+
+ bool operator!=(const ScheduleTiming& other) const { return !(*this == other); }
+ };
+
+ /*
* Schedules the registered callback to be dispatched.
*
- * The callback will be dispatched at 'workDuration' nanoseconds before a vsync event.
+ * The callback will be dispatched at 'workDuration + readyDuration' nanoseconds before a vsync
+ * event.
*
* The caller designates the earliest vsync event that should be targeted by the earliestVsync
* parameter.
- * The callback will be scheduled at (workDuration - predictedVsync), where predictedVsync
- * is the first vsync event time where ( predictedVsync >= earliestVsync ).
+ * The callback will be scheduled at (workDuration + readyDuration - predictedVsync), where
+ * predictedVsync is the first vsync event time where ( predictedVsync >= earliestVsync ).
*
- * If (workDuration - earliestVsync) is in the past, or if a callback has already been
- * dispatched for the predictedVsync, an error will be returned.
+ * If (workDuration + readyDuration - earliestVsync) is in the past, or if a callback has
+ * already been dispatched for the predictedVsync, an error will be returned.
*
* It is valid to reschedule a callback to a different time.
*
* \param [in] token The callback to schedule.
- * \param [in] workDuration The time before the actual vsync time to invoke the callback
- * associated with token.
- * \param [in] earliestVsync The targeted display time. This will be snapped to the closest
- * predicted vsync time after earliestVsync.
+ * \param [in] scheduleTiming The timing information for this schedule call
* \return A ScheduleResult::Scheduled if callback was scheduled.
* A ScheduleResult::CannotSchedule
- * if (workDuration - earliestVsync) is in the past, or
- * if a callback was dispatched for the predictedVsync already.
- * A ScheduleResult::Error if there was another error.
+ * if (workDuration + readyDuration - earliestVsync) is in the past,
+ * or if a callback was dispatched for the predictedVsync already. A ScheduleResult::Error if
+ * there was another error.
*/
- virtual ScheduleResult schedule(CallbackToken token, nsecs_t workDuration,
- nsecs_t earliestVsync) = 0;
+ virtual ScheduleResult schedule(CallbackToken token, ScheduleTiming scheduleTiming) = 0;
/* Cancels a scheduled callback, if possible.
*
@@ -129,7 +159,7 @@
~VSyncCallbackRegistration();
// See documentation for VSyncDispatch::schedule.
- ScheduleResult schedule(nsecs_t workDuration, nsecs_t earliestVsync);
+ ScheduleResult schedule(VSyncDispatch::ScheduleTiming scheduleTiming);
// See documentation for VSyncDispatch::cancel.
CancelResult cancel();
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index ef92680..ca6ea27 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -35,8 +35,6 @@
nsecs_t minVsyncDistance)
: mName(name),
mCallback(cb),
- mWorkDuration(0),
- mEarliestVsync(0),
mMinVsyncDistance(minVsyncDistance) {}
std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::lastExecutedVsyncTarget() const {
@@ -54,6 +52,13 @@
return {mArmedInfo->mActualWakeupTime};
}
+std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::readyTime() const {
+ if (!mArmedInfo) {
+ return {};
+ }
+ return {mArmedInfo->mActualReadyTime};
+}
+
std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::targetVsync() const {
if (!mArmedInfo) {
return {};
@@ -61,10 +66,10 @@
return {mArmedInfo->mActualVsyncTime};
}
-ScheduleResult VSyncDispatchTimerQueueEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync,
+ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTiming timing,
VSyncTracker& tracker, nsecs_t now) {
- auto nextVsyncTime =
- tracker.nextAnticipatedVSyncTimeFrom(std::max(earliestVsync, now + workDuration));
+ auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(
+ std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration));
bool const wouldSkipAVsyncTarget =
mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance));
@@ -80,16 +85,15 @@
tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance);
}
- auto const nextWakeupTime = nextVsyncTime - workDuration;
- mWorkDuration = workDuration;
- mEarliestVsync = earliestVsync;
- mArmedInfo = {nextWakeupTime, nextVsyncTime};
+ auto const nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;
+ auto const nextReadyTime = nextVsyncTime - timing.readyDuration;
+ mScheduleTiming = timing;
+ mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
return ScheduleResult::Scheduled;
}
-void VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(nsecs_t workDuration,
- nsecs_t earliestVsync) {
- mWorkloadUpdateInfo = {.earliestVsync = earliestVsync, .duration = workDuration};
+void VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(VSyncDispatch::ScheduleTiming timing) {
+ mWorkloadUpdateInfo = timing;
}
bool VSyncDispatchTimerQueueEntry::hasPendingWorkloadUpdate() const {
@@ -102,14 +106,18 @@
}
if (mWorkloadUpdateInfo) {
- mEarliestVsync = mWorkloadUpdateInfo->earliestVsync;
- mWorkDuration = mWorkloadUpdateInfo->duration;
+ mScheduleTiming = *mWorkloadUpdateInfo;
mWorkloadUpdateInfo.reset();
}
- auto const nextVsyncTime =
- tracker.nextAnticipatedVSyncTimeFrom(std::max(mEarliestVsync, now + mWorkDuration));
- mArmedInfo = {nextVsyncTime - mWorkDuration, nextVsyncTime};
+ const auto earliestReadyBy = now + mScheduleTiming.workDuration + mScheduleTiming.readyDuration;
+ const auto earliestVsync = std::max(earliestReadyBy, mScheduleTiming.earliestVsync);
+
+ const auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(earliestVsync);
+ const auto nextReadyTime = nextVsyncTime - mScheduleTiming.readyDuration;
+ const auto nextWakeupTime = nextReadyTime - mScheduleTiming.workDuration;
+
+ mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
}
void VSyncDispatchTimerQueueEntry::disarm() {
@@ -122,13 +130,14 @@
return *mLastDispatchTime;
}
-void VSyncDispatchTimerQueueEntry::callback(nsecs_t vsyncTimestamp, nsecs_t wakeupTimestamp) {
+void VSyncDispatchTimerQueueEntry::callback(nsecs_t vsyncTimestamp, nsecs_t wakeupTimestamp,
+ nsecs_t deadlineTimestamp) {
{
std::lock_guard<std::mutex> lk(mRunningMutex);
mRunning = true;
}
- mCallback(vsyncTimestamp, wakeupTimestamp);
+ mCallback(vsyncTimestamp, wakeupTimestamp, deadlineTimestamp);
std::lock_guard<std::mutex> lk(mRunningMutex);
mRunning = false;
@@ -144,15 +153,20 @@
std::lock_guard<std::mutex> lk(mRunningMutex);
std::string armedInfo;
if (mArmedInfo) {
- StringAppendF(&armedInfo, "[wake up in %.2fms for vsync %.2fms from now]",
+ StringAppendF(&armedInfo,
+ "[wake up in %.2fms deadline in %.2fms for vsync %.2fms from now]",
(mArmedInfo->mActualWakeupTime - systemTime()) / 1e6f,
+ (mArmedInfo->mActualReadyTime - systemTime()) / 1e6f,
(mArmedInfo->mActualVsyncTime - systemTime()) / 1e6f);
}
StringAppendF(&result, "\t\t%s: %s %s\n", mName.c_str(),
mRunning ? "(in callback function)" : "", armedInfo.c_str());
- StringAppendF(&result, "\t\t\tmWorkDuration: %.2fms mEarliestVsync: %.2fms relative to now\n",
- mWorkDuration / 1e6f, (mEarliestVsync - systemTime()) / 1e6f);
+ StringAppendF(&result,
+ "\t\t\tworkDuration: %.2fms readyDuration: %.2fms earliestVsync: %.2fms relative "
+ "to now\n",
+ mScheduleTiming.workDuration / 1e6f, mScheduleTiming.readyDuration / 1e6f,
+ (mScheduleTiming.earliestVsync - systemTime()) / 1e6f);
if (mLastDispatchTime) {
StringAppendF(&result, "\t\t\tmLastDispatchTime: %.2fms ago\n",
@@ -171,7 +185,7 @@
mMinVsyncDistance(minVsyncDistance) {}
VSyncDispatchTimerQueue::~VSyncDispatchTimerQueue() {
- std::lock_guard<decltype(mMutex)> lk(mMutex);
+ std::lock_guard lock(mMutex);
cancelTimer();
}
@@ -239,10 +253,11 @@
std::shared_ptr<VSyncDispatchTimerQueueEntry> callback;
nsecs_t vsyncTimestamp;
nsecs_t wakeupTimestamp;
+ nsecs_t deadlineTimestamp;
};
std::vector<Invocation> invocations;
{
- std::lock_guard<decltype(mMutex)> lk(mMutex);
+ std::lock_guard lock(mMutex);
auto const now = mTimeKeeper->now();
mLastTimerCallback = now;
for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
@@ -252,11 +267,13 @@
continue;
}
+ auto const readyTime = callback->readyTime();
+
auto const lagAllowance = std::max(now - mIntendedWakeupTime, static_cast<nsecs_t>(0));
if (*wakeupTime < mIntendedWakeupTime + mTimerSlack + lagAllowance) {
callback->executing();
- invocations.emplace_back(
- Invocation{callback, *callback->lastExecutedVsyncTarget(), *wakeupTime});
+ invocations.emplace_back(Invocation{callback, *callback->lastExecutedVsyncTarget(),
+ *wakeupTime, *readyTime});
}
}
@@ -265,13 +282,14 @@
}
for (auto const& invocation : invocations) {
- invocation.callback->callback(invocation.vsyncTimestamp, invocation.wakeupTimestamp);
+ invocation.callback->callback(invocation.vsyncTimestamp, invocation.wakeupTimestamp,
+ invocation.deadlineTimestamp);
}
}
VSyncDispatchTimerQueue::CallbackToken VSyncDispatchTimerQueue::registerCallback(
Callback const& callbackFn, std::string callbackName) {
- std::lock_guard<decltype(mMutex)> lk(mMutex);
+ std::lock_guard lock(mMutex);
return CallbackToken{
mCallbacks
.emplace(++mCallbackToken,
@@ -284,7 +302,7 @@
void VSyncDispatchTimerQueue::unregisterCallback(CallbackToken token) {
std::shared_ptr<VSyncDispatchTimerQueueEntry> entry = nullptr;
{
- std::lock_guard<decltype(mMutex)> lk(mMutex);
+ std::lock_guard lock(mMutex);
auto it = mCallbacks.find(token);
if (it != mCallbacks.end()) {
entry = it->second;
@@ -297,11 +315,11 @@
}
}
-ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token, nsecs_t workDuration,
- nsecs_t earliestVsync) {
+ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token,
+ ScheduleTiming scheduleTiming) {
auto result = ScheduleResult::Error;
{
- std::lock_guard<decltype(mMutex)> lk(mMutex);
+ std::lock_guard lock(mMutex);
auto it = mCallbacks.find(token);
if (it == mCallbacks.end()) {
@@ -314,11 +332,11 @@
* timer recalculation to avoid cancelling a callback that is about to fire. */
auto const rearmImminent = now > mIntendedWakeupTime;
if (CC_UNLIKELY(rearmImminent)) {
- callback->addPendingWorkloadUpdate(workDuration, earliestVsync);
+ callback->addPendingWorkloadUpdate(scheduleTiming);
return ScheduleResult::Scheduled;
}
- result = callback->schedule(workDuration, earliestVsync, mTracker, now);
+ result = callback->schedule(scheduleTiming, mTracker, now);
if (result == ScheduleResult::CannotSchedule) {
return result;
}
@@ -332,7 +350,7 @@
}
CancelResult VSyncDispatchTimerQueue::cancel(CallbackToken token) {
- std::lock_guard<decltype(mMutex)> lk(mMutex);
+ std::lock_guard lock(mMutex);
auto it = mCallbacks.find(token);
if (it == mCallbacks.end()) {
@@ -354,7 +372,7 @@
}
void VSyncDispatchTimerQueue::dump(std::string& result) const {
- std::lock_guard<decltype(mMutex)> lk(mMutex);
+ std::lock_guard lock(mMutex);
StringAppendF(&result, "\tTimer:\n");
mTimeKeeper->dump(result);
StringAppendF(&result, "\tmTimerSlack: %.2fms mMinVsyncDistance: %.2fms\n", mTimerSlack / 1e6f,
@@ -396,11 +414,11 @@
if (mValidToken) mDispatch.get().unregisterCallback(mToken);
}
-ScheduleResult VSyncCallbackRegistration::schedule(nsecs_t workDuration, nsecs_t earliestVsync) {
+ScheduleResult VSyncCallbackRegistration::schedule(VSyncDispatch::ScheduleTiming scheduleTiming) {
if (!mValidToken) {
return ScheduleResult::Error;
}
- return mDispatch.get().schedule(mToken, workDuration, earliestVsync);
+ return mDispatch.get().schedule(mToken, scheduleTiming);
}
CancelResult VSyncCallbackRegistration::cancel() {
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index 957c0d1..26237b6 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -47,7 +47,7 @@
std::optional<nsecs_t> lastExecutedVsyncTarget() const;
// This moves the state from disarmed->armed and will calculate the wakeupTime.
- ScheduleResult schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker,
+ ScheduleResult schedule(VSyncDispatch::ScheduleTiming timing, VSyncTracker& tracker,
nsecs_t now);
// This will update armed entries with the latest vsync information. Entry remains armed.
void update(VSyncTracker& tracker, nsecs_t now);
@@ -56,6 +56,8 @@
// It will not update the wakeupTime.
std::optional<nsecs_t> wakeupTime() const;
+ std::optional<nsecs_t> readyTime() const;
+
std::optional<nsecs_t> targetVsync() const;
// This moves state from armed->disarmed.
@@ -67,14 +69,14 @@
// Adds a pending upload of the earliestVSync and workDuration that will be applied on the next
// call to update()
- void addPendingWorkloadUpdate(nsecs_t workDuration, nsecs_t earliestVsync);
+ void addPendingWorkloadUpdate(VSyncDispatch::ScheduleTiming);
// Checks if there is a pending update to the workload, returning true if so.
bool hasPendingWorkloadUpdate() const;
// End: functions that are not threadsafe.
// Invoke the callback with the two given timestamps, moving the state from running->disarmed.
- void callback(nsecs_t vsyncTimestamp, nsecs_t wakeupTimestamp);
+ void callback(nsecs_t vsyncTimestamp, nsecs_t wakeupTimestamp, nsecs_t deadlineTimestamp);
// Block calling thread while the callback is executing.
void ensureNotRunning();
@@ -84,22 +86,18 @@
std::string const mName;
VSyncDispatch::Callback const mCallback;
- nsecs_t mWorkDuration;
- nsecs_t mEarliestVsync;
+ VSyncDispatch::ScheduleTiming mScheduleTiming;
nsecs_t const mMinVsyncDistance;
struct ArmingInfo {
nsecs_t mActualWakeupTime;
nsecs_t mActualVsyncTime;
+ nsecs_t mActualReadyTime;
};
std::optional<ArmingInfo> mArmedInfo;
std::optional<nsecs_t> mLastDispatchTime;
- struct WorkloadUpdateInfo {
- nsecs_t duration;
- nsecs_t earliestVsync;
- };
- std::optional<WorkloadUpdateInfo> mWorkloadUpdateInfo;
+ std::optional<VSyncDispatch::ScheduleTiming> mWorkloadUpdateInfo;
mutable std::mutex mRunningMutex;
std::condition_variable mCv;
@@ -125,7 +123,7 @@
CallbackToken registerCallback(Callback const& callbackFn, std::string callbackName) final;
void unregisterCallback(CallbackToken token) final;
- ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync) final;
+ ScheduleResult schedule(CallbackToken token, ScheduleTiming scheduleTiming) final;
CancelResult cancel(CallbackToken token) final;
void dump(std::string& result) const final;
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.cpp b/services/surfaceflinger/Scheduler/VSyncModulator.cpp
deleted file mode 100644
index 2567c04..0000000
--- a/services/surfaceflinger/Scheduler/VSyncModulator.cpp
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright 2019 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.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "VSyncModulator.h"
-
-#include <cutils/properties.h>
-#include <utils/Trace.h>
-
-#include <chrono>
-#include <cinttypes>
-#include <mutex>
-
-namespace android::scheduler {
-
-VSyncModulator::VSyncModulator(IPhaseOffsetControl& phaseOffsetControl,
- Scheduler::ConnectionHandle appConnectionHandle,
- Scheduler::ConnectionHandle sfConnectionHandle,
- const OffsetsConfig& config)
- : mPhaseOffsetControl(phaseOffsetControl),
- mAppConnectionHandle(appConnectionHandle),
- mSfConnectionHandle(sfConnectionHandle),
- mOffsetsConfig(config) {
- char value[PROPERTY_VALUE_MAX];
- property_get("debug.sf.vsync_trace_detailed_info", value, "0");
- mTraceDetailedInfo = atoi(value);
-}
-
-void VSyncModulator::setPhaseOffsets(const OffsetsConfig& config) {
- std::lock_guard<std::mutex> lock(mMutex);
- mOffsetsConfig = config;
- updateOffsetsLocked();
-}
-
-void VSyncModulator::setTransactionStart(Scheduler::TransactionStart transactionStart) {
- switch (transactionStart) {
- case Scheduler::TransactionStart::EarlyStart:
- ALOGW_IF(mExplicitEarlyWakeup, "Already in TransactionStart::EarlyStart");
- mExplicitEarlyWakeup = true;
- break;
- case Scheduler::TransactionStart::EarlyEnd:
- ALOGW_IF(!mExplicitEarlyWakeup, "Not in TransactionStart::EarlyStart");
- mExplicitEarlyWakeup = false;
- break;
- case Scheduler::TransactionStart::Normal:
- case Scheduler::TransactionStart::Early:
- // Non explicit don't change the explicit early wakeup state
- break;
- }
-
- if (mTraceDetailedInfo) {
- ATRACE_INT("mExplicitEarlyWakeup", mExplicitEarlyWakeup);
- }
-
- if (!mExplicitEarlyWakeup &&
- (transactionStart == Scheduler::TransactionStart::Early ||
- transactionStart == Scheduler::TransactionStart::EarlyEnd)) {
- mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT_TRANSACTION;
- mEarlyTxnStartTime = std::chrono::steady_clock::now();
- }
-
- // An early transaction stays an early transaction.
- if (transactionStart == mTransactionStart ||
- mTransactionStart == Scheduler::TransactionStart::EarlyEnd) {
- return;
- }
- mTransactionStart = transactionStart;
- updateOffsets();
-}
-
-void VSyncModulator::onTransactionHandled() {
- mTxnAppliedTime = std::chrono::steady_clock::now();
- if (mTransactionStart == Scheduler::TransactionStart::Normal) return;
- mTransactionStart = Scheduler::TransactionStart::Normal;
- updateOffsets();
-}
-
-void VSyncModulator::onRefreshRateChangeInitiated() {
- if (mRefreshRateChangePending) {
- return;
- }
- mRefreshRateChangePending = true;
- updateOffsets();
-}
-
-void VSyncModulator::onRefreshRateChangeCompleted() {
- if (!mRefreshRateChangePending) {
- return;
- }
- mRefreshRateChangePending = false;
- updateOffsets();
-}
-
-void VSyncModulator::onRefreshed(bool usedRenderEngine) {
- bool updateOffsetsNeeded = false;
-
- // Apply a margin to account for potential data races
- // This might make us stay in early offsets for one
- // additional frame but it's better to be conservative here.
- if ((mEarlyTxnStartTime.load() + MARGIN_FOR_TX_APPLY) < mTxnAppliedTime.load()) {
- if (mRemainingEarlyFrameCount > 0) {
- mRemainingEarlyFrameCount--;
- updateOffsetsNeeded = true;
- }
- }
- if (usedRenderEngine) {
- mRemainingRenderEngineUsageCount = MIN_EARLY_GL_FRAME_COUNT_TRANSACTION;
- updateOffsetsNeeded = true;
- } else if (mRemainingRenderEngineUsageCount > 0) {
- mRemainingRenderEngineUsageCount--;
- updateOffsetsNeeded = true;
- }
- if (updateOffsetsNeeded) {
- updateOffsets();
- }
-}
-
-VSyncModulator::Offsets VSyncModulator::getOffsets() const {
- std::lock_guard<std::mutex> lock(mMutex);
- return mOffsets;
-}
-
-const VSyncModulator::Offsets& VSyncModulator::getNextOffsets() const {
- // Early offsets are used if we're in the middle of a refresh rate
- // change, or if we recently begin a transaction.
- if (mExplicitEarlyWakeup || mTransactionStart == Scheduler::TransactionStart::EarlyEnd ||
- mRemainingEarlyFrameCount > 0 || mRefreshRateChangePending) {
- return mOffsetsConfig.early;
- } else if (mRemainingRenderEngineUsageCount > 0) {
- return mOffsetsConfig.earlyGl;
- } else {
- return mOffsetsConfig.late;
- }
-}
-
-void VSyncModulator::updateOffsets() {
- std::lock_guard<std::mutex> lock(mMutex);
- updateOffsetsLocked();
-}
-
-void VSyncModulator::updateOffsetsLocked() {
- const Offsets& offsets = getNextOffsets();
-
- mPhaseOffsetControl.setPhaseOffset(mSfConnectionHandle, offsets.sf);
- mPhaseOffsetControl.setPhaseOffset(mAppConnectionHandle, offsets.app);
-
- mOffsets = offsets;
-
- if (!mTraceDetailedInfo) {
- return;
- }
-
- const bool isEarly = &offsets == &mOffsetsConfig.early;
- const bool isEarlyGl = &offsets == &mOffsetsConfig.earlyGl;
- const bool isLate = &offsets == &mOffsetsConfig.late;
-
- ATRACE_INT("Vsync-EarlyOffsetsOn", isEarly);
- ATRACE_INT("Vsync-EarlyGLOffsetsOn", isEarlyGl);
- ATRACE_INT("Vsync-LateOffsetsOn", isLate);
-}
-
-} // namespace android::scheduler
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h
deleted file mode 100644
index ab678c9..0000000
--- a/services/surfaceflinger/Scheduler/VSyncModulator.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright 2018 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.
- */
-
-#pragma once
-
-#include <chrono>
-#include <mutex>
-
-#include "Scheduler.h"
-
-namespace android::scheduler {
-
-/*
- * Modulates the vsync-offsets depending on current SurfaceFlinger state.
- */
-class VSyncModulator {
-private:
- // Number of frames we'll keep the early phase offsets once they are activated for a
- // transaction. This acts as a low-pass filter in case the client isn't quick enough in
- // sending new transactions.
- static constexpr int MIN_EARLY_FRAME_COUNT_TRANSACTION = 2;
-
- // Number of frames we'll keep the early gl phase offsets once they are activated.
- // This acts as a low-pass filter to avoid scenarios where we rapidly
- // switch in and out of gl composition.
- static constexpr int MIN_EARLY_GL_FRAME_COUNT_TRANSACTION = 2;
-
- // Margin used to account for potential data races
- static const constexpr std::chrono::nanoseconds MARGIN_FOR_TX_APPLY = 1ms;
-
-public:
- // Wrapper for a collection of surfaceflinger/app offsets for a particular
- // configuration.
- struct Offsets {
- nsecs_t sf;
- nsecs_t app;
-
- bool operator==(const Offsets& other) const { return sf == other.sf && app == other.app; }
-
- bool operator!=(const Offsets& other) const { return !(*this == other); }
- };
-
- struct OffsetsConfig {
- Offsets early; // For transactions with the eEarlyWakeup flag.
- Offsets earlyGl; // As above but while compositing with GL.
- Offsets late; // Default.
-
- bool operator==(const OffsetsConfig& other) const {
- return early == other.early && earlyGl == other.earlyGl && late == other.late;
- }
-
- bool operator!=(const OffsetsConfig& other) const { return !(*this == other); }
- };
-
- VSyncModulator(IPhaseOffsetControl&, ConnectionHandle appConnectionHandle,
- ConnectionHandle sfConnectionHandle, const OffsetsConfig&);
-
- void setPhaseOffsets(const OffsetsConfig&) EXCLUDES(mMutex);
-
- // Signals that a transaction has started, and changes offsets accordingly.
- void setTransactionStart(Scheduler::TransactionStart transactionStart);
-
- // Signals that a transaction has been completed, so that we can finish
- // special handling for a transaction.
- void onTransactionHandled();
-
- // Called when we send a refresh rate change to hardware composer, so that
- // we can move into early offsets.
- void onRefreshRateChangeInitiated();
-
- // Called when we detect from vsync signals that the refresh rate changed.
- // This way we can move out of early offsets if no longer necessary.
- void onRefreshRateChangeCompleted();
-
- // Called when the display is presenting a new frame. usedRenderEngine
- // should be set to true if RenderEngine was involved with composing the new
- // frame.
- void onRefreshed(bool usedRenderEngine);
-
- // Returns the offsets that we are currently using
- Offsets getOffsets() const EXCLUDES(mMutex);
-
-private:
- friend class VSyncModulatorTest;
- // Returns the next offsets that we should be using
- const Offsets& getNextOffsets() const REQUIRES(mMutex);
- // Updates offsets and persists them into the scheduler framework.
- void updateOffsets() EXCLUDES(mMutex);
- void updateOffsetsLocked() REQUIRES(mMutex);
-
- IPhaseOffsetControl& mPhaseOffsetControl;
- const ConnectionHandle mAppConnectionHandle;
- const ConnectionHandle mSfConnectionHandle;
-
- mutable std::mutex mMutex;
- OffsetsConfig mOffsetsConfig GUARDED_BY(mMutex);
-
- Offsets mOffsets GUARDED_BY(mMutex){mOffsetsConfig.late};
-
- std::atomic<Scheduler::TransactionStart> mTransactionStart =
- Scheduler::TransactionStart::Normal;
- std::atomic<bool> mRefreshRateChangePending = false;
- std::atomic<bool> mExplicitEarlyWakeup = false;
- std::atomic<int> mRemainingEarlyFrameCount = 0;
- std::atomic<int> mRemainingRenderEngineUsageCount = 0;
- std::atomic<std::chrono::steady_clock::time_point> mEarlyTxnStartTime = {};
- std::atomic<std::chrono::steady_clock::time_point> mTxnAppliedTime = {};
-
- bool mTraceDetailedInfo = false;
-};
-
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 708a5b8..028f7a6 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -16,7 +16,7 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
@@ -30,6 +30,10 @@
#include <algorithm>
#include <chrono>
#include <sstream>
+#include "RefreshRateConfigs.h"
+
+#undef LOG_TAG
+#define LOG_TAG "VSyncPredictor"
namespace android::scheduler {
using base::StringAppendF;
@@ -54,7 +58,7 @@
}
}
-inline size_t VSyncPredictor::next(int i) const {
+inline size_t VSyncPredictor::next(size_t i) const {
return (i + 1) % mTimestamps.size();
}
@@ -69,12 +73,12 @@
}
nsecs_t VSyncPredictor::currentPeriod() const {
- std::lock_guard<std::mutex> lk(mMutex);
- return std::get<0>(mRateMap.find(mIdealPeriod)->second);
+ std::lock_guard lock(mMutex);
+ return mRateMap.find(mIdealPeriod)->second.slope;
}
bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
- std::lock_guard<std::mutex> lk(mMutex);
+ std::lock_guard lock(mMutex);
if (!validate(timestamp)) {
// VSR could elect to ignore the incongruent timestamp or resetModel(). If ts is ignored,
@@ -126,7 +130,7 @@
// normalizing to the oldest timestamp cuts down on error in calculating the intercept.
auto const oldest_ts = *std::min_element(mTimestamps.begin(), mTimestamps.end());
auto it = mRateMap.find(mIdealPeriod);
- auto const currentPeriod = std::get<0>(it->second);
+ auto const currentPeriod = it->second.slope;
// TODO (b/144707443): its important that there's some precision in the mean of the ordinals
// for the intercept calculation, so scale the ordinals by 1000 to continue
// fixed point calculation. Explore expanding
@@ -142,14 +146,14 @@
auto meanTS = scheduler::calculate_mean(vsyncTS);
auto meanOrdinal = scheduler::calculate_mean(ordinals);
- for (auto i = 0; i < vsyncTS.size(); i++) {
+ for (size_t i = 0; i < vsyncTS.size(); i++) {
vsyncTS[i] -= meanTS;
ordinals[i] -= meanOrdinal;
}
auto top = 0ll;
auto bottom = 0ll;
- for (auto i = 0; i < vsyncTS.size(); i++) {
+ for (size_t i = 0; i < vsyncTS.size(); i++) {
top += vsyncTS[i] * ordinals[i];
bottom += ordinals[i] * ordinals[i];
}
@@ -180,10 +184,8 @@
return true;
}
-nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const {
- std::lock_guard<std::mutex> lk(mMutex);
-
- auto const [slope, intercept] = getVSyncPredictionModel(lk);
+nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const {
+ auto const [slope, intercept] = getVSyncPredictionModelLocked();
if (mTimestamps.empty()) {
traceInt64If("VSP-mode", 1);
@@ -218,20 +220,81 @@
return prediction;
}
-std::tuple<nsecs_t, nsecs_t> VSyncPredictor::getVSyncPredictionModel() const {
- std::lock_guard<std::mutex> lk(mMutex);
- return VSyncPredictor::getVSyncPredictionModel(lk);
+nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const {
+ std::lock_guard lock(mMutex);
+ return nextAnticipatedVSyncTimeFromLocked(timePoint);
}
-std::tuple<nsecs_t, nsecs_t> VSyncPredictor::getVSyncPredictionModel(
- std::lock_guard<std::mutex> const&) const {
+/*
+ * Returns whether a given vsync timestamp is in phase with a frame rate.
+ * If the frame rate is not a divider of the refresh rate, it is always considered in phase.
+ * For example, if the vsync timestamps are (16.6,33.3,50.0,66.6):
+ * isVSyncInPhase(16.6, 30) = true
+ * isVSyncInPhase(33.3, 30) = false
+ * isVSyncInPhase(50.0, 30) = true
+ */
+bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const {
+ struct VsyncError {
+ nsecs_t vsyncTimestamp;
+ float error;
+
+ bool operator<(const VsyncError& other) const { return error < other.error; }
+ };
+
+ std::lock_guard lock(mMutex);
+ const auto divider =
+ RefreshRateConfigs::getFrameRateDivider(Fps::fromPeriodNsecs(mIdealPeriod), frameRate);
+ if (divider <= 1 || timePoint == 0) {
+ return true;
+ }
+
+ const nsecs_t period = mRateMap[mIdealPeriod].slope;
+ const nsecs_t justBeforeTimePoint = timePoint - period / 2;
+ const nsecs_t dividedPeriod = mIdealPeriod / divider;
+
+ // If this is the first time we have asked about this divider with the
+ // current vsync period, it is considered in phase and we store the closest
+ // vsync timestamp
+ const auto knownTimestampIter = mRateDividerKnownTimestampMap.find(dividedPeriod);
+ if (knownTimestampIter == mRateDividerKnownTimestampMap.end()) {
+ const auto vsync = nextAnticipatedVSyncTimeFromLocked(justBeforeTimePoint);
+ mRateDividerKnownTimestampMap[dividedPeriod] = vsync;
+ return true;
+ }
+
+ // Find the next N vsync timestamp where N is the divider.
+ // One of these vsyncs will be in phase. We return the one which is
+ // the most aligned with the last known in phase vsync
+ std::vector<VsyncError> vsyncs(static_cast<size_t>(divider));
+ const nsecs_t knownVsync = knownTimestampIter->second;
+ nsecs_t point = justBeforeTimePoint;
+ for (size_t i = 0; i < divider; i++) {
+ const nsecs_t vsync = nextAnticipatedVSyncTimeFromLocked(point);
+ const auto numPeriods = static_cast<float>(vsync - knownVsync) / (period * divider);
+ const auto error = std::abs(std::round(numPeriods) - numPeriods);
+ vsyncs[i] = {vsync, error};
+ point = vsync + 1;
+ }
+
+ const auto minVsyncError = std::min_element(vsyncs.begin(), vsyncs.end());
+ mRateDividerKnownTimestampMap[dividedPeriod] = minVsyncError->vsyncTimestamp;
+ return std::abs(minVsyncError->vsyncTimestamp - timePoint) < period / 2;
+}
+
+VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const {
+ std::lock_guard lock(mMutex);
+ const auto model = VSyncPredictor::getVSyncPredictionModelLocked();
+ return {model.slope, model.intercept};
+}
+
+VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModelLocked() const {
return mRateMap.find(mIdealPeriod)->second;
}
void VSyncPredictor::setPeriod(nsecs_t period) {
ATRACE_CALL();
- std::lock_guard<std::mutex> lk(mMutex);
+ std::lock_guard lock(mMutex);
static constexpr size_t kSizeLimit = 30;
if (CC_UNLIKELY(mRateMap.size() == kSizeLimit)) {
mRateMap.erase(mRateMap.begin());
@@ -260,29 +323,29 @@
}
bool VSyncPredictor::needsMoreSamples() const {
- std::lock_guard<std::mutex> lk(mMutex);
+ std::lock_guard lock(mMutex);
return mTimestamps.size() < kMinimumSamplesForPrediction;
}
void VSyncPredictor::resetModel() {
- std::lock_guard<std::mutex> lk(mMutex);
+ std::lock_guard lock(mMutex);
mRateMap[mIdealPeriod] = {mIdealPeriod, 0};
clearTimestamps();
}
void VSyncPredictor::dump(std::string& result) const {
- std::lock_guard<std::mutex> lk(mMutex);
+ std::lock_guard lock(mMutex);
StringAppendF(&result, "\tmIdealPeriod=%.2f\n", mIdealPeriod / 1e6f);
StringAppendF(&result, "\tRefresh Rate Map:\n");
for (const auto& [idealPeriod, periodInterceptTuple] : mRateMap) {
StringAppendF(&result,
"\t\tFor ideal period %.2fms: period = %.2fms, intercept = %" PRId64 "\n",
- idealPeriod / 1e6f, std::get<0>(periodInterceptTuple) / 1e6f,
- std::get<1>(periodInterceptTuple));
+ idealPeriod / 1e6f, periodInterceptTuple.slope / 1e6f,
+ periodInterceptTuple.intercept);
}
}
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 5f3c418..40e6944 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -38,10 +38,10 @@
uint32_t outlierTolerancePercent);
~VSyncPredictor();
- bool addVsyncTimestamp(nsecs_t timestamp) final;
- nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final;
- nsecs_t currentPeriod() const final;
- void resetModel() final;
+ bool addVsyncTimestamp(nsecs_t timestamp) final EXCLUDES(mMutex);
+ nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final EXCLUDES(mMutex);
+ nsecs_t currentPeriod() const final EXCLUDES(mMutex);
+ void resetModel() final EXCLUDES(mMutex);
/*
* Inform the model that the period is anticipated to change to a new value.
@@ -50,16 +50,23 @@
*
* \param [in] period The new period that should be used.
*/
- void setPeriod(nsecs_t period) final;
+ void setPeriod(nsecs_t period) final EXCLUDES(mMutex);
/* Query if the model is in need of more samples to make a prediction.
* \return True, if model would benefit from more samples, False if not.
*/
- bool needsMoreSamples() const final;
+ bool needsMoreSamples() const final EXCLUDES(mMutex);
- std::tuple<nsecs_t /* slope */, nsecs_t /* intercept */> getVSyncPredictionModel() const;
+ struct Model {
+ nsecs_t slope;
+ nsecs_t intercept;
+ };
- void dump(std::string& result) const final;
+ VSyncPredictor::Model getVSyncPredictionModel() const EXCLUDES(mMutex);
+
+ bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const final EXCLUDES(mMutex);
+
+ void dump(std::string& result) const final EXCLUDES(mMutex);
private:
VSyncPredictor(VSyncPredictor const&) = delete;
@@ -74,17 +81,23 @@
size_t const kOutlierTolerancePercent;
std::mutex mutable mMutex;
- size_t next(int i) const REQUIRES(mMutex);
+ size_t next(size_t i) const REQUIRES(mMutex);
bool validate(nsecs_t timestamp) const REQUIRES(mMutex);
- std::tuple<nsecs_t, nsecs_t> getVSyncPredictionModel(std::lock_guard<std::mutex> const&) const
- REQUIRES(mMutex);
+
+ Model getVSyncPredictionModelLocked() const REQUIRES(mMutex);
+
+ nsecs_t nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const REQUIRES(mMutex);
nsecs_t mIdealPeriod GUARDED_BY(mMutex);
std::optional<nsecs_t> mKnownTimestamp GUARDED_BY(mMutex);
- std::unordered_map<nsecs_t, std::tuple<nsecs_t, nsecs_t>> mutable mRateMap GUARDED_BY(mMutex);
+ // Map between ideal vsync period and the calculated model
+ std::unordered_map<nsecs_t, Model> mutable mRateMap GUARDED_BY(mMutex);
- int mLastTimestampIndex GUARDED_BY(mMutex) = 0;
+ // Map between the divided vsync period and the last known vsync timestamp
+ std::unordered_map<nsecs_t, nsecs_t> mutable mRateDividerKnownTimestampMap GUARDED_BY(mMutex);
+
+ size_t mLastTimestampIndex GUARDED_BY(mMutex) = 0;
std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex);
};
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index efa8bab..7b5d462 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -30,136 +30,23 @@
namespace android::scheduler {
using base::StringAppendF;
+VsyncController::~VsyncController() = default;
+
Clock::~Clock() = default;
nsecs_t SystemClock::now() const {
return systemTime(SYSTEM_TIME_MONOTONIC);
}
-class PredictedVsyncTracer {
-public:
- PredictedVsyncTracer(VSyncDispatch& dispatch)
- : mRegistration(dispatch,
- std::bind(&PredictedVsyncTracer::callback, this, std::placeholders::_1,
- std::placeholders::_2),
- "PredictedVsyncTracer") {
- mRegistration.schedule(0, 0);
- }
-
-private:
- TracedOrdinal<bool> mParity = {"VSYNC-predicted", 0};
- VSyncCallbackRegistration mRegistration;
-
- void callback(nsecs_t /*vsyncTime*/, nsecs_t /*targetWakeupTim*/) {
- mParity = !mParity;
- mRegistration.schedule(0, 0);
- }
-};
-
-VSyncReactor::VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch,
- std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit,
- bool supportKernelIdleTimer)
+VSyncReactor::VSyncReactor(std::unique_ptr<Clock> clock, VSyncTracker& tracker,
+ size_t pendingFenceLimit, bool supportKernelIdleTimer)
: mClock(std::move(clock)),
- mTracker(std::move(tracker)),
- mDispatch(std::move(dispatch)),
+ mTracker(tracker),
mPendingLimit(pendingFenceLimit),
- mPredictedVsyncTracer(property_get_bool("debug.sf.show_predicted_vsync", false)
- ? std::make_unique<PredictedVsyncTracer>(*mDispatch)
- : nullptr),
mSupportKernelIdleTimer(supportKernelIdleTimer) {}
VSyncReactor::~VSyncReactor() = default;
-// The DispSync interface has a 'repeat this callback at rate' semantic. This object adapts
-// VSyncDispatch's individually-scheduled callbacks so as to meet DispSync's existing semantic
-// for now.
-class CallbackRepeater {
-public:
- CallbackRepeater(VSyncDispatch& dispatch, DispSync::Callback* cb, const char* name,
- nsecs_t period, nsecs_t offset, nsecs_t notBefore)
- : mName(name),
- mCallback(cb),
- mRegistration(dispatch,
- std::bind(&CallbackRepeater::callback, this, std::placeholders::_1,
- std::placeholders::_2),
- mName),
- mPeriod(period),
- mOffset(offset),
- mLastCallTime(notBefore) {}
-
- ~CallbackRepeater() {
- std::lock_guard<std::mutex> lk(mMutex);
- mRegistration.cancel();
- }
-
- void start(nsecs_t offset) {
- std::lock_guard<std::mutex> lk(mMutex);
- mStopped = false;
- mOffset = offset;
-
- auto const schedule_result = mRegistration.schedule(calculateWorkload(), mLastCallTime);
- LOG_ALWAYS_FATAL_IF((schedule_result != ScheduleResult::Scheduled),
- "Error scheduling callback: rc %X", schedule_result);
- }
-
- void setPeriod(nsecs_t period) {
- std::lock_guard<std::mutex> lk(mMutex);
- if (period == mPeriod) {
- return;
- }
- mPeriod = period;
- }
-
- void stop() {
- std::lock_guard<std::mutex> lk(mMutex);
- LOG_ALWAYS_FATAL_IF(mStopped, "DispSyncInterface misuse: callback already stopped");
- mStopped = true;
- mRegistration.cancel();
- }
-
- void dump(std::string& result) const {
- std::lock_guard<std::mutex> lk(mMutex);
- StringAppendF(&result, "\t%s: mPeriod=%.2f last vsync time %.2fms relative to now (%s)\n",
- mName.c_str(), mPeriod / 1e6f, (mLastCallTime - systemTime()) / 1e6f,
- mStopped ? "stopped" : "running");
- }
-
-private:
- void callback(nsecs_t vsynctime, nsecs_t wakeupTime) {
- {
- std::lock_guard<std::mutex> lk(mMutex);
- mLastCallTime = vsynctime;
- }
-
- mCallback->onDispSyncEvent(wakeupTime, vsynctime);
-
- {
- std::lock_guard<std::mutex> lk(mMutex);
- if (mStopped) {
- return;
- }
- auto const schedule_result = mRegistration.schedule(calculateWorkload(), vsynctime);
- LOG_ALWAYS_FATAL_IF((schedule_result != ScheduleResult::Scheduled),
- "Error rescheduling callback: rc %X", schedule_result);
- }
- }
-
- // DispSync offsets are defined as time after the vsync before presentation.
- // VSyncReactor workloads are defined as time before the intended presentation vsync.
- // Note change in sign between the two defnitions.
- nsecs_t calculateWorkload() REQUIRES(mMutex) { return mPeriod - mOffset; }
-
- const std::string mName;
- DispSync::Callback* const mCallback;
-
- std::mutex mutable mMutex;
- VSyncCallbackRegistration mRegistration GUARDED_BY(mMutex);
- bool mStopped GUARDED_BY(mMutex) = false;
- nsecs_t mPeriod GUARDED_BY(mMutex);
- nsecs_t mOffset GUARDED_BY(mMutex);
- nsecs_t mLastCallTime GUARDED_BY(mMutex);
-};
-
-bool VSyncReactor::addPresentFence(const std::shared_ptr<FenceTime>& fence) {
+bool VSyncReactor::addPresentFence(const std::shared_ptr<android::FenceTime>& fence) {
if (!fence) {
return false;
}
@@ -169,7 +56,7 @@
return true;
}
- std::lock_guard<std::mutex> lk(mMutex);
+ std::lock_guard lock(mMutex);
if (mExternalIgnoreFences || mInternalIgnoreFences) {
return true;
}
@@ -182,7 +69,7 @@
} else if (time == Fence::SIGNAL_TIME_INVALID) {
it = mUnfiredFences.erase(it);
} else {
- timestampAccepted &= mTracker->addVsyncTimestamp(time);
+ timestampAccepted &= mTracker.addVsyncTimestamp(time);
it = mUnfiredFences.erase(it);
}
@@ -194,7 +81,7 @@
}
mUnfiredFences.push_back(fence);
} else {
- timestampAccepted &= mTracker->addVsyncTimestamp(signalTime);
+ timestampAccepted &= mTracker.addVsyncTimestamp(signalTime);
}
if (!timestampAccepted) {
@@ -206,14 +93,14 @@
return mMoreSamplesNeeded;
}
-void VSyncReactor::setIgnorePresentFences(bool ignoration) {
- std::lock_guard<std::mutex> lk(mMutex);
- mExternalIgnoreFences = ignoration;
+void VSyncReactor::setIgnorePresentFences(bool ignore) {
+ std::lock_guard lock(mMutex);
+ mExternalIgnoreFences = ignore;
updateIgnorePresentFencesInternal();
}
-void VSyncReactor::setIgnorePresentFencesInternal(bool ignoration) {
- mInternalIgnoreFences = ignoration;
+void VSyncReactor::setIgnorePresentFencesInternal(bool ignore) {
+ mInternalIgnoreFences = ignore;
updateIgnorePresentFencesInternal();
}
@@ -223,16 +110,7 @@
}
}
-nsecs_t VSyncReactor::computeNextRefresh(int periodOffset, nsecs_t now) const {
- auto const currentPeriod = periodOffset ? mTracker->currentPeriod() : 0;
- return mTracker->nextAnticipatedVSyncTimeFrom(now + periodOffset * currentPeriod);
-}
-
-nsecs_t VSyncReactor::expectedPresentTime(nsecs_t now) {
- return mTracker->nextAnticipatedVSyncTimeFrom(now);
-}
-
-void VSyncReactor::startPeriodTransition(nsecs_t newPeriod) {
+void VSyncReactor::startPeriodTransitionInternal(nsecs_t newPeriod) {
ATRACE_CALL();
mPeriodConfirmationInProgress = true;
mPeriodTransitioningTo = newPeriod;
@@ -247,30 +125,20 @@
mLastHwVsync.reset();
}
-void VSyncReactor::setPeriod(nsecs_t period) {
+void VSyncReactor::startPeriodTransition(nsecs_t period) {
ATRACE_INT64("VSR-setPeriod", period);
- std::lock_guard lk(mMutex);
+ std::lock_guard lock(mMutex);
mLastHwVsync.reset();
- if (!mSupportKernelIdleTimer && period == getPeriod()) {
+ if (!mSupportKernelIdleTimer && period == mTracker.currentPeriod()) {
endPeriodTransition();
setIgnorePresentFencesInternal(false);
mMoreSamplesNeeded = false;
} else {
- startPeriodTransition(period);
+ startPeriodTransitionInternal(period);
}
}
-nsecs_t VSyncReactor::getPeriod() {
- return mTracker->currentPeriod();
-}
-
-void VSyncReactor::beginResync() {
- mTracker->resetModel();
-}
-
-void VSyncReactor::endResync() {}
-
bool VSyncReactor::periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_t> HwcVsyncPeriod) {
if (!mPeriodConfirmationInProgress) {
return false;
@@ -281,13 +149,13 @@
}
const bool periodIsChanging =
- mPeriodTransitioningTo && (*mPeriodTransitioningTo != getPeriod());
+ mPeriodTransitioningTo && (*mPeriodTransitioningTo != mTracker.currentPeriod());
if (mSupportKernelIdleTimer && !periodIsChanging) {
// Clear out the Composer-provided period and use the allowance logic below
HwcVsyncPeriod = {};
}
- auto const period = mPeriodTransitioningTo ? *mPeriodTransitioningTo : getPeriod();
+ auto const period = mPeriodTransitioningTo ? *mPeriodTransitioningTo : mTracker.currentPeriod();
static constexpr int allowancePercent = 10;
static constexpr std::ratio<allowancePercent, 100> allowancePercentRatio;
auto const allowance = period * allowancePercentRatio.num / allowancePercentRatio.den;
@@ -299,28 +167,25 @@
return std::abs(distance - period) < allowance;
}
-bool VSyncReactor::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
- bool* periodFlushed) {
+bool VSyncReactor::addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+ bool* periodFlushed) {
assert(periodFlushed);
- std::lock_guard<std::mutex> lk(mMutex);
+ std::lock_guard lock(mMutex);
if (periodConfirmed(timestamp, hwcVsyncPeriod)) {
ATRACE_NAME("VSR: period confirmed");
if (mPeriodTransitioningTo) {
- mTracker->setPeriod(*mPeriodTransitioningTo);
- for (auto& entry : mCallbacks) {
- entry.second->setPeriod(*mPeriodTransitioningTo);
- }
+ mTracker.setPeriod(*mPeriodTransitioningTo);
*periodFlushed = true;
}
if (mLastHwVsync) {
- mTracker->addVsyncTimestamp(*mLastHwVsync);
+ mTracker.addVsyncTimestamp(*mLastHwVsync);
}
- mTracker->addVsyncTimestamp(timestamp);
+ mTracker.addVsyncTimestamp(timestamp);
endPeriodTransition();
- mMoreSamplesNeeded = mTracker->needsMoreSamples();
+ mMoreSamplesNeeded = mTracker.needsMoreSamples();
} else if (mPeriodConfirmationInProgress) {
ATRACE_NAME("VSR: still confirming period");
mLastHwVsync = timestamp;
@@ -329,8 +194,8 @@
} else {
ATRACE_NAME("VSR: adding sample");
*periodFlushed = false;
- mTracker->addVsyncTimestamp(timestamp);
- mMoreSamplesNeeded = mTracker->needsMoreSamples();
+ mTracker.addVsyncTimestamp(timestamp);
+ mMoreSamplesNeeded = mTracker.needsMoreSamples();
}
if (!mMoreSamplesNeeded) {
@@ -339,51 +204,8 @@
return mMoreSamplesNeeded;
}
-status_t VSyncReactor::addEventListener(const char* name, nsecs_t phase,
- DispSync::Callback* callback,
- nsecs_t /* lastCallbackTime */) {
- std::lock_guard<std::mutex> lk(mMutex);
- auto it = mCallbacks.find(callback);
- if (it == mCallbacks.end()) {
- // TODO (b/146557561): resolve lastCallbackTime semantics in DispSync i/f.
- static auto constexpr maxListeners = 4;
- if (mCallbacks.size() >= maxListeners) {
- ALOGE("callback %s not added, exceeded callback limit of %i (currently %zu)", name,
- maxListeners, mCallbacks.size());
- return NO_MEMORY;
- }
-
- auto const period = mTracker->currentPeriod();
- auto repeater = std::make_unique<CallbackRepeater>(*mDispatch, callback, name, period,
- phase, mClock->now());
- it = mCallbacks.emplace(std::pair(callback, std::move(repeater))).first;
- }
-
- it->second->start(phase);
- return NO_ERROR;
-}
-
-status_t VSyncReactor::removeEventListener(DispSync::Callback* callback,
- nsecs_t* /* outLastCallback */) {
- std::lock_guard<std::mutex> lk(mMutex);
- auto const it = mCallbacks.find(callback);
- LOG_ALWAYS_FATAL_IF(it == mCallbacks.end(), "callback %p not registered", callback);
-
- it->second->stop();
- return NO_ERROR;
-}
-
-status_t VSyncReactor::changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) {
- std::lock_guard<std::mutex> lk(mMutex);
- auto const it = mCallbacks.find(callback);
- LOG_ALWAYS_FATAL_IF(it == mCallbacks.end(), "callback was %p not registered", callback);
-
- it->second->start(phase);
- return NO_ERROR;
-}
-
void VSyncReactor::dump(std::string& result) const {
- std::lock_guard<std::mutex> lk(mMutex);
+ std::lock_guard lock(mMutex);
StringAppendF(&result, "VsyncReactor in use\n");
StringAppendF(&result, "Has %zu unfired fences\n", mUnfiredFences.size());
StringAppendF(&result, "mInternalIgnoreFences=%d mExternalIgnoreFences=%d\n",
@@ -403,17 +225,8 @@
StringAppendF(&result, "No Last HW vsync\n");
}
- StringAppendF(&result, "CallbackRepeaters:\n");
- for (const auto& [callback, repeater] : mCallbacks) {
- repeater->dump(result);
- }
-
StringAppendF(&result, "VSyncTracker:\n");
- mTracker->dump(result);
- StringAppendF(&result, "VSyncDispatch:\n");
- mDispatch->dump(result);
+ mTracker.dump(result);
}
-void VSyncReactor::reset() {}
-
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index 265d89c..449d4c3 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -22,74 +22,53 @@
#include <mutex>
#include <unordered_map>
#include <vector>
-#include "DispSync.h"
#include "TimeKeeper.h"
+#include "VsyncController.h"
namespace android::scheduler {
class Clock;
class VSyncDispatch;
class VSyncTracker;
-class CallbackRepeater;
-class PredictedVsyncTracer;
// TODO (b/145217110): consider renaming.
-class VSyncReactor : public android::DispSync {
+class VSyncReactor : public VsyncController {
public:
- VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch,
- std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit,
+ VSyncReactor(std::unique_ptr<Clock> clock, VSyncTracker& tracker, size_t pendingFenceLimit,
bool supportKernelIdleTimer);
~VSyncReactor();
- bool addPresentFence(const std::shared_ptr<FenceTime>& fence) final;
- void setIgnorePresentFences(bool ignoration) final;
+ bool addPresentFence(const std::shared_ptr<android::FenceTime>& fence) final;
+ void setIgnorePresentFences(bool ignore) final;
- nsecs_t computeNextRefresh(int periodOffset, nsecs_t now) const final;
- nsecs_t expectedPresentTime(nsecs_t now) final;
+ void startPeriodTransition(nsecs_t period) final;
- void setPeriod(nsecs_t period) final;
- nsecs_t getPeriod() final;
-
- // TODO: (b/145626181) remove begin,endResync functions from DispSync i/f when possible.
- void beginResync() final;
- bool addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
- bool* periodFlushed) final;
- void endResync() final;
-
- status_t addEventListener(const char* name, nsecs_t phase, DispSync::Callback* callback,
- nsecs_t lastCallbackTime) final;
- status_t removeEventListener(DispSync::Callback* callback, nsecs_t* outLastCallback) final;
- status_t changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) final;
+ bool addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+ bool* periodFlushed) final;
void dump(std::string& result) const final;
- void reset() final;
private:
- void setIgnorePresentFencesInternal(bool ignoration) REQUIRES(mMutex);
+ void setIgnorePresentFencesInternal(bool ignore) REQUIRES(mMutex);
void updateIgnorePresentFencesInternal() REQUIRES(mMutex);
- void startPeriodTransition(nsecs_t newPeriod) REQUIRES(mMutex);
+ void startPeriodTransitionInternal(nsecs_t newPeriod) REQUIRES(mMutex);
void endPeriodTransition() REQUIRES(mMutex);
bool periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_t> hwcVsyncPeriod)
REQUIRES(mMutex);
std::unique_ptr<Clock> const mClock;
- std::unique_ptr<VSyncTracker> const mTracker;
- std::unique_ptr<VSyncDispatch> const mDispatch;
+ VSyncTracker& mTracker;
size_t const mPendingLimit;
mutable std::mutex mMutex;
bool mInternalIgnoreFences GUARDED_BY(mMutex) = false;
bool mExternalIgnoreFences GUARDED_BY(mMutex) = false;
- std::vector<std::shared_ptr<FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
+ std::vector<std::shared_ptr<android::FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
bool mMoreSamplesNeeded GUARDED_BY(mMutex) = false;
bool mPeriodConfirmationInProgress GUARDED_BY(mMutex) = false;
std::optional<nsecs_t> mPeriodTransitioningTo GUARDED_BY(mMutex);
std::optional<nsecs_t> mLastHwVsync GUARDED_BY(mMutex);
- std::unordered_map<DispSync::Callback*, std::unique_ptr<CallbackRepeater>> mCallbacks
- GUARDED_BY(mMutex);
-
- const std::unique_ptr<PredictedVsyncTracer> mPredictedVsyncTracer;
const bool mSupportKernelIdleTimer = false;
};
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 107c540..95750ad 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -17,6 +17,7 @@
#pragma once
#include <utils/Timers.h>
+#include "Fps.h"
#include "VSyncDispatch.h"
namespace android::scheduler {
@@ -68,6 +69,14 @@
virtual bool needsMoreSamples() const = 0;
+ /*
+ * Checks if a vsync timestamp is in phase for a frame rate
+ *
+ * \param [in] timePoint A vsync timestamp
+ * \param [in] frameRate The frame rate to check for
+ */
+ virtual bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const = 0;
+
virtual void dump(std::string& result) const = 0;
protected:
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
new file mode 100644
index 0000000..cb57aea
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
@@ -0,0 +1,369 @@
+/*
+ * Copyright 2019 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 "VsyncConfiguration.h"
+
+#include <chrono>
+#include <cinttypes>
+#include <optional>
+
+#include <cutils/properties.h>
+#include <log/log.h>
+
+#include "SurfaceFlingerProperties.h"
+
+namespace {
+
+using namespace std::chrono_literals;
+
+std::optional<nsecs_t> getProperty(const char* name) {
+ char value[PROPERTY_VALUE_MAX];
+ property_get(name, value, "-1");
+ if (const int i = atoi(value); i != -1) return i;
+ return std::nullopt;
+}
+
+} // namespace
+
+namespace android::scheduler::impl {
+
+VsyncConfiguration::VsyncConfiguration(Fps currentFps) : mRefreshRateFps(currentFps) {}
+
+PhaseOffsets::VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRate(Fps fps) const {
+ std::lock_guard lock(mLock);
+ return getConfigsForRefreshRateLocked(fps);
+}
+
+PhaseOffsets::VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRateLocked(Fps fps) const {
+ const auto iter = mOffsetsCache.find(fps);
+ if (iter != mOffsetsCache.end()) {
+ return iter->second;
+ }
+
+ const auto offset = constructOffsets(fps.getPeriodNsecs());
+ mOffsetsCache[fps] = offset;
+ return offset;
+}
+
+void VsyncConfiguration::dump(std::string& result) const {
+ const auto [early, earlyGpu, late] = getCurrentConfigs();
+ using base::StringAppendF;
+ StringAppendF(&result,
+ " app phase: %9" PRId64 " ns\t SF phase: %9" PRId64
+ " ns\n"
+ " app duration: %9lld ns\t SF duration: %9lld ns\n"
+ " early app phase: %9" PRId64 " ns\t early SF phase: %9" PRId64
+ " ns\n"
+ " early app duration: %9lld ns\t early SF duration: %9lld ns\n"
+ " GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64
+ " ns\n"
+ " GL early app duration: %9lld ns\tGL early SF duration: %9lld ns\n",
+ late.appOffset, late.sfOffset,
+
+ late.appWorkDuration.count(), late.sfWorkDuration.count(),
+
+ early.appOffset, early.sfOffset,
+
+ early.appWorkDuration.count(), early.sfWorkDuration.count(),
+
+ earlyGpu.appOffset, earlyGpu.sfOffset,
+
+ earlyGpu.appWorkDuration.count(), earlyGpu.sfWorkDuration.count());
+}
+
+PhaseOffsets::PhaseOffsets(Fps currentRefreshRate)
+ : PhaseOffsets(currentRefreshRate, sysprop::vsync_event_phase_offset_ns(1000000),
+ sysprop::vsync_sf_event_phase_offset_ns(1000000),
+ getProperty("debug.sf.early_phase_offset_ns"),
+ getProperty("debug.sf.early_gl_phase_offset_ns"),
+ getProperty("debug.sf.early_app_phase_offset_ns"),
+ getProperty("debug.sf.early_gl_app_phase_offset_ns"),
+ getProperty("debug.sf.high_fps_late_app_phase_offset_ns").value_or(2000000),
+ getProperty("debug.sf.high_fps_late_sf_phase_offset_ns").value_or(1000000),
+ getProperty("debug.sf.high_fps_early_phase_offset_ns"),
+ getProperty("debug.sf.high_fps_early_gl_phase_offset_ns"),
+ getProperty("debug.sf.high_fps_early_app_phase_offset_ns"),
+ getProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns"),
+ // Below defines the threshold when an offset is considered to be negative,
+ // i.e. targeting for the N+2 vsync instead of N+1. This means that: For offset
+ // < threshold, SF wake up (vsync_duration - offset) before HW vsync. For
+ // offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW
+ // vsync.
+ getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
+ .value_or(std::numeric_limits<nsecs_t>::max())) {}
+
+PhaseOffsets::PhaseOffsets(Fps currentFps, nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
+ std::optional<nsecs_t> earlySfOffsetNs,
+ std::optional<nsecs_t> earlyGpuSfOffsetNs,
+ std::optional<nsecs_t> earlyAppOffsetNs,
+ std::optional<nsecs_t> earlyGpuAppOffsetNs,
+ nsecs_t highFpsVsyncPhaseOffsetNs, nsecs_t highFpsSfVSyncPhaseOffsetNs,
+ std::optional<nsecs_t> highFpsEarlySfOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyGpuSfOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyAppOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyGpuAppOffsetNs,
+ nsecs_t thresholdForNextVsync)
+ : VsyncConfiguration(currentFps),
+ mVSyncPhaseOffsetNs(vsyncPhaseOffsetNs),
+ mSfVSyncPhaseOffsetNs(sfVSyncPhaseOffsetNs),
+ mEarlySfOffsetNs(earlySfOffsetNs),
+ mEarlyGpuSfOffsetNs(earlyGpuSfOffsetNs),
+ mEarlyAppOffsetNs(earlyAppOffsetNs),
+ mEarlyGpuAppOffsetNs(earlyGpuAppOffsetNs),
+ mHighFpsVSyncPhaseOffsetNs(highFpsVsyncPhaseOffsetNs),
+ mHighFpsSfVSyncPhaseOffsetNs(highFpsSfVSyncPhaseOffsetNs),
+ mHighFpsEarlySfOffsetNs(highFpsEarlySfOffsetNs),
+ mHighFpsEarlyGpuSfOffsetNs(highFpsEarlyGpuSfOffsetNs),
+ mHighFpsEarlyAppOffsetNs(highFpsEarlyAppOffsetNs),
+ mHighFpsEarlyGpuAppOffsetNs(highFpsEarlyGpuAppOffsetNs),
+ mThresholdForNextVsync(thresholdForNextVsync) {}
+
+PhaseOffsets::VsyncConfigSet PhaseOffsets::constructOffsets(nsecs_t vsyncDuration) const {
+ if (vsyncDuration < std::chrono::nanoseconds(15ms).count()) {
+ return getHighFpsOffsets(vsyncDuration);
+ } else {
+ return getDefaultOffsets(vsyncDuration);
+ }
+}
+
+namespace {
+std::chrono::nanoseconds sfOffsetToDuration(nsecs_t sfOffset, nsecs_t vsyncDuration) {
+ return std::chrono::nanoseconds(vsyncDuration - sfOffset);
+}
+
+std::chrono::nanoseconds appOffsetToDuration(nsecs_t appOffset, nsecs_t sfOffset,
+ nsecs_t vsyncDuration) {
+ auto duration = vsyncDuration + (sfOffset - appOffset);
+ if (duration < vsyncDuration) {
+ duration += vsyncDuration;
+ }
+
+ return std::chrono::nanoseconds(duration);
+}
+} // namespace
+
+PhaseOffsets::VsyncConfigSet PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const {
+ const auto earlySfOffset =
+ mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
+
+ ? mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs)
+ : mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration;
+ const auto earlyAppOffset = mEarlyAppOffsetNs.value_or(mVSyncPhaseOffsetNs);
+ const auto earlyGpuSfOffset =
+ mEarlyGpuSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
+
+ ? mEarlyGpuSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs)
+ : mEarlyGpuSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration;
+ const auto earlyGpuAppOffset = mEarlyGpuAppOffsetNs.value_or(mVSyncPhaseOffsetNs);
+ const auto lateSfOffset = mSfVSyncPhaseOffsetNs < mThresholdForNextVsync
+ ? mSfVSyncPhaseOffsetNs
+ : mSfVSyncPhaseOffsetNs - vsyncDuration;
+ const auto lateAppOffset = mVSyncPhaseOffsetNs;
+
+ return {
+ .early = {.sfOffset = earlySfOffset,
+ .appOffset = earlyAppOffset,
+ .sfWorkDuration = sfOffsetToDuration(earlySfOffset, vsyncDuration),
+ .appWorkDuration =
+ appOffsetToDuration(earlyAppOffset, earlySfOffset, vsyncDuration)},
+ .earlyGpu = {.sfOffset = earlyGpuSfOffset,
+ .appOffset = earlyGpuAppOffset,
+ .sfWorkDuration = sfOffsetToDuration(earlyGpuSfOffset, vsyncDuration),
+ .appWorkDuration = appOffsetToDuration(earlyGpuAppOffset, earlyGpuSfOffset,
+ vsyncDuration)},
+ .late = {.sfOffset = lateSfOffset,
+ .appOffset = lateAppOffset,
+ .sfWorkDuration = sfOffsetToDuration(lateSfOffset, vsyncDuration),
+ .appWorkDuration =
+ appOffsetToDuration(lateAppOffset, lateSfOffset, vsyncDuration)},
+ };
+}
+
+PhaseOffsets::VsyncConfigSet PhaseOffsets::getHighFpsOffsets(nsecs_t vsyncDuration) const {
+ const auto earlySfOffset =
+ mHighFpsEarlySfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
+ ? mHighFpsEarlySfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs)
+ : mHighFpsEarlySfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs) - vsyncDuration;
+ const auto earlyAppOffset = mHighFpsEarlyAppOffsetNs.value_or(mHighFpsVSyncPhaseOffsetNs);
+ const auto earlyGpuSfOffset = mHighFpsEarlyGpuSfOffsetNs.value_or(
+ mHighFpsSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
+
+ ? mHighFpsEarlyGpuSfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs)
+ : mHighFpsEarlyGpuSfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs) - vsyncDuration;
+ const auto earlyGpuAppOffset = mHighFpsEarlyGpuAppOffsetNs.value_or(mHighFpsVSyncPhaseOffsetNs);
+ const auto lateSfOffset = mHighFpsSfVSyncPhaseOffsetNs < mThresholdForNextVsync
+ ? mHighFpsSfVSyncPhaseOffsetNs
+ : mHighFpsSfVSyncPhaseOffsetNs - vsyncDuration;
+ const auto lateAppOffset = mHighFpsVSyncPhaseOffsetNs;
+
+ return {
+ .early =
+ {
+ .sfOffset = earlySfOffset,
+ .appOffset = earlyAppOffset,
+ .sfWorkDuration = sfOffsetToDuration(earlySfOffset, vsyncDuration),
+ .appWorkDuration = appOffsetToDuration(earlyAppOffset, earlySfOffset,
+ vsyncDuration),
+ },
+ .earlyGpu =
+ {
+ .sfOffset = earlyGpuSfOffset,
+ .appOffset = earlyGpuAppOffset,
+ .sfWorkDuration = sfOffsetToDuration(earlyGpuSfOffset, vsyncDuration),
+ .appWorkDuration = appOffsetToDuration(earlyGpuAppOffset,
+ earlyGpuSfOffset, vsyncDuration),
+ },
+ .late =
+ {
+ .sfOffset = lateSfOffset,
+ .appOffset = lateAppOffset,
+ .sfWorkDuration = sfOffsetToDuration(lateSfOffset, vsyncDuration),
+ .appWorkDuration =
+ appOffsetToDuration(lateAppOffset, lateSfOffset, vsyncDuration),
+ },
+ };
+}
+
+static void validateSysprops() {
+ const auto validatePropertyBool = [](const char* prop) {
+ LOG_ALWAYS_FATAL_IF(!property_get_bool(prop, false), "%s is false", prop);
+ };
+
+ validatePropertyBool("debug.sf.use_phase_offsets_as_durations");
+
+ LOG_ALWAYS_FATAL_IF(sysprop::vsync_event_phase_offset_ns(-1) != -1,
+ "ro.surface_flinger.vsync_event_phase_offset_ns is set but expecting "
+ "duration");
+
+ LOG_ALWAYS_FATAL_IF(sysprop::vsync_sf_event_phase_offset_ns(-1) != -1,
+ "ro.surface_flinger.vsync_sf_event_phase_offset_ns is set but expecting "
+ "duration");
+
+ const auto validateProperty = [](const char* prop) {
+ LOG_ALWAYS_FATAL_IF(getProperty(prop).has_value(),
+ "%s is set to %" PRId64 " but expecting duration", prop,
+ getProperty(prop).value_or(-1));
+ };
+
+ validateProperty("debug.sf.early_phase_offset_ns");
+ validateProperty("debug.sf.early_gl_phase_offset_ns");
+ validateProperty("debug.sf.early_app_phase_offset_ns");
+ validateProperty("debug.sf.early_gl_app_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_late_app_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_late_sf_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_early_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_early_gl_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_early_app_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
+}
+
+namespace {
+nsecs_t sfDurationToOffset(std::chrono::nanoseconds sfDuration, nsecs_t vsyncDuration) {
+ return vsyncDuration - sfDuration.count() % vsyncDuration;
+}
+
+nsecs_t appDurationToOffset(std::chrono::nanoseconds appDuration,
+ std::chrono::nanoseconds sfDuration, nsecs_t vsyncDuration) {
+ return vsyncDuration - (appDuration + sfDuration).count() % vsyncDuration;
+}
+} // namespace
+
+WorkDuration::VsyncConfigSet WorkDuration::constructOffsets(nsecs_t vsyncDuration) const {
+ const auto sfDurationFixup = [vsyncDuration](nsecs_t duration) {
+ return duration == -1 ? std::chrono::nanoseconds(vsyncDuration) - 1ms
+ : std::chrono::nanoseconds(duration);
+ };
+
+ const auto appDurationFixup = [vsyncDuration](nsecs_t duration) {
+ return duration == -1 ? std::chrono::nanoseconds(vsyncDuration)
+ : std::chrono::nanoseconds(duration);
+ };
+
+ const auto sfEarlyDuration = sfDurationFixup(mSfEarlyDuration);
+ const auto appEarlyDuration = appDurationFixup(mAppEarlyDuration);
+ const auto sfEarlyGpuDuration = sfDurationFixup(mSfEarlyGpuDuration);
+ const auto appEarlyGpuDuration = appDurationFixup(mAppEarlyGpuDuration);
+ const auto sfDuration = sfDurationFixup(mSfDuration);
+ const auto appDuration = appDurationFixup(mAppDuration);
+
+ return {
+ .early =
+ {
+
+ .sfOffset = sfEarlyDuration.count() < vsyncDuration
+ ? sfDurationToOffset(sfEarlyDuration, vsyncDuration)
+ : sfDurationToOffset(sfEarlyDuration, vsyncDuration) -
+ vsyncDuration,
+
+ .appOffset = appDurationToOffset(appEarlyDuration, sfEarlyDuration,
+ vsyncDuration),
+
+ .sfWorkDuration = sfEarlyDuration,
+ .appWorkDuration = appEarlyDuration,
+ },
+ .earlyGpu =
+ {
+
+ .sfOffset = sfEarlyGpuDuration.count() < vsyncDuration
+
+ ? sfDurationToOffset(sfEarlyGpuDuration, vsyncDuration)
+ : sfDurationToOffset(sfEarlyGpuDuration, vsyncDuration) -
+ vsyncDuration,
+
+ .appOffset = appDurationToOffset(appEarlyGpuDuration,
+ sfEarlyGpuDuration, vsyncDuration),
+ .sfWorkDuration = sfEarlyGpuDuration,
+ .appWorkDuration = appEarlyGpuDuration,
+ },
+ .late =
+ {
+
+ .sfOffset = sfDuration.count() < vsyncDuration
+
+ ? sfDurationToOffset(sfDuration, vsyncDuration)
+ : sfDurationToOffset(sfDuration, vsyncDuration) - vsyncDuration,
+
+ .appOffset =
+ appDurationToOffset(appDuration, sfDuration, vsyncDuration),
+
+ .sfWorkDuration = sfDuration,
+ .appWorkDuration = appDuration,
+ },
+ };
+}
+
+WorkDuration::WorkDuration(Fps currentRefreshRate)
+ : WorkDuration(currentRefreshRate, getProperty("debug.sf.late.sf.duration").value_or(-1),
+ getProperty("debug.sf.late.app.duration").value_or(-1),
+ getProperty("debug.sf.early.sf.duration").value_or(mSfDuration),
+ getProperty("debug.sf.early.app.duration").value_or(mAppDuration),
+ getProperty("debug.sf.earlyGl.sf.duration").value_or(mSfDuration),
+ getProperty("debug.sf.earlyGl.app.duration").value_or(mAppDuration)) {
+ validateSysprops();
+}
+
+WorkDuration::WorkDuration(Fps currentRefreshRate, nsecs_t sfDuration, nsecs_t appDuration,
+ nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
+ nsecs_t sfEarlyGpuDuration, nsecs_t appEarlyGpuDuration)
+ : VsyncConfiguration(currentRefreshRate),
+ mSfDuration(sfDuration),
+ mAppDuration(appDuration),
+ mSfEarlyDuration(sfEarlyDuration),
+ mAppEarlyDuration(appEarlyDuration),
+ mSfEarlyGpuDuration(sfEarlyGpuDuration),
+ mAppEarlyGpuDuration(appEarlyGpuDuration) {}
+
+} // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.h b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
new file mode 100644
index 0000000..d9d206d
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2019 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.
+ */
+
+#pragma once
+
+#include <mutex>
+#include <type_traits>
+#include <unordered_map>
+#include <vector>
+
+#include <utils/Timers.h>
+
+#include "Fps.h"
+#include "VsyncModulator.h"
+
+namespace android::scheduler {
+
+/*
+ * This class encapsulates vsync configurations for different refresh rates. Depending
+ * on what refresh rate we are using, and wheter we are composing in GL,
+ * different offsets will help us with latency. This class keeps track of
+ * which mode the device is on, and returns approprate offsets when needed.
+ */
+class VsyncConfiguration {
+public:
+ using VsyncConfigSet = VsyncModulator::VsyncConfigSet;
+
+ virtual ~VsyncConfiguration() = default;
+ virtual VsyncConfigSet getCurrentConfigs() const = 0;
+ virtual VsyncConfigSet getConfigsForRefreshRate(Fps fps) const = 0;
+ virtual void reset() = 0;
+
+ virtual void setRefreshRateFps(Fps fps) = 0;
+ virtual void dump(std::string& result) const = 0;
+};
+
+namespace impl {
+
+/*
+ * This is a common implementation for both phase offsets and durations.
+ * PhaseOffsets and WorkDuration derive from this class and implement the
+ * constructOffsets method
+ */
+class VsyncConfiguration : public scheduler::VsyncConfiguration {
+public:
+ explicit VsyncConfiguration(Fps currentFps);
+
+ // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
+ VsyncConfigSet getConfigsForRefreshRate(Fps fps) const override EXCLUDES(mLock);
+
+ // Returns early, early GL, and late offsets for Apps and SF.
+ VsyncConfigSet getCurrentConfigs() const override EXCLUDES(mLock) {
+ std::lock_guard lock(mLock);
+ return getConfigsForRefreshRateLocked(mRefreshRateFps);
+ }
+
+ // Cleans the internal cache.
+ void reset() override EXCLUDES(mLock) {
+ std::lock_guard lock(mLock);
+ mOffsetsCache.clear();
+ }
+
+ // This function should be called when the device is switching between different
+ // refresh rates, to properly update the offsets.
+ void setRefreshRateFps(Fps fps) override EXCLUDES(mLock) {
+ std::lock_guard lock(mLock);
+ mRefreshRateFps = fps;
+ }
+
+ // Returns current offsets in human friendly format.
+ void dump(std::string& result) const override;
+
+protected:
+ virtual VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const = 0;
+
+ VsyncConfigSet getConfigsForRefreshRateLocked(Fps fps) const REQUIRES(mLock);
+
+ mutable std::unordered_map<Fps, VsyncConfigSet, std::hash<Fps>, Fps::EqualsInBuckets>
+ mOffsetsCache GUARDED_BY(mLock);
+ std::atomic<Fps> mRefreshRateFps GUARDED_BY(mLock);
+ mutable std::mutex mLock;
+};
+
+/*
+ * This is the old implementation of phase offsets and considered as deprecated.
+ * WorkDuration is the new implementation.
+ */
+class PhaseOffsets : public VsyncConfiguration {
+public:
+ explicit PhaseOffsets(Fps currentRefreshRate);
+
+protected:
+ // Used for unit tests
+ PhaseOffsets(Fps currentRefreshRate, nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
+ std::optional<nsecs_t> earlySfOffsetNs, std::optional<nsecs_t> earlyGpuSfOffsetNs,
+ std::optional<nsecs_t> earlyAppOffsetNs,
+ std::optional<nsecs_t> earlyGpuAppOffsetNs, nsecs_t highFpsVsyncPhaseOffsetNs,
+ nsecs_t highFpsSfVSyncPhaseOffsetNs, std::optional<nsecs_t> highFpsEarlySfOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyGpuSfOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyAppOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyGpuAppOffsetNs, nsecs_t thresholdForNextVsync);
+
+private:
+ VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const override;
+
+ VsyncConfigSet getDefaultOffsets(nsecs_t vsyncPeriod) const;
+ VsyncConfigSet getHighFpsOffsets(nsecs_t vsyncPeriod) const;
+
+ const nsecs_t mVSyncPhaseOffsetNs;
+ const nsecs_t mSfVSyncPhaseOffsetNs;
+ const std::optional<nsecs_t> mEarlySfOffsetNs;
+ const std::optional<nsecs_t> mEarlyGpuSfOffsetNs;
+ const std::optional<nsecs_t> mEarlyAppOffsetNs;
+ const std::optional<nsecs_t> mEarlyGpuAppOffsetNs;
+
+ const nsecs_t mHighFpsVSyncPhaseOffsetNs;
+ const nsecs_t mHighFpsSfVSyncPhaseOffsetNs;
+ const std::optional<nsecs_t> mHighFpsEarlySfOffsetNs;
+ const std::optional<nsecs_t> mHighFpsEarlyGpuSfOffsetNs;
+ const std::optional<nsecs_t> mHighFpsEarlyAppOffsetNs;
+ const std::optional<nsecs_t> mHighFpsEarlyGpuAppOffsetNs;
+
+ const nsecs_t mThresholdForNextVsync;
+};
+
+/*
+ * Class that encapsulates the phase offsets for SurfaceFlinger and App.
+ * The offsets are calculated from durations for each one of the (late, early, earlyGpu)
+ * offset types.
+ */
+class WorkDuration : public VsyncConfiguration {
+public:
+ explicit WorkDuration(Fps currentRefrshRate);
+
+protected:
+ // Used for unit tests
+ WorkDuration(Fps currentFps, nsecs_t sfDuration, nsecs_t appDuration, nsecs_t sfEarlyDuration,
+ nsecs_t appEarlyDuration, nsecs_t sfEarlyGpuDuration, nsecs_t appEarlyGpuDuration);
+
+private:
+ VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const override;
+
+ const nsecs_t mSfDuration;
+ const nsecs_t mAppDuration;
+
+ const nsecs_t mSfEarlyDuration;
+ const nsecs_t mAppEarlyDuration;
+
+ const nsecs_t mSfEarlyGpuDuration;
+ const nsecs_t mAppEarlyGpuDuration;
+};
+
+} // namespace impl
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VsyncController.h b/services/surfaceflinger/Scheduler/VsyncController.h
new file mode 100644
index 0000000..0f0df22
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncController.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <cstddef>
+
+#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+
+#include <ui/FenceTime.h>
+
+#include <memory>
+
+namespace android::scheduler {
+
+class FenceTime;
+
+class VsyncController {
+public:
+ virtual ~VsyncController();
+
+ /*
+ * Adds a present fence to the model. The controller will use the fence time as
+ * a vsync signal.
+ *
+ * \param [in] fence The present fence given from the display
+ * \return True if the model needs more vsync signals to make
+ * an accurate prediction,
+ * False otherwise
+ */
+ virtual bool addPresentFence(const std::shared_ptr<android::FenceTime>&) = 0;
+
+ /*
+ * Adds a hw sync timestamp to the model. The controller will use the timestamp
+ * time as a vsync signal.
+ *
+ * \param [in] timestamp The HW Vsync timestamp
+ * \param [in] hwcVsyncPeriod The Vsync period reported by composer, if available
+ * \param [out] periodFlushed True if the vsync period changed is completed
+ * \return True if the model needs more vsync signals to make
+ * an accurate prediction,
+ * False otherwise
+ */
+ virtual bool addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+ bool* periodFlushed) = 0;
+
+ /*
+ * Inform the controller that the period is changing and the controller needs to recalibrate
+ * itself. The controller will end the period transition internally.
+ *
+ * \param [in] period The period that the system is changing into.
+ */
+ virtual void startPeriodTransition(nsecs_t period) = 0;
+
+ /*
+ * Tells the tracker to stop using present fences to get a vsync signal.
+ *
+ * \param [in] ignore Whether to ignore the present fences or not
+ */
+ virtual void setIgnorePresentFences(bool ignore) = 0;
+
+ virtual void dump(std::string& result) const = 0;
+
+protected:
+ VsyncController() = default;
+ VsyncController(VsyncController const&) = delete;
+ VsyncController& operator=(VsyncController const&) = delete;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.cpp b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
new file mode 100644
index 0000000..194d808
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2019 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#undef LOG_TAG
+#define LOG_TAG "VsyncModulator"
+
+#include "VsyncModulator.h"
+
+#include <android-base/properties.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+#include <chrono>
+#include <cinttypes>
+#include <mutex>
+
+using namespace std::chrono_literals;
+
+namespace android::scheduler {
+
+const std::chrono::nanoseconds VsyncModulator::MIN_EARLY_TRANSACTION_TIME = 1ms;
+
+VsyncModulator::VsyncModulator(const VsyncConfigSet& config, Now now)
+ : mVsyncConfigSet(config),
+ mNow(now),
+ mTraceDetailedInfo(base::GetBoolProperty("debug.sf.vsync_trace_detailed_info", false)) {}
+
+VsyncModulator::VsyncConfig VsyncModulator::setVsyncConfigSet(const VsyncConfigSet& config) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mVsyncConfigSet = config;
+ return updateVsyncConfigLocked();
+}
+
+VsyncModulator::VsyncConfigOpt VsyncModulator::setTransactionSchedule(
+ TransactionSchedule schedule) {
+ switch (schedule) {
+ case Schedule::EarlyStart:
+ ALOGW_IF(mEarlyWakeup, "%s: Duplicate EarlyStart", __FUNCTION__);
+ mEarlyWakeup = true;
+ break;
+ case Schedule::EarlyEnd:
+ ALOGW_IF(!mEarlyWakeup, "%s: Unexpected EarlyEnd", __FUNCTION__);
+ mEarlyWakeup = false;
+ break;
+ case Schedule::Late:
+ // No change to mEarlyWakeup for non-explicit states.
+ break;
+ }
+
+ if (mTraceDetailedInfo) {
+ ATRACE_INT("mEarlyWakeup", mEarlyWakeup);
+ }
+
+ if (!mEarlyWakeup && schedule == Schedule::EarlyEnd) {
+ mEarlyTransactionFrames = MIN_EARLY_TRANSACTION_FRAMES;
+ mEarlyTransactionStartTime = mNow();
+ }
+
+ // An early transaction stays an early transaction.
+ if (schedule == mTransactionSchedule || mTransactionSchedule == Schedule::EarlyEnd) {
+ return std::nullopt;
+ }
+ mTransactionSchedule = schedule;
+ return updateVsyncConfig();
+}
+
+VsyncModulator::VsyncConfigOpt VsyncModulator::onTransactionCommit() {
+ mLastTransactionCommitTime = mNow();
+ if (mTransactionSchedule == Schedule::Late) return std::nullopt;
+ mTransactionSchedule = Schedule::Late;
+ return updateVsyncConfig();
+}
+
+VsyncModulator::VsyncConfigOpt VsyncModulator::onRefreshRateChangeInitiated() {
+ if (mRefreshRateChangePending) return std::nullopt;
+ mRefreshRateChangePending = true;
+ return updateVsyncConfig();
+}
+
+VsyncModulator::VsyncConfigOpt VsyncModulator::onRefreshRateChangeCompleted() {
+ if (!mRefreshRateChangePending) return std::nullopt;
+ mRefreshRateChangePending = false;
+ return updateVsyncConfig();
+}
+
+VsyncModulator::VsyncConfigOpt VsyncModulator::onDisplayRefresh(bool usedGpuComposition) {
+ bool updateOffsetsNeeded = false;
+
+ if (mEarlyTransactionStartTime.load() + MIN_EARLY_TRANSACTION_TIME <=
+ mLastTransactionCommitTime.load()) {
+ if (mEarlyTransactionFrames > 0) {
+ mEarlyTransactionFrames--;
+ updateOffsetsNeeded = true;
+ }
+ }
+ if (usedGpuComposition) {
+ mEarlyGpuFrames = MIN_EARLY_GPU_FRAMES;
+ updateOffsetsNeeded = true;
+ } else if (mEarlyGpuFrames > 0) {
+ mEarlyGpuFrames--;
+ updateOffsetsNeeded = true;
+ }
+
+ if (!updateOffsetsNeeded) return std::nullopt;
+ return updateVsyncConfig();
+}
+
+VsyncModulator::VsyncConfig VsyncModulator::getVsyncConfig() const {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mVsyncConfig;
+}
+
+const VsyncModulator::VsyncConfig& VsyncModulator::getNextVsyncConfig() const {
+ // Early offsets are used if we're in the middle of a refresh rate
+ // change, or if we recently begin a transaction.
+ if (mEarlyWakeup || mTransactionSchedule == Schedule::EarlyEnd || mEarlyTransactionFrames > 0 ||
+ mRefreshRateChangePending) {
+ return mVsyncConfigSet.early;
+ } else if (mEarlyGpuFrames > 0) {
+ return mVsyncConfigSet.earlyGpu;
+ } else {
+ return mVsyncConfigSet.late;
+ }
+}
+
+VsyncModulator::VsyncConfig VsyncModulator::updateVsyncConfig() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return updateVsyncConfigLocked();
+}
+
+VsyncModulator::VsyncConfig VsyncModulator::updateVsyncConfigLocked() {
+ const VsyncConfig& offsets = getNextVsyncConfig();
+ mVsyncConfig = offsets;
+
+ if (mTraceDetailedInfo) {
+ const bool isEarly = &offsets == &mVsyncConfigSet.early;
+ const bool isEarlyGpu = &offsets == &mVsyncConfigSet.earlyGpu;
+ const bool isLate = &offsets == &mVsyncConfigSet.late;
+
+ ATRACE_INT("Vsync-EarlyOffsetsOn", isEarly);
+ ATRACE_INT("Vsync-EarlyGpuOffsetsOn", isEarlyGpu);
+ ATRACE_INT("Vsync-LateOffsetsOn", isLate);
+ }
+
+ return offsets;
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.h b/services/surfaceflinger/Scheduler/VsyncModulator.h
new file mode 100644
index 0000000..fcde279
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#pragma once
+
+#include <chrono>
+#include <mutex>
+#include <optional>
+
+#include <android-base/thread_annotations.h>
+#include <utils/Timers.h>
+
+namespace android::scheduler {
+
+// State machine controlled by transaction flags. VsyncModulator switches to early phase offsets
+// when a transaction is flagged EarlyStart or Early, lasting until an EarlyEnd transaction or a
+// fixed number of frames, respectively.
+enum class TransactionSchedule {
+ Late, // Default.
+ EarlyStart,
+ EarlyEnd
+};
+
+// Modulates VSYNC phase depending on transaction schedule and refresh rate changes.
+class VsyncModulator {
+public:
+ // Number of frames to keep early offsets after an early transaction or GPU composition.
+ // This acts as a low-pass filter in case subsequent transactions are delayed, or if the
+ // composition strategy alternates on subsequent frames.
+ static constexpr int MIN_EARLY_TRANSACTION_FRAMES = 2;
+ static constexpr int MIN_EARLY_GPU_FRAMES = 2;
+
+ // Duration to delay the MIN_EARLY_TRANSACTION_FRAMES countdown after an early transaction.
+ // This may keep early offsets for an extra frame, but avoids a race with transaction commit.
+ static const std::chrono::nanoseconds MIN_EARLY_TRANSACTION_TIME;
+
+ // Phase offsets and work durations for SF and app deadlines from VSYNC.
+ struct VsyncConfig {
+ nsecs_t sfOffset;
+ nsecs_t appOffset;
+ std::chrono::nanoseconds sfWorkDuration;
+ std::chrono::nanoseconds appWorkDuration;
+
+ bool operator==(const VsyncConfig& other) const {
+ return sfOffset == other.sfOffset && appOffset == other.appOffset &&
+ sfWorkDuration == other.sfWorkDuration &&
+ appWorkDuration == other.appWorkDuration;
+ }
+
+ bool operator!=(const VsyncConfig& other) const { return !(*this == other); }
+ };
+
+ using VsyncConfigOpt = std::optional<VsyncConfig>;
+
+ struct VsyncConfigSet {
+ VsyncConfig early; // Used for early transactions, and during refresh rate change.
+ VsyncConfig earlyGpu; // Used during GPU composition.
+ VsyncConfig late; // Default.
+
+ bool operator==(const VsyncConfigSet& other) const {
+ return early == other.early && earlyGpu == other.earlyGpu && late == other.late;
+ }
+
+ bool operator!=(const VsyncConfigSet& other) const { return !(*this == other); }
+ };
+
+ using Clock = std::chrono::steady_clock;
+ using TimePoint = Clock::time_point;
+ using Now = TimePoint (*)();
+
+ explicit VsyncModulator(const VsyncConfigSet&, Now = Clock::now);
+
+ VsyncConfig getVsyncConfig() const EXCLUDES(mMutex);
+
+ [[nodiscard]] VsyncConfig setVsyncConfigSet(const VsyncConfigSet&) EXCLUDES(mMutex);
+
+ // Changes offsets in response to transaction flags or commit.
+ [[nodiscard]] VsyncConfigOpt setTransactionSchedule(TransactionSchedule);
+ [[nodiscard]] VsyncConfigOpt onTransactionCommit();
+
+ // Called when we send a refresh rate change to hardware composer, so that
+ // we can move into early offsets.
+ [[nodiscard]] VsyncConfigOpt onRefreshRateChangeInitiated();
+
+ // Called when we detect from VSYNC signals that the refresh rate changed.
+ // This way we can move out of early offsets if no longer necessary.
+ [[nodiscard]] VsyncConfigOpt onRefreshRateChangeCompleted();
+
+ [[nodiscard]] VsyncConfigOpt onDisplayRefresh(bool usedGpuComposition);
+
+private:
+ const VsyncConfig& getNextVsyncConfig() const REQUIRES(mMutex);
+ [[nodiscard]] VsyncConfig updateVsyncConfig() EXCLUDES(mMutex);
+ [[nodiscard]] VsyncConfig updateVsyncConfigLocked() REQUIRES(mMutex);
+
+ mutable std::mutex mMutex;
+ VsyncConfigSet mVsyncConfigSet GUARDED_BY(mMutex);
+
+ VsyncConfig mVsyncConfig GUARDED_BY(mMutex){mVsyncConfigSet.late};
+
+ using Schedule = TransactionSchedule;
+ std::atomic<Schedule> mTransactionSchedule = Schedule::Late;
+ std::atomic<bool> mEarlyWakeup = false;
+
+ std::atomic<bool> mRefreshRateChangePending = false;
+
+ std::atomic<int> mEarlyTransactionFrames = 0;
+ std::atomic<int> mEarlyGpuFrames = 0;
+ std::atomic<TimePoint> mEarlyTransactionStartTime = TimePoint();
+ std::atomic<TimePoint> mLastTransactionCommitTime = TimePoint();
+
+ const Now mNow;
+ const bool mTraceDetailedInfo;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 4698ac6..b048682 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -17,19 +17,23 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
//#define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "SurfaceFlinger.h"
+#include "TraceUtils.h"
#include <android-base/properties.h>
#include <android/configuration.h>
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/types.h>
-#include <android/hardware/power/1.0/IPower.h>
+#include <android/hardware/power/Boost.h>
#include <android/native_window.h>
+#include <android/os/BnSetInputWindowsListener.h>
+#include <android/os/IInputFlinger.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/PermissionCache.h>
@@ -45,11 +49,9 @@
#include <configstore/Utils.h>
#include <cutils/compiler.h>
#include <cutils/properties.h>
-#include <dlfcn.h>
-#include <errno.h>
+#include <ftl/future.h>
#include <gui/BufferQueue.h>
#include <gui/DebugEGLImageTracker.h>
-#include <gui/GuiConfig.h>
#include <gui/IDisplayEventConnection.h>
#include <gui/IProducerListener.h>
#include <gui/LayerDebugInfo.h>
@@ -57,37 +59,37 @@
#include <gui/LayerState.h>
#include <gui/Surface.h>
#include <hidl/ServiceManagement.h>
-#include <input/IInputFlinger.h>
#include <layerproto/LayerProtoParser.h>
#include <log/log.h>
#include <private/android_filesystem_config.h>
#include <private/gui/SyncFeatures.h>
#include <renderengine/RenderEngine.h>
-#include <statslog.h>
#include <sys/types.h>
#include <ui/ColorSpace.h>
#include <ui/DebugUtils.h>
-#include <ui/DisplayConfig.h>
-#include <ui/DisplayInfo.h>
+#include <ui/DisplayId.h>
+#include <ui/DisplayMode.h>
#include <ui/DisplayStatInfo.h>
#include <ui/DisplayState.h>
+#include <ui/DynamicDisplayInfo.h>
#include <ui/GraphicBufferAllocator.h>
#include <ui/PixelFormat.h>
-#include <ui/UiConfig.h>
+#include <ui/StaticDisplayInfo.h>
#include <utils/StopWatch.h>
#include <utils/String16.h>
#include <utils/String8.h>
#include <utils/Timers.h>
-#include <utils/Trace.h>
#include <utils/misc.h>
#include <algorithm>
+#include <cerrno>
#include <cinttypes>
#include <cmath>
#include <cstdint>
#include <functional>
#include <mutex>
#include <optional>
+#include <type_traits>
#include <unordered_map>
#include "BufferLayer.h"
@@ -101,31 +103,36 @@
#include "DisplayHardware/DisplayIdentification.h"
#include "DisplayHardware/FramebufferSurface.h"
#include "DisplayHardware/HWComposer.h"
+#include "DisplayHardware/Hal.h"
#include "DisplayHardware/VirtualDisplaySurface.h"
+#include "DisplayRenderArea.h"
#include "EffectLayer.h"
#include "Effects/Daltonizer.h"
+#include "FpsReporter.h"
+#include "FrameTimeline/FrameTimeline.h"
#include "FrameTracer/FrameTracer.h"
+#include "HdrLayerInfoReporter.h"
#include "Layer.h"
+#include "LayerRenderArea.h"
#include "LayerVector.h"
#include "MonitoredProducer.h"
#include "NativeWindowSurface.h"
-#include "Promise.h"
#include "RefreshRateOverlay.h"
#include "RegionSamplingThread.h"
-#include "Scheduler/DispSync.h"
#include "Scheduler/DispSyncSource.h"
-#include "Scheduler/EventControlThread.h"
#include "Scheduler/EventThread.h"
#include "Scheduler/LayerHistory.h"
#include "Scheduler/MessageQueue.h"
-#include "Scheduler/PhaseOffsets.h"
#include "Scheduler/Scheduler.h"
+#include "Scheduler/VsyncConfiguration.h"
+#include "Scheduler/VsyncController.h"
#include "StartPropertySetThread.h"
#include "SurfaceFlingerProperties.h"
#include "SurfaceInterceptor.h"
#include "TimeStats/TimeStats.h"
#include "android-base/parseint.h"
#include "android-base/stringprintf.h"
+#include "android-base/strings.h"
#define MAIN_THREAD ACQUIRE(mStateLock) RELEASE(mStateLock)
@@ -148,12 +155,11 @@
using namespace android::hardware::configstore::V1_0;
using namespace android::sysprop;
-using android::hardware::power::V1_0::PowerHint;
+using android::hardware::power::Boost;
using base::StringAppendF;
using ui::ColorMode;
using ui::Dataspace;
using ui::DisplayPrimaries;
-using ui::Hdr;
using ui::RenderIntent;
namespace hal = android::hardware::graphics::composer::hal;
@@ -224,7 +230,7 @@
~UnnecessaryLock() RELEASE() {}
};
-// TODO(b/141333600): Consolidate with HWC2::Display::Config::Builder::getDefaultDensity.
+// TODO(b/141333600): Consolidate with DisplayMode::Builder::getDefaultDensity.
constexpr float FALLBACK_DENSITY = ACONFIGURATION_DENSITY_TV;
float getDensityFromProperty(const char* property, bool required) {
@@ -251,13 +257,35 @@
std::function<void()> mCallback;
};
+enum Permission {
+ ACCESS_SURFACE_FLINGER = 0x1,
+ ROTATE_SURFACE_FLINGER = 0x2,
+};
+
} // namespace anonymous
+struct SetInputWindowsListener : os::BnSetInputWindowsListener {
+ explicit SetInputWindowsListener(std::function<void()> listenerCb) : mListenerCb(listenerCb) {}
+
+ binder::Status onSetInputWindowsFinished() override;
+
+ std::function<void()> mListenerCb;
+};
+
+binder::Status SetInputWindowsListener::onSetInputWindowsFinished() {
+ if (mListenerCb != nullptr) {
+ mListenerCb();
+ }
+ return binder::Status::ok();
+}
+
// ---------------------------------------------------------------------------
const String16 sHardwareTest("android.permission.HARDWARE_TEST");
const String16 sAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER");
+const String16 sRotateSurfaceFlinger("android.permission.ROTATE_SURFACE_FLINGER");
const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
+const String16 sControlDisplayBrightness("android.permission.CONTROL_DISPLAY_BRIGHTNESS");
const String16 sDump("android.permission.DUMP");
const char* KERNEL_IDLE_TIMER_PROP = "graphics.display.kernel_idle_timer.enabled";
@@ -279,20 +307,6 @@
ui::PixelFormat SurfaceFlinger::wideColorGamutCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
bool SurfaceFlinger::useFrameRateApi;
-std::string getHwcServiceName() {
- char value[PROPERTY_VALUE_MAX] = {};
- property_get("debug.sf.hwc_service_name", value, "default");
- ALOGI("Using HWComposer service: '%s'", value);
- return std::string(value);
-}
-
-bool useTrebleTestingOverride() {
- char value[PROPERTY_VALUE_MAX] = {};
- property_get("debug.sf.treble_testing_override", value, "false");
- ALOGI("Treble testing override: '%s'", value);
- return std::string(value) == "true";
-}
-
std::string decodeDisplayColorSetting(DisplayColorSetting displayColorSetting) {
switch(displayColorSetting) {
case DisplayColorSetting::kManaged:
@@ -307,17 +321,29 @@
}
}
-SurfaceFlingerBE::SurfaceFlingerBE() : mHwcServiceName(getHwcServiceName()) {}
+bool callingThreadHasRotateSurfaceFlingerAccess() {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+ return uid == AID_GRAPHICS || uid == AID_SYSTEM ||
+ PermissionCache::checkPermission(sRotateSurfaceFlinger, pid, uid);
+}
SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag)
: mFactory(factory),
- mInterceptor(mFactory.createSurfaceInterceptor(this)),
+ mInterceptor(mFactory.createSurfaceInterceptor()),
mTimeStats(std::make_shared<impl::TimeStats>()),
- mFrameTracer(std::make_unique<FrameTracer>()),
+ mFrameTracer(mFactory.createFrameTracer()),
+ mFrameTimeline(mFactory.createFrameTimeline(mTimeStats, getpid())),
mEventQueue(mFactory.createMessageQueue()),
mCompositionEngine(mFactory.createCompositionEngine()),
+ mHwcServiceName(base::GetProperty("debug.sf.hwc_service_name"s, "default"s)),
mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)),
- mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)) {}
+ mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)) {
+ ALOGI("Using HWComposer service: %s", mHwcServiceName.c_str());
+
+ mSetInputWindowsListener = new SetInputWindowsListener([&]() { setInputWindowsFinished(); });
+}
SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipInitialization) {
ALOGI("SurfaceFlinger is starting");
@@ -337,7 +363,9 @@
hasWideColorDisplay = has_wide_color_display(false);
- useColorManagement = use_color_management(false);
+ // Android 12 and beyond, color management in display pipeline is turned on
+ // by default.
+ useColorManagement = use_color_management(true);
mDefaultCompositionDataspace =
static_cast<ui::Dataspace>(default_composition_dataspace(Dataspace::V0_SRGB));
@@ -392,10 +420,6 @@
int debugDdms = atoi(value);
ALOGI_IF(debugDdms, "DDMS debugging not supported");
- property_get("debug.sf.disable_backpressure", value, "0");
- mPropagateBackpressure = !atoi(value);
- ALOGI_IF(!mPropagateBackpressure, "Disabling backpressure propagation");
-
property_get("debug.sf.enable_gl_backpressure", value, "0");
mPropagateBackpressureClientComposition = atoi(value);
ALOGI_IF(mPropagateBackpressureClientComposition,
@@ -405,10 +429,6 @@
mUseHwcVirtualDisplays = atoi(value);
ALOGI_IF(mUseHwcVirtualDisplays, "Enabling HWC virtual displays");
- property_get("ro.sf.disable_triple_buffer", value, "0");
- mLayerTripleBufferingDisabled = atoi(value);
- ALOGI_IF(mLayerTripleBufferingDisabled, "Disabling Triple Buffering");
-
property_get("ro.surface_flinger.supports_background_blur", value, "0");
bool supportsBlurs = atoi(value);
mSupportsBlur = supportsBlurs;
@@ -419,6 +439,10 @@
const size_t defaultListSize = ISurfaceComposer::MAX_LAYERS;
auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize));
mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize;
+ mGraphicBufferProducerListSizeLogThreshold =
+ std::max(static_cast<int>(0.95 *
+ static_cast<double>(mMaxGraphicBufferProducerListSize)),
+ 1);
property_get("debug.sf.luma_sampling", value, "1");
mLumaSampling = atoi(value);
@@ -431,12 +455,13 @@
// comes online to attempt to read the property. The property is
// instead read after the boot animation
- if (useTrebleTestingOverride()) {
+ if (base::GetBoolProperty("debug.sf.treble_testing_override"s, false)) {
// Without the override SurfaceFlinger cannot connect to HIDL
// services that are not listed in the manifests. Considered
// deriving the setting from the set service name, but it
// would be brittle if the name that's not 'default' is used
// for production purposes later on.
+ ALOGI("Enabling Treble testing override");
android::hardware::details::setTrebleTestingOverride(true);
}
@@ -444,6 +469,8 @@
mKernelIdleTimerEnabled = mSupportKernelIdleTimer = sysprop::support_kernel_idle_timer(false);
base::SetProperty(KERNEL_IDLE_TIMER_PROP, mKernelIdleTimerEnabled ? "true" : "false");
+
+ mRefreshRateOverlaySpinner = property_get_bool("sf.debug.show_refresh_rate_overlay_spinner", 0);
}
SurfaceFlinger::~SurfaceFlinger() = default;
@@ -456,6 +483,9 @@
// the window manager died on us. prepare its eulogy.
mBootFinished = false;
+ // Sever the link to inputflinger since its gone as well.
+ static_cast<void>(schedule([=] { mInputFlinger = nullptr; }));
+
// restore initial conditions (default device unblank, etc)
initializeDisplays();
@@ -482,6 +512,15 @@
}
sp<IBinder> SurfaceFlinger::createDisplay(const String8& displayName, bool secure) {
+ // onTransact already checks for some permissions, but adding an additional check here.
+ // This is to ensure that only system and graphics can request to create a secure
+ // display. Secure displays can show secure content so we add an additional restriction on it.
+ const int uid = IPCThreadState::self()->getCallingUid();
+ if (secure && uid != AID_GRAPHICS && uid != AID_SYSTEM) {
+ ALOGE("Only privileged processes can create a secure display");
+ return nullptr;
+ }
+
class DisplayToken : public BBinder {
sp<SurfaceFlinger> flinger;
virtual ~DisplayToken() {
@@ -537,11 +576,11 @@
std::vector<PhysicalDisplayId> displayIds;
displayIds.reserve(mPhysicalDisplayTokens.size());
- displayIds.push_back(internalDisplayId->value);
+ displayIds.push_back(*internalDisplayId);
for (const auto& [id, token] : mPhysicalDisplayTokens) {
if (id != *internalDisplayId) {
- displayIds.push_back(id.value);
+ displayIds.push_back(id);
}
}
@@ -550,7 +589,7 @@
sp<IBinder> SurfaceFlinger::getPhysicalDisplayToken(PhysicalDisplayId displayId) const {
Mutex::Autolock lock(mStateLock);
- return getPhysicalDisplayTokenLocked(DisplayId{displayId});
+ return getPhysicalDisplayTokenLocked(displayId);
}
status_t SurfaceFlinger::getColorManagement(bool* outGetColorManagement) const {
@@ -573,8 +612,7 @@
return *mCompositionEngine.get();
}
-void SurfaceFlinger::bootFinished()
-{
+void SurfaceFlinger::bootFinished() {
if (mBootFinished == true) {
ALOGE("Extra call to bootFinished");
return;
@@ -588,7 +626,7 @@
ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
mFrameTracer->initialize();
- mTimeStats->onBootFinished();
+ mFrameTimeline->onBootFinished();
// wait patiently for the window manager death
const String16 name("window");
@@ -612,7 +650,7 @@
if (input == nullptr) {
ALOGE("Failed to link to input service");
} else {
- mInputFlinger = interface_cast<IInputFlinger>(input);
+ mInputFlinger = interface_cast<os::IInputFlinger>(input);
}
readPersistentProperties();
@@ -641,12 +679,16 @@
// The pool was empty, so we need to get a new texture name directly using a
// blocking call to the main thread
- return schedule([this] {
+ auto genTextures = [this] {
uint32_t name = 0;
getRenderEngine().genTextures(1, &name);
return name;
- })
- .get();
+ };
+ if (std::this_thread::get_id() == mMainThreadId) {
+ return genTextures();
+ } else {
+ return schedule(genTextures).get();
+ }
}
void SurfaceFlinger::deleteTextureAsync(uint32_t texture) {
@@ -669,24 +711,27 @@
// Sending maxFrameBufferAcquiredBuffers as the cache size is tightly tuned to single-display.
mCompositionEngine->setRenderEngine(renderengine::RenderEngine::create(
renderengine::RenderEngineCreationArgs::Builder()
- .setPixelFormat(static_cast<int32_t>(defaultCompositionPixelFormat))
- .setImageCacheSize(maxFrameBufferAcquiredBuffers)
- .setUseColorManagerment(useColorManagement)
- .setEnableProtectedContext(enable_protected_contents(false))
- .setPrecacheToneMapperShaderOnly(false)
- .setSupportsBackgroundBlur(mSupportsBlur)
- .setContextPriority(useContextPriority
- ? renderengine::RenderEngine::ContextPriority::HIGH
- : renderengine::RenderEngine::ContextPriority::MEDIUM)
- .build()));
+ .setPixelFormat(static_cast<int32_t>(defaultCompositionPixelFormat))
+ .setImageCacheSize(maxFrameBufferAcquiredBuffers)
+ .setUseColorManagerment(useColorManagement)
+ .setEnableProtectedContext(enable_protected_contents(false))
+ .setPrecacheToneMapperShaderOnly(false)
+ .setSupportsBackgroundBlur(mSupportsBlur)
+ .setContextPriority(
+ useContextPriority
+ ? renderengine::RenderEngine::ContextPriority::REALTIME
+ : renderengine::RenderEngine::ContextPriority::MEDIUM)
+ .build()));
mCompositionEngine->setTimeStats(mTimeStats);
- mCompositionEngine->setHwComposer(getFactory().createHWComposer(getBE().mHwcServiceName));
+ mCompositionEngine->setHwComposer(getFactory().createHWComposer(mHwcServiceName));
mCompositionEngine->getHwComposer().setConfiguration(this, getBE().mComposerSequenceId);
+ ClientCache::getInstance().setRenderEngine(&getRenderEngine());
// Process any initial hotplug and resulting display changes.
processDisplayHotplugEventsLocked();
const auto display = getDefaultDisplayDeviceLocked();
LOG_ALWAYS_FATAL_IF(!display, "Missing internal display after registering composer callback.");
- LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(*display->getId()),
+ const auto displayId = display->getPhysicalId();
+ LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(displayId),
"Internal display is disconnected.");
// initialize our drawing state
@@ -701,6 +746,8 @@
getRenderEngine().primeCache();
}
+ getRenderEngine().onPrimaryDisplaySizeChanged(display->getSize());
+
// Inform native graphics APIs whether the present timestamp is supported:
const bool presentFenceReliable =
@@ -803,13 +850,15 @@
state->layerStack = display->getLayerStack();
state->orientation = display->getOrientation();
- const Rect viewport = display->getViewport();
- state->viewport = viewport.isValid() ? viewport.getSize() : display->getSize();
+ const Rect layerStackRect = display->getLayerStackSpaceRect();
+ state->layerStackSpaceRect =
+ layerStackRect.isValid() ? layerStackRect.getSize() : display->getSize();
return NO_ERROR;
}
-status_t SurfaceFlinger::getDisplayInfo(const sp<IBinder>& displayToken, DisplayInfo* info) {
+status_t SurfaceFlinger::getStaticDisplayInfo(const sp<IBinder>& displayToken,
+ ui::StaticDisplayInfo* info) {
if (!displayToken || !info) {
return BAD_VALUE;
}
@@ -830,68 +879,71 @@
if (mEmulatedDisplayDensity) {
info->density = mEmulatedDisplayDensity;
} else {
- info->density = info->connectionType == DisplayConnectionType::Internal
+ info->density = info->connectionType == ui::DisplayConnectionType::Internal
? mInternalDisplayDensity
: FALLBACK_DENSITY;
}
info->density /= ACONFIGURATION_DENSITY_MEDIUM;
info->secure = display->isSecure();
- info->deviceProductInfo = getDeviceProductInfoLocked(*display);
+ info->deviceProductInfo = display->getDeviceProductInfo();
return NO_ERROR;
}
-status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& displayToken,
- Vector<DisplayConfig>* configs) {
- if (!displayToken || !configs) {
+status_t SurfaceFlinger::getDynamicDisplayInfo(const sp<IBinder>& displayToken,
+ ui::DynamicDisplayInfo* info) {
+ if (!displayToken || !info) {
return BAD_VALUE;
}
Mutex::Autolock lock(mStateLock);
- const auto displayId = getPhysicalDisplayIdLocked(displayToken);
- if (!displayId) {
+ const auto display = getDisplayDeviceLocked(displayToken);
+ if (!display) {
return NAME_NOT_FOUND;
}
- const bool isInternal = (displayId == getInternalDisplayIdLocked());
+ info->activeDisplayModeId = static_cast<int32_t>(display->getActiveMode()->getId().value());
- configs->clear();
+ const auto& supportedModes = display->getSupportedModes();
+ info->supportedDisplayModes.clear();
+ info->supportedDisplayModes.reserve(supportedModes.size());
+ for (const auto& mode : supportedModes) {
+ ui::DisplayMode outMode;
+ outMode.id = static_cast<int32_t>(mode->getId().value());
- for (const auto& hwConfig : getHwComposer().getConfigs(*displayId)) {
- DisplayConfig config;
+ auto width = mode->getWidth();
+ auto height = mode->getHeight();
- auto width = hwConfig->getWidth();
- auto height = hwConfig->getHeight();
+ auto xDpi = mode->getDpiX();
+ auto yDpi = mode->getDpiY();
- auto xDpi = hwConfig->getDpiX();
- auto yDpi = hwConfig->getDpiY();
-
- if (isInternal &&
+ if (display->isPrimary() &&
(internalDisplayOrientation == ui::ROTATION_90 ||
internalDisplayOrientation == ui::ROTATION_270)) {
std::swap(width, height);
std::swap(xDpi, yDpi);
}
- config.resolution = ui::Size(width, height);
+ outMode.resolution = ui::Size(width, height);
if (mEmulatedDisplayDensity) {
- config.xDpi = mEmulatedDisplayDensity;
- config.yDpi = mEmulatedDisplayDensity;
+ outMode.xDpi = mEmulatedDisplayDensity;
+ outMode.yDpi = mEmulatedDisplayDensity;
} else {
- config.xDpi = xDpi;
- config.yDpi = yDpi;
+ outMode.xDpi = xDpi;
+ outMode.yDpi = yDpi;
}
- const nsecs_t period = hwConfig->getVsyncPeriod();
- config.refreshRate = 1e9f / period;
+ const nsecs_t period = mode->getVsyncPeriod();
+ outMode.refreshRate = Fps::fromPeriodNsecs(period).getValue();
- const auto offsets = mPhaseConfiguration->getOffsetsForRefreshRate(config.refreshRate);
- config.appVsyncOffset = offsets.late.app;
- config.sfVsyncOffset = offsets.late.sf;
- config.configGroup = hwConfig->getConfigGroup();
+ const auto vsyncConfigSet =
+ mVsyncConfiguration->getConfigsForRefreshRate(Fps(outMode.refreshRate));
+ outMode.appVsyncOffset = vsyncConfigSet.late.appOffset;
+ outMode.sfVsyncOffset = vsyncConfigSet.late.sfOffset;
+ outMode.group = mode->getGroup();
// This is how far in advance a buffer must be queued for
// presentation at a given time. If you want a buffer to appear
@@ -900,16 +952,29 @@
//
// Normally it's one full refresh period (to give SF a chance to
// latch the buffer), but this can be reduced by configuring a
- // DispSync offset. Any additional delays introduced by the hardware
+ // VsyncController offset. Any additional delays introduced by the hardware
// composer or panel must be accounted for here.
//
// We add an additional 1ms to allow for processing time and
// differences between the ideal and actual refresh rate.
- config.presentationDeadline = period - config.sfVsyncOffset + 1000000;
+ outMode.presentationDeadline = period - outMode.sfVsyncOffset + 1000000;
- configs->push_back(config);
+ info->supportedDisplayModes.push_back(outMode);
}
+ info->activeColorMode = display->getCompositionDisplay()->getState().colorMode;
+ const auto displayId = display->getPhysicalId();
+ info->supportedColorModes = getDisplayColorModes(displayId);
+
+ info->hdrCapabilities = display->getHdrCapabilities();
+ info->autoLowLatencyModeSupported =
+ getHwComposer().hasDisplayCapability(displayId,
+ hal::DisplayCapability::AUTO_LOW_LATENCY_MODE);
+ std::vector<hal::ContentType> types;
+ getHwComposer().getSupportedContentTypes(displayId, &types);
+ info->gameContentTypeSupported = std::any_of(types.begin(), types.end(), [](auto type) {
+ return type == hal::ContentType::GAME;
+ });
return NO_ERROR;
}
@@ -918,57 +983,31 @@
return BAD_VALUE;
}
- mScheduler->getDisplayStatInfo(stats);
+ *stats = mScheduler->getDisplayStatInfo(systemTime());
return NO_ERROR;
}
-int SurfaceFlinger::getActiveConfig(const sp<IBinder>& displayToken) {
- int activeConfig;
- bool isPrimary;
-
- {
- Mutex::Autolock lock(mStateLock);
-
- if (const auto display = getDisplayDeviceLocked(displayToken)) {
- activeConfig = display->getActiveConfig().value();
- isPrimary = display->isPrimary();
- } else {
- ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
- return NAME_NOT_FOUND;
- }
- }
-
- if (isPrimary) {
- if (const auto config = getDesiredActiveConfig()) {
- return config->configId.value();
- }
- }
-
- return activeConfig;
-}
-
-void SurfaceFlinger::setDesiredActiveConfig(const ActiveConfigInfo& info) {
+void SurfaceFlinger::setDesiredActiveMode(const ActiveModeInfo& info) {
ATRACE_CALL();
- auto& refreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId(info.configId);
- ALOGV("setDesiredActiveConfig(%s)", refreshRate.getName().c_str());
+ auto refreshRate = mRefreshRateConfigs->getRefreshRateFromModeId(info.modeId);
+ ALOGV("%s(%s)", __func__, refreshRate.getName().c_str());
- std::lock_guard<std::mutex> lock(mActiveConfigLock);
- if (mDesiredActiveConfigChanged) {
- // If a config change is pending, just cache the latest request in
- // mDesiredActiveConfig
- const Scheduler::ConfigEvent prevConfig = mDesiredActiveConfig.event;
- mDesiredActiveConfig = info;
- mDesiredActiveConfig.event = mDesiredActiveConfig.event | prevConfig;
+ std::lock_guard<std::mutex> lock(mActiveModeLock);
+ if (mDesiredActiveModeChanged) {
+ // If a mode change is pending, just cache the latest request in mDesiredActiveMode
+ const Scheduler::ModeEvent prevConfig = mDesiredActiveMode.event;
+ mDesiredActiveMode = info;
+ mDesiredActiveMode.event = mDesiredActiveMode.event | prevConfig;
} else {
- // Check is we are already at the desired config
+ // Check if we are already at the desired mode
const auto display = getDefaultDisplayDeviceLocked();
- if (!display || display->getActiveConfig() == refreshRate.getConfigId()) {
+ if (!display || display->getActiveMode()->getId() == refreshRate.getModeId()) {
return;
}
- // Initiate a config change.
- mDesiredActiveConfigChanged = true;
- mDesiredActiveConfig = info;
+ // Initiate a mode change.
+ mDesiredActiveModeChanged = true;
+ mDesiredActiveMode = info;
// This will trigger HWC refresh without resetting the idle timer.
repaintEverythingForHWC();
@@ -976,20 +1015,19 @@
// switch.
mScheduler->resyncToHardwareVsync(true, refreshRate.getVsyncPeriod());
// As we called to set period, we will call to onRefreshRateChangeCompleted once
- // DispSync model is locked.
- mVSyncModulator->onRefreshRateChangeInitiated();
+ // VsyncController model is locked.
+ modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated);
- mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps());
- mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
- mScheduler->setConfigChangePending(true);
+ updatePhaseConfiguration(refreshRate.getFps());
+ mScheduler->setModeChangePending(true);
}
if (mRefreshRateOverlay) {
- mRefreshRateOverlay->changeRefreshRate(refreshRate);
+ mRefreshRateOverlay->changeRefreshRate(refreshRate.getFps());
}
}
-status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& displayToken, int mode) {
+status_t SurfaceFlinger::setActiveMode(const sp<IBinder>& displayToken, int modeId) {
ATRACE_CALL();
if (!displayToken) {
@@ -999,26 +1037,38 @@
auto future = schedule([=]() -> status_t {
const auto display = ON_MAIN_THREAD(getDisplayDeviceLocked(displayToken));
if (!display) {
- ALOGE("Attempt to set allowed display configs for invalid display token %p",
+ ALOGE("Attempt to set allowed display modes for invalid display token %p",
displayToken.get());
return NAME_NOT_FOUND;
- } else if (display->isVirtual()) {
- ALOGW("Attempt to set allowed display configs for virtual display");
- return INVALID_OPERATION;
- } else {
- const HwcConfigIndexType config(mode);
- const float fps = mRefreshRateConfigs->getRefreshRateFromConfigId(config).getFps();
- const scheduler::RefreshRateConfigs::Policy policy{config, {fps, fps}};
- constexpr bool kOverridePolicy = false;
-
- return setDesiredDisplayConfigSpecsInternal(display, policy, kOverridePolicy);
}
+
+ if (display->isVirtual()) {
+ ALOGW("Attempt to set allowed display modes for virtual display");
+ return INVALID_OPERATION;
+ }
+
+ const auto mode = display->getMode(DisplayModeId{modeId});
+ if (!mode) {
+ ALOGW("Attempt to switch to an unsupported mode %d.", modeId);
+ return BAD_VALUE;
+ }
+
+ const auto fps = mode->getFps();
+ // Keep the old switching type.
+ const auto allowGroupSwitching =
+ mRefreshRateConfigs->getCurrentPolicy().allowGroupSwitching;
+ const scheduler::RefreshRateConfigs::Policy policy{mode->getId(),
+ allowGroupSwitching,
+ {fps, fps}};
+ constexpr bool kOverridePolicy = false;
+
+ return setDesiredDisplayModeSpecsInternal(display, policy, kOverridePolicy);
});
return future.get();
}
-void SurfaceFlinger::setActiveConfigInternal() {
+void SurfaceFlinger::setActiveModeInternal() {
ATRACE_CALL();
const auto display = getDefaultDisplayDeviceLocked();
@@ -1026,80 +1076,95 @@
return;
}
- auto& oldRefreshRate =
- mRefreshRateConfigs->getRefreshRateFromConfigId(display->getActiveConfig());
+ const auto upcomingMode = display->getMode(mUpcomingActiveMode.modeId);
+ if (!upcomingMode) {
+ ALOGW("Upcoming active mode is no longer supported. Mode ID = %d",
+ mUpcomingActiveMode.modeId.value());
+ // TODO(b/159590486) Handle the error better. Some parts of SurfaceFlinger may
+ // have been already updated with the upcoming active mode.
+ return;
+ }
+ const Fps oldRefreshRate = display->getActiveMode()->getFps();
- std::lock_guard<std::mutex> lock(mActiveConfigLock);
- mRefreshRateConfigs->setCurrentConfigId(mUpcomingActiveConfig.configId);
- mRefreshRateStats->setConfigMode(mUpcomingActiveConfig.configId);
- display->setActiveConfig(mUpcomingActiveConfig.configId);
+ std::lock_guard<std::mutex> lock(mActiveModeLock);
+ mRefreshRateConfigs->setCurrentModeId(mUpcomingActiveMode.modeId);
+ display->setActiveMode(mUpcomingActiveMode.modeId);
- auto& refreshRate =
- mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId);
- if (refreshRate.getVsyncPeriod() != oldRefreshRate.getVsyncPeriod()) {
+ const Fps refreshRate = upcomingMode->getFps();
+
+ mRefreshRateStats->setRefreshRate(refreshRate);
+
+ if (!refreshRate.equalsWithMargin(oldRefreshRate)) {
mTimeStats->incrementRefreshRateSwitches();
}
- mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps());
- mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
- ATRACE_INT("ActiveConfigFPS", refreshRate.getFps());
+ updatePhaseConfiguration(refreshRate);
+ ATRACE_INT("ActiveConfigFPS", refreshRate.getValue());
- if (mUpcomingActiveConfig.event != Scheduler::ConfigEvent::None) {
- const nsecs_t vsyncPeriod =
- mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId)
- .getVsyncPeriod();
- mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, display->getId()->value,
- mUpcomingActiveConfig.configId, vsyncPeriod);
+ if (mUpcomingActiveMode.event != Scheduler::ModeEvent::None) {
+ const nsecs_t vsyncPeriod = refreshRate.getPeriodNsecs();
+ const auto physicalId = display->getPhysicalId();
+ mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, physicalId,
+ mUpcomingActiveMode.modeId, vsyncPeriod);
}
}
-void SurfaceFlinger::desiredActiveConfigChangeDone() {
- std::lock_guard<std::mutex> lock(mActiveConfigLock);
- mDesiredActiveConfig.event = Scheduler::ConfigEvent::None;
- mDesiredActiveConfigChanged = false;
-
- const auto& refreshRate =
- mRefreshRateConfigs->getRefreshRateFromConfigId(mDesiredActiveConfig.configId);
- mScheduler->resyncToHardwareVsync(true, refreshRate.getVsyncPeriod());
- mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps());
- mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
- mScheduler->setConfigChangePending(false);
+void SurfaceFlinger::clearDesiredActiveModeState() {
+ std::lock_guard<std::mutex> lock(mActiveModeLock);
+ mDesiredActiveMode.event = Scheduler::ModeEvent::None;
+ mDesiredActiveModeChanged = false;
+ mScheduler->setModeChangePending(false);
}
-void SurfaceFlinger::performSetActiveConfig() {
+void SurfaceFlinger::desiredActiveModeChangeDone() {
+ const auto modeId = getDesiredActiveMode()->modeId;
+
+ clearDesiredActiveModeState();
+
+ const auto refreshRate = getDefaultDisplayDeviceLocked()->getMode(modeId)->getFps();
+ mScheduler->resyncToHardwareVsync(true, refreshRate.getPeriodNsecs());
+ updatePhaseConfiguration(refreshRate);
+}
+
+void SurfaceFlinger::performSetActiveMode() {
ATRACE_CALL();
- ALOGV("performSetActiveConfig");
+ ALOGV("%s", __FUNCTION__);
// Store the local variable to release the lock.
- const auto desiredActiveConfig = getDesiredActiveConfig();
- if (!desiredActiveConfig) {
- // No desired active config pending to be applied
+ const auto desiredActiveMode = getDesiredActiveMode();
+ if (!desiredActiveMode) {
+ // No desired active mode pending to be applied
return;
}
- auto& refreshRate =
- mRefreshRateConfigs->getRefreshRateFromConfigId(desiredActiveConfig->configId);
- ALOGV("performSetActiveConfig changing active config to %d(%s)",
- refreshRate.getConfigId().value(), refreshRate.getName().c_str());
const auto display = getDefaultDisplayDeviceLocked();
- if (!display || display->getActiveConfig() == desiredActiveConfig->configId) {
+ const auto desiredMode = display->getMode(desiredActiveMode->modeId);
+ if (!desiredMode) {
+ ALOGW("Desired display mode is no longer supported. Mode ID = %d",
+ desiredActiveMode->modeId.value());
+ clearDesiredActiveModeState();
+ return;
+ }
+ const auto refreshRate = desiredMode->getFps();
+ ALOGV("%s changing active mode to %d(%s)", __FUNCTION__, desiredMode->getId().value(),
+ to_string(refreshRate).c_str());
+
+ if (!display || display->getActiveMode()->getId() == desiredActiveMode->modeId) {
// display is not valid or we are already in the requested mode
// on both cases there is nothing left to do
- desiredActiveConfigChangeDone();
+ desiredActiveModeChangeDone();
return;
}
- // Desired active config was set, it is different than the config currently in use, however
- // allowed configs might have change by the time we process the refresh.
- // Make sure the desired config is still allowed
- if (!isDisplayConfigAllowed(desiredActiveConfig->configId)) {
- desiredActiveConfigChangeDone();
+ // Desired active mode was set, it is different than the mode currently in use, however
+ // allowed modes might have changed by the time we process the refresh.
+ // Make sure the desired mode is still allowed
+ if (!isDisplayModeAllowed(desiredActiveMode->modeId)) {
+ desiredActiveModeChangeDone();
return;
}
- mUpcomingActiveConfig = *desiredActiveConfig;
- const auto displayId = display->getId();
- LOG_ALWAYS_FATAL_IF(!displayId);
+ mUpcomingActiveMode = *desiredActiveMode;
- ATRACE_INT("ActiveConfigFPS_HWC", refreshRate.getFps());
+ ATRACE_INT("ActiveModeFPS_HWC", refreshRate.getValue());
// TODO(b/142753666) use constrains
hal::VsyncPeriodChangeConstraints constraints;
@@ -1107,55 +1172,34 @@
constraints.seamlessRequired = false;
hal::VsyncPeriodChangeTimeline outTimeline;
- auto status =
- getHwComposer().setActiveConfigWithConstraints(*displayId,
- mUpcomingActiveConfig.configId.value(),
- constraints, &outTimeline);
+ const auto status =
+ display->initiateModeChange(mUpcomingActiveMode.modeId, constraints, &outTimeline);
if (status != NO_ERROR) {
- // setActiveConfigWithConstraints may fail if a hotplug event is just about
+ // initiateModeChange may fail if a hotplug event is just about
// to be sent. We just log the error in this case.
- ALOGW("setActiveConfigWithConstraints failed: %d", status);
+ ALOGW("initiateModeChange failed: %d", status);
return;
}
mScheduler->onNewVsyncPeriodChangeTimeline(outTimeline);
// Scheduler will submit an empty frame to HWC if needed.
- mSetActiveConfigPending = true;
+ mSetActiveModePending = true;
}
-status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& displayToken,
- Vector<ColorMode>* outColorModes) {
- if (!displayToken || !outColorModes) {
- return BAD_VALUE;
- }
-
- std::vector<ColorMode> modes;
- bool isInternalDisplay = false;
- {
- ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
-
- const auto displayId = getPhysicalDisplayIdLocked(displayToken);
- if (!displayId) {
- return NAME_NOT_FOUND;
- }
-
- modes = getHwComposer().getColorModes(*displayId);
- isInternalDisplay = displayId == getInternalDisplayIdLocked();
- }
- outColorModes->clear();
+std::vector<ColorMode> SurfaceFlinger::getDisplayColorModes(PhysicalDisplayId displayId) {
+ auto modes = getHwComposer().getColorModes(displayId);
+ bool isInternalDisplay = displayId == getInternalDisplayIdLocked();
// If it's built-in display and the configuration claims it's not wide color capable,
// filter out all wide color modes. The typical reason why this happens is that the
// hardware is not good enough to support GPU composition of wide color, and thus the
// OEMs choose to disable this capability.
if (isInternalDisplay && !hasWideColorDisplay) {
- std::remove_copy_if(modes.cbegin(), modes.cend(), std::back_inserter(*outColorModes),
- isWideColorMode);
- } else {
- std::copy(modes.cbegin(), modes.cend(), std::back_inserter(*outColorModes));
+ const auto newEnd = std::remove_if(modes.begin(), modes.end(), isWideColorMode);
+ modes.erase(newEnd, modes.end());
}
- return NO_ERROR;
+ return modes;
}
status_t SurfaceFlinger::getDisplayNativePrimaries(const sp<IBinder>& displayToken,
@@ -1173,19 +1217,14 @@
return NO_ERROR;
}
-ColorMode SurfaceFlinger::getActiveColorMode(const sp<IBinder>& displayToken) {
- Mutex::Autolock lock(mStateLock);
-
- if (const auto display = getDisplayDeviceLocked(displayToken)) {
- return display->getCompositionDisplay()->getState().colorMode;
- }
- return static_cast<ColorMode>(BAD_VALUE);
-}
-
status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ColorMode mode) {
schedule([=]() MAIN_THREAD {
- Vector<ColorMode> modes;
- getDisplayColorModes(displayToken, &modes);
+ const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+ if (!displayId) {
+ ALOGE("Invalid display token %p", displayToken.get());
+ return;
+ }
+ const auto modes = getDisplayColorModes(*displayId);
bool exists = std::find(std::begin(modes), std::end(modes), mode) != std::end(modes);
if (mode < ColorMode::NATIVE || !exists) {
ALOGE("Attempt to set invalid active color mode %s (%d) for display token %p",
@@ -1210,24 +1249,6 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::getAutoLowLatencyModeSupport(const sp<IBinder>& displayToken,
- bool* outSupport) const {
- if (!displayToken) {
- return BAD_VALUE;
- }
-
- Mutex::Autolock lock(mStateLock);
-
- const auto displayId = getPhysicalDisplayIdLocked(displayToken);
- if (!displayId) {
- return NAME_NOT_FOUND;
- }
- *outSupport =
- getHwComposer().hasDisplayCapability(*displayId,
- hal::DisplayCapability::AUTO_LOW_LATENCY_MODE);
- return NO_ERROR;
-}
-
void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) {
static_cast<void>(schedule([=]() MAIN_THREAD {
if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
@@ -1238,27 +1259,6 @@
}));
}
-status_t SurfaceFlinger::getGameContentTypeSupport(const sp<IBinder>& displayToken,
- bool* outSupport) const {
- if (!displayToken) {
- return BAD_VALUE;
- }
-
- Mutex::Autolock lock(mStateLock);
-
- const auto displayId = getPhysicalDisplayIdLocked(displayToken);
- if (!displayId) {
- return NAME_NOT_FOUND;
- }
-
- std::vector<hal::ContentType> types;
- getHwComposer().getSupportedContentTypes(*displayId, &types);
-
- *outSupport = std::any_of(types.begin(), types.end(),
- [](auto type) { return type == hal::ContentType::GAME; });
- return NO_ERROR;
-}
-
void SurfaceFlinger::setGameContentType(const sp<IBinder>& displayToken, bool on) {
static_cast<void>(schedule([=]() MAIN_THREAD {
if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
@@ -1282,50 +1282,24 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::getHdrCapabilities(const sp<IBinder>& displayToken,
- HdrCapabilities* outCapabilities) const {
+status_t SurfaceFlinger::overrideHdrTypes(const sp<IBinder>& displayToken,
+ const std::vector<ui::Hdr>& hdrTypes) {
Mutex::Autolock lock(mStateLock);
- const auto display = getDisplayDeviceLocked(displayToken);
+ auto display = getDisplayDeviceLocked(displayToken);
if (!display) {
ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
return NAME_NOT_FOUND;
}
- // At this point the DisplayDevice should already be set up,
- // meaning the luminance information is already queried from
- // hardware composer and stored properly.
- const HdrCapabilities& capabilities = display->getHdrCapabilities();
- *outCapabilities = HdrCapabilities(capabilities.getSupportedHdrTypes(),
- capabilities.getDesiredMaxLuminance(),
- capabilities.getDesiredMaxAverageLuminance(),
- capabilities.getDesiredMinLuminance());
-
+ display->overrideHdrTypes(hdrTypes);
+ dispatchDisplayHotplugEvent(display->getPhysicalId(), true /* connected */);
return NO_ERROR;
}
-std::optional<DeviceProductInfo> SurfaceFlinger::getDeviceProductInfoLocked(
- const DisplayDevice& display) const {
- // TODO(b/149075047): Populate DeviceProductInfo on hotplug and store it in DisplayDevice to
- // avoid repetitive HAL IPC and EDID parsing.
- const auto displayId = display.getId();
- LOG_FATAL_IF(!displayId);
-
- const auto hwcDisplayId = getHwComposer().fromPhysicalDisplayId(*displayId);
- LOG_FATAL_IF(!hwcDisplayId);
-
- uint8_t port;
- DisplayIdentificationData data;
- if (!getHwComposer().getDisplayIdentificationData(*hwcDisplayId, &port, &data)) {
- ALOGV("%s: No identification data.", __FUNCTION__);
- return {};
- }
-
- const auto info = parseDisplayIdentificationData(port, data);
- if (!info) {
- return {};
- }
- return info->deviceProductInfo;
+status_t SurfaceFlinger::onPullAtom(const int32_t atomId, std::string* pulledData, bool* success) {
+ *success = mTimeStats->onPullAtom(atomId, pulledData);
+ return NO_ERROR;
}
status_t SurfaceFlinger::getDisplayedContentSamplingAttributes(const sp<IBinder>& displayToken,
@@ -1406,8 +1380,7 @@
Mutex::Autolock lock(mStateLock);
if (const auto handle = mScheduler->enableVSyncInjection(enable)) {
- mEventQueue->setEventConnection(
- mScheduler->getEventConnection(enable ? handle : mSfConnectionHandle));
+ mEventQueue->setInjector(enable ? mScheduler->getEventConnection(handle) : nullptr);
}
}).wait();
@@ -1416,7 +1389,12 @@
status_t SurfaceFlinger::injectVSync(nsecs_t when) {
Mutex::Autolock lock(mStateLock);
- return mScheduler->injectVSync(when, calculateExpectedPresentTime(when)) ? NO_ERROR : BAD_VALUE;
+ const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(when);
+ const auto expectedPresent = calculateExpectedPresentTime(stats);
+ return mScheduler->injectVSync(when, /*expectedVSyncTime=*/expectedPresent,
+ /*deadlineTimestamp=*/expectedPresent)
+ ? NO_ERROR
+ : BAD_VALUE;
}
status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) {
@@ -1461,6 +1439,23 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::addFpsListener(int32_t taskId, const sp<gui::IFpsListener>& listener) {
+ if (!listener) {
+ return BAD_VALUE;
+ }
+
+ mFpsReporter->addListener(listener, taskId);
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::removeFpsListener(const sp<gui::IFpsListener>& listener) {
+ if (!listener) {
+ return BAD_VALUE;
+ }
+ mFpsReporter->removeListener(listener);
+ return NO_ERROR;
+}
+
status_t SurfaceFlinger::getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
bool* outSupport) const {
if (!displayToken || !outSupport) {
@@ -1478,27 +1473,70 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) {
+status_t SurfaceFlinger::setDisplayBrightness(const sp<IBinder>& displayToken,
+ const gui::DisplayBrightness& brightness) {
if (!displayToken) {
return BAD_VALUE;
}
- return promise::chain(schedule([=]() MAIN_THREAD {
+ return ftl::chain(schedule([=]() MAIN_THREAD {
if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
- return getHwComposer().setDisplayBrightness(*displayId, brightness);
+ return getHwComposer().setDisplayBrightness(*displayId,
+ brightness.displayBrightness);
} else {
ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
- return promise::yield<status_t>(NAME_NOT_FOUND);
+ return ftl::yield<status_t>(NAME_NOT_FOUND);
}
}))
.then([](std::future<status_t> task) { return task; })
.get();
}
-status_t SurfaceFlinger::notifyPowerHint(int32_t hintId) {
- PowerHint powerHint = static_cast<PowerHint>(hintId);
+status_t SurfaceFlinger::addHdrLayerInfoListener(const sp<IBinder>& displayToken,
+ const sp<gui::IHdrLayerInfoListener>& listener) {
+ if (!displayToken) {
+ return BAD_VALUE;
+ }
- if (powerHint == PowerHint::INTERACTION) {
+ Mutex::Autolock lock(mStateLock);
+
+ const auto display = getDisplayDeviceLocked(displayToken);
+ if (!display) {
+ return NAME_NOT_FOUND;
+ }
+ const auto displayId = display->getId();
+ sp<HdrLayerInfoReporter>& hdrInfoReporter = mHdrLayerInfoListeners[displayId];
+ if (!hdrInfoReporter) {
+ hdrInfoReporter = sp<HdrLayerInfoReporter>::make();
+ }
+ hdrInfoReporter->addListener(listener);
+ return OK;
+}
+
+status_t SurfaceFlinger::removeHdrLayerInfoListener(
+ const sp<IBinder>& displayToken, const sp<gui::IHdrLayerInfoListener>& listener) {
+ if (!displayToken) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mStateLock);
+
+ const auto display = getDisplayDeviceLocked(displayToken);
+ if (!display) {
+ return NAME_NOT_FOUND;
+ }
+ const auto displayId = display->getId();
+ sp<HdrLayerInfoReporter>& hdrInfoReporter = mHdrLayerInfoListeners[displayId];
+ if (hdrInfoReporter) {
+ hdrInfoReporter->removeListener(listener);
+ }
+ return OK;
+}
+
+status_t SurfaceFlinger::notifyPowerBoost(int32_t boostId) {
+ Boost powerBoost = static_cast<Boost>(boostId);
+
+ if (powerBoost == Boost::INTERACTION) {
mScheduler->notifyTouchEvent();
}
@@ -1508,11 +1546,12 @@
// ----------------------------------------------------------------------------
sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
- ISurfaceComposer::VsyncSource vsyncSource, ISurfaceComposer::ConfigChanged configChanged) {
+ ISurfaceComposer::VsyncSource vsyncSource,
+ ISurfaceComposer::EventRegistrationFlags eventRegistration) {
const auto& handle =
vsyncSource == eVsyncSourceSurfaceFlinger ? mSfConnectionHandle : mAppConnectionHandle;
- return mScheduler->createDisplayEventConnection(handle, configChanged);
+ return mScheduler->createDisplayEventConnection(handle, eventRegistration);
}
void SurfaceFlinger::signalTransaction() {
@@ -1533,12 +1572,11 @@
}
nsecs_t SurfaceFlinger::getVsyncPeriodFromHWC() const {
- const auto displayId = getInternalDisplayIdLocked();
- if (!displayId || !getHwComposer().isConnected(*displayId)) {
- return 0;
+ if (const auto display = getDefaultDisplayDeviceLocked()) {
+ return display->getVsyncPeriodFromHWC();
}
- return getHwComposer().getDisplayVsyncPeriod(*displayId);
+ return 0;
}
void SurfaceFlinger::onVsyncReceived(int32_t sequenceId, hal::HWDisplayId hwcDisplayId,
@@ -1552,6 +1590,12 @@
return;
}
+ if (const auto displayId = getHwComposer().toPhysicalDisplayId(hwcDisplayId)) {
+ auto token = getPhysicalDisplayTokenLocked(*displayId);
+ auto display = getDisplayDeviceLocked(token);
+ display->onVsync(timestamp);
+ }
+
if (!getHwComposer().onVsync(hwcDisplayId, timestamp)) {
return;
}
@@ -1564,7 +1608,7 @@
bool periodFlushed = false;
mScheduler->addResyncSample(timestamp, vsyncPeriod, &periodFlushed);
if (periodFlushed) {
- mVSyncModulator->onRefreshRateChangeCompleted();
+ modulateVsync(&VsyncModulator::onRefreshRateChangeCompleted);
}
}
@@ -1573,12 +1617,12 @@
*compositorTiming = getBE().mCompositorTiming;
}
-bool SurfaceFlinger::isDisplayConfigAllowed(HwcConfigIndexType configId) const {
- return mRefreshRateConfigs->isConfigAllowed(configId);
+bool SurfaceFlinger::isDisplayModeAllowed(DisplayModeId modeId) const {
+ return mRefreshRateConfigs->isModeAllowed(modeId);
}
void SurfaceFlinger::changeRefreshRateLocked(const RefreshRate& refreshRate,
- Scheduler::ConfigEvent event) {
+ Scheduler::ModeEvent event) {
const auto display = getDefaultDisplayDeviceLocked();
if (!display || mBootStage != BootStage::FINISHED) {
return;
@@ -1586,18 +1630,18 @@
ATRACE_CALL();
// Don't do any updating if the current fps is the same as the new one.
- if (!isDisplayConfigAllowed(refreshRate.getConfigId())) {
- ALOGV("Skipping config %d as it is not part of allowed configs",
- refreshRate.getConfigId().value());
+ if (!isDisplayModeAllowed(refreshRate.getModeId())) {
+ ALOGV("Skipping mode %d as it is not part of allowed modes",
+ refreshRate.getModeId().value());
return;
}
- setDesiredActiveConfig({refreshRate.getConfigId(), event});
+ setDesiredActiveMode({refreshRate.getModeId(), event});
}
void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hal::HWDisplayId hwcDisplayId,
hal::Connection connection) {
- ALOGV("%s(%d, %" PRIu64 ", %s)", __FUNCTION__, sequenceId, hwcDisplayId,
+ ALOGI("%s(%d, %" PRIu64 ", %s)", __FUNCTION__, sequenceId, hwcDisplayId,
connection == hal::Connection::CONNECTED ? "connected" : "disconnected");
// Ignore events that do not have the right sequenceId.
@@ -1632,7 +1676,7 @@
}
void SurfaceFlinger::onSeamlessPossible(int32_t /*sequenceId*/, hal::HWDisplayId /*display*/) {
- // TODO(b/142753666): use constraints when calling to setActiveConfigWithConstrains and
+ // TODO(b/142753666): use constraints when calling to setActiveModeWithConstraints and
// use this callback to know when to retry in case of SEAMLESS_NOT_POSSIBLE.
}
@@ -1644,40 +1688,33 @@
repaintEverythingForHWC();
}
-void SurfaceFlinger::setPrimaryVsyncEnabled(bool enabled) {
+void SurfaceFlinger::setVsyncEnabled(bool enabled) {
ATRACE_CALL();
- // Enable / Disable HWVsync from the main thread to avoid race conditions with
- // display power state.
- static_cast<void>(schedule([=]() MAIN_THREAD { setPrimaryVsyncEnabledInternal(enabled); }));
-}
+ // On main thread to avoid race conditions with display power state.
+ static_cast<void>(schedule([=]() MAIN_THREAD {
+ mHWCVsyncPendingState = enabled ? hal::Vsync::ENABLE : hal::Vsync::DISABLE;
-void SurfaceFlinger::setPrimaryVsyncEnabledInternal(bool enabled) {
- ATRACE_CALL();
-
- mHWCVsyncPendingState = enabled ? hal::Vsync::ENABLE : hal::Vsync::DISABLE;
-
- if (const auto displayId = getInternalDisplayIdLocked()) {
- sp<DisplayDevice> display = getDefaultDisplayDeviceLocked();
- if (display && display->isPoweredOn()) {
- getHwComposer().setVsyncEnabled(*displayId, mHWCVsyncPendingState);
+ if (const auto display = getDefaultDisplayDeviceLocked();
+ display && display->isPoweredOn()) {
+ getHwComposer().setVsyncEnabled(display->getPhysicalId(), mHWCVsyncPendingState);
}
- }
+ }));
}
-sp<Fence> SurfaceFlinger::previousFrameFence() {
+SurfaceFlinger::FenceWithFenceTime SurfaceFlinger::previousFrameFence() {
// We are storing the last 2 present fences. If sf's phase offset is to be
// woken up before the actual vsync but targeting the next vsync, we need to check
// fence N-2
- return mVSyncModulator->getOffsets().sf > 0 ? mPreviousPresentFences[0]
- : mPreviousPresentFences[1];
+ return mVsyncModulator->getVsyncConfig().sfOffset > 0 ? mPreviousPresentFences[0]
+ : mPreviousPresentFences[1];
}
bool SurfaceFlinger::previousFramePending(int graceTimeMs) {
ATRACE_CALL();
- const sp<Fence>& fence = previousFrameFence();
+ const std::shared_ptr<FenceTime>& fence = previousFrameFence().fenceTime;
- if (fence == Fence::NO_FENCE) {
+ if (fence == FenceTime::NO_FENCE) {
return false;
}
@@ -1688,28 +1725,31 @@
}
nsecs_t SurfaceFlinger::previousFramePresentTime() {
- const sp<Fence>& fence = previousFrameFence();
+ const std::shared_ptr<FenceTime>& fence = previousFrameFence().fenceTime;
- if (fence == Fence::NO_FENCE) {
+ if (fence == FenceTime::NO_FENCE) {
return Fence::SIGNAL_TIME_INVALID;
}
return fence->getSignalTime();
}
-nsecs_t SurfaceFlinger::calculateExpectedPresentTime(nsecs_t now) const {
- DisplayStatInfo stats;
- mScheduler->getDisplayStatInfo(&stats);
- const nsecs_t presentTime = mScheduler->getDispSyncExpectedPresentTime(now);
+nsecs_t SurfaceFlinger::calculateExpectedPresentTime(DisplayStatInfo stats) const {
// Inflate the expected present time if we're targetting the next vsync.
- return mVSyncModulator->getOffsets().sf > 0 ? presentTime : presentTime + stats.vsyncPeriod;
+ return mVsyncModulator->getVsyncConfig().sfOffset > 0 ? stats.vsyncTime
+ : stats.vsyncTime + stats.vsyncPeriod;
}
-void SurfaceFlinger::onMessageReceived(int32_t what, nsecs_t expectedVSyncTime) {
- ATRACE_CALL();
+void SurfaceFlinger::onMessageReceived(int32_t what, int64_t vsyncId, nsecs_t expectedVSyncTime) {
+ const auto vsyncIn = [&] {
+ if (!ATRACE_ENABLED()) return 0.f;
+ return (expectedVSyncTime - systemTime()) / 1e6f;
+ }();
+
+ ATRACE_FORMAT("onMessageReceived %" PRId64 " vsyncIn %.2fms", vsyncId, vsyncIn);
switch (what) {
case MessageQueue::INVALIDATE: {
- onMessageInvalidate(expectedVSyncTime);
+ onMessageInvalidate(vsyncId, expectedVSyncTime);
break;
}
case MessageQueue::REFRESH: {
@@ -1719,7 +1759,7 @@
}
}
-void SurfaceFlinger::onMessageInvalidate(nsecs_t expectedVSyncTime) {
+void SurfaceFlinger::onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTime) {
ATRACE_CALL();
const nsecs_t frameStart = systemTime();
@@ -1729,7 +1769,8 @@
if (expectedVSyncTime >= frameStart) {
mExpectedPresentTime = expectedVSyncTime;
} else {
- mExpectedPresentTime = mScheduler->getDispSyncExpectedPresentTime(frameStart);
+ const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(frameStart);
+ mExpectedPresentTime = calculateExpectedPresentTime(stats);
}
const nsecs_t lastScheduledPresentTime = mScheduledPresentTime;
@@ -1739,10 +1780,7 @@
// for the present fence to fire instead of just giving up on this frame to handle cases
// where present fence is just about to get signaled.
const int graceTimeForPresentFenceMs =
- (mPropagateBackpressure &&
- (mPropagateBackpressureClientComposition || !mHadClientComposition))
- ? 1
- : 0;
+ (mPropagateBackpressureClientComposition || !mHadClientComposition) ? 1 : 0;
// Pending frames may trigger backpressure propagation.
const TracedOrdinal<bool> framePending = {"PrevFramePending",
@@ -1756,8 +1794,7 @@
// Add some slop to correct for drift. This should generally be
// smaller than a typical frame duration, but should not be so small
// that it reports reasonable drift as a missed frame.
- DisplayStatInfo stats;
- mScheduler->getDisplayStatInfo(&stats);
+ const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(systemTime());
const nsecs_t frameMissedSlop = stats.vsyncPeriod / 2;
const nsecs_t previousPresentTime = previousFramePresentTime();
const TracedOrdinal<bool> frameMissed = {"PrevFrameMissed",
@@ -1773,10 +1810,6 @@
if (frameMissed) {
mFrameMissedCount++;
mTimeStats->incrementMissedFrames();
- if (mMissedFrameJankCount == 0) {
- mMissedFrameJankStart = systemTime();
- }
- mMissedFrameJankCount++;
}
if (hwcFrameMissed) {
@@ -1787,73 +1820,52 @@
mGpuFrameMissedCount++;
}
- // If we are in the middle of a config change and the fence hasn't
+ // If we are in the middle of a mode change and the fence hasn't
// fired yet just wait for the next invalidate
- if (mSetActiveConfigPending) {
+ if (mSetActiveModePending) {
if (framePending) {
mEventQueue->invalidate();
return;
}
// We received the present fence from the HWC, so we assume it successfully updated
- // the config, hence we update SF.
- mSetActiveConfigPending = false;
- ON_MAIN_THREAD(setActiveConfigInternal());
+ // the mode, hence we update SF.
+ mSetActiveModePending = false;
+ ON_MAIN_THREAD(setActiveModeInternal());
}
- if (framePending && mPropagateBackpressure) {
+ if (framePending) {
if ((hwcFrameMissed && !gpuFrameMissed) || mPropagateBackpressureClientComposition) {
signalLayerUpdate();
return;
}
}
- // Our jank window is always at least 100ms since we missed a
- // frame...
- static constexpr nsecs_t kMinJankyDuration =
- std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count();
- // ...but if it's larger than 1s then we missed the trace cutoff.
- static constexpr nsecs_t kMaxJankyDuration =
- std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
- nsecs_t jankDurationToUpload = -1;
- // If we're in a user build then don't push any atoms
- if (!mIsUserBuild && mMissedFrameJankCount > 0) {
- const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
- // Only report jank when the display is on, as displays in DOZE
- // power mode may operate at a different frame rate than is
- // reported in their config, which causes noticeable (but less
- // severe) jank.
- if (display && display->getPowerMode() == hal::PowerMode::ON) {
- const nsecs_t currentTime = systemTime();
- const nsecs_t jankDuration = currentTime - mMissedFrameJankStart;
- if (jankDuration > kMinJankyDuration && jankDuration < kMaxJankyDuration) {
- jankDurationToUpload = jankDuration;
- }
-
- // We either reported a jank event or we missed the trace
- // window, so clear counters here.
- if (jankDuration > kMinJankyDuration) {
- mMissedFrameJankCount = 0;
- mMissedFrameJankStart = 0;
- }
- }
- }
-
if (mTracingEnabledChanged) {
mTracingEnabled = mTracing.isEnabled();
mTracingEnabledChanged = false;
}
+ if (mRefreshRateOverlaySpinner) {
+ if (Mutex::Autolock lock(mStateLock); mRefreshRateOverlay) {
+ mRefreshRateOverlay->onInvalidate();
+ }
+ }
+
bool refreshNeeded;
{
- ConditionalLockGuard<std::mutex> lock(mTracingLock, mTracingEnabled);
+ mTracePostComposition = mTracing.flagIsSet(SurfaceTracing::TRACE_COMPOSITION) ||
+ mTracing.flagIsSet(SurfaceTracing::TRACE_SYNC) ||
+ mTracing.flagIsSet(SurfaceTracing::TRACE_BUFFERS);
+ const bool tracePreComposition = mTracingEnabled && !mTracePostComposition;
+ ConditionalLockGuard<std::mutex> lock(mTracingLock, tracePreComposition);
+
+ mFrameTimeline->setSfWakeUp(vsyncId, frameStart, Fps::fromPeriodNsecs(stats.vsyncPeriod));
refreshNeeded = handleMessageTransaction();
refreshNeeded |= handleMessageInvalidate();
- if (mTracingEnabled) {
- mAddCompositionStateToTrace =
- mTracing.flagIsSetLocked(SurfaceTracing::TRACE_COMPOSITION);
- if (mVisibleRegionsDirty && !mAddCompositionStateToTrace) {
+ if (tracePreComposition) {
+ if (mVisibleRegionsDirty) {
mTracing.notifyLocked("visibleRegionsDirty");
}
}
@@ -1868,14 +1880,13 @@
mScheduler->chooseRefreshRateForContent();
}
- ON_MAIN_THREAD(performSetActiveConfig());
+ ON_MAIN_THREAD(performSetActiveMode());
updateCursorAsync();
updateInputFlinger();
refreshNeeded |= mRepaintEverything;
if (refreshNeeded && CC_LIKELY(mBootStage != BootStage::BOOTLOADER)) {
- mLastJankDuration = jankDurationToUpload;
// Signal a refresh if a transaction modified the window state,
// a new buffer was latched, or if HWC has requested a full
// repaint
@@ -1886,25 +1897,27 @@
// underestimated.
mFrameStartTime = frameStart;
}
- signalRefresh();
+
+ // Run the refresh immediately after invalidate as there is no point going thru the message
+ // queue again, and to ensure that we actually refresh the screen instead of handling
+ // other messages that were queued us already in the MessageQueue.
+ mRefreshPending = true;
+ onMessageRefresh();
}
}
bool SurfaceFlinger::handleMessageTransaction() {
ATRACE_CALL();
+
+ if (getTransactionFlags(eTransactionFlushNeeded)) {
+ flushTransactionQueues();
+ }
uint32_t transactionFlags = peekTransactionFlags();
-
- bool flushedATransaction = flushTransactionQueues();
-
bool runHandleTransaction =
- (transactionFlags && (transactionFlags != eTransactionFlushNeeded)) ||
- flushedATransaction ||
- mForceTraversal;
+ ((transactionFlags & (~eTransactionFlushNeeded)) != 0) || mForceTraversal;
if (runHandleTransaction) {
handleTransaction(eTransactionMask);
- } else {
- getTransactionFlags(eTransactionFlushNeeded);
}
if (transactionFlushNeeded()) {
@@ -1930,7 +1943,7 @@
refreshArgs.layers.push_back(layerFE);
});
refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
- for (sp<Layer> layer : mLayersWithQueuedFrames) {
+ for (auto layer : mLayersWithQueuedFrames) {
if (auto layerFE = layer->getCompositionEngineLayerFE())
refreshArgs.layersWithQueuedFrames.push_back(layerFE);
}
@@ -1959,6 +1972,8 @@
std::chrono::milliseconds(mDebugRegion > 1 ? mDebugRegion : 0);
}
+ refreshArgs.earliestPresentTime = mScheduler->getPreviousVsyncFrom(mExpectedPresentTime);
+
mGeometryInvalid = false;
// Store the present time just before calling to the composition engine so we could notify
@@ -1975,7 +1990,7 @@
postFrame();
postComposition();
- const bool prevFrameHadDeviceComposition = mHadDeviceComposition;
+ const bool prevFrameHadClientComposition = mHadClientComposition;
mHadClientComposition = std::any_of(displays.cbegin(), displays.cend(), [](const auto& pair) {
const auto& state = pair.second->getCompositionDisplay()->getState();
@@ -1990,22 +2005,25 @@
const auto& state = pair.second->getCompositionDisplay()->getState();
return state.reusedClientComposition;
});
-
- // Only report a strategy change if we move in and out of composition with hw overlays
- if (prevFrameHadDeviceComposition != mHadDeviceComposition) {
+ // Only report a strategy change if we move in and out of client composition
+ if (prevFrameHadClientComposition != mHadClientComposition) {
mTimeStats->incrementCompositionStrategyChanges();
}
// TODO: b/160583065 Enable skip validation when SF caches all client composition layers
- mVSyncModulator->onRefreshed(mHadClientComposition || mReusedClientComposition);
+ const bool usedGpuComposition = mHadClientComposition || mReusedClientComposition;
+ modulateVsync(&VsyncModulator::onDisplayRefresh, usedGpuComposition);
mLayersWithQueuedFrames.clear();
- if (mVisibleRegionsDirty) {
- mVisibleRegionsDirty = false;
- if (mTracingEnabled && mAddCompositionStateToTrace) {
+ if (mTracingEnabled && mTracePostComposition) {
+ // This may block if SurfaceTracing is running in sync mode.
+ if (mVisibleRegionsDirty) {
mTracing.notify("visibleRegionsDirty");
+ } else if (mTracing.flagIsSet(SurfaceTracing::TRACE_BUFFERS)) {
+ mTracing.notify("bufferLatched");
}
}
+ mVisibleRegionsDirty = false;
if (mCompositionEngine->needsAnotherUpdate()) {
signalLayerUpdate();
@@ -2016,6 +2034,9 @@
ATRACE_CALL();
bool refreshNeeded = handlePageFlip();
+ // Send on commit callbacks
+ mTransactionCallbackInvoker.sendCallbacks();
+
if (mVisibleRegionsDirty) {
computeLayerBounds();
}
@@ -2059,12 +2080,12 @@
nsecs_t compositeToPresentLatency) {
// Integer division and modulo round toward 0 not -inf, so we need to
// treat negative and positive offsets differently.
- nsecs_t idealLatency = (mPhaseConfiguration->getCurrentOffsets().late.sf > 0)
+ nsecs_t idealLatency = (mVsyncConfiguration->getCurrentConfigs().late.sfOffset > 0)
? (stats.vsyncPeriod -
- (mPhaseConfiguration->getCurrentOffsets().late.sf % stats.vsyncPeriod))
- : ((-mPhaseConfiguration->getCurrentOffsets().late.sf) % stats.vsyncPeriod);
+ (mVsyncConfiguration->getCurrentConfigs().late.sfOffset % stats.vsyncPeriod))
+ : ((-mVsyncConfiguration->getCurrentConfigs().late.sfOffset) % stats.vsyncPeriod);
- // Just in case mPhaseConfiguration->getCurrentOffsets().late.sf == -vsyncInterval.
+ // Just in case mVsyncConfiguration->getCurrentConfigs().late.sf == -vsyncInterval.
if (idealLatency <= 0) {
idealLatency = stats.vsyncPeriod;
}
@@ -2074,7 +2095,7 @@
// Reducing jitter is important if an app attempts to extrapolate
// something (such as user input) to an accurate diasplay time.
// Snapping also allows an app to precisely calculate
- // mPhaseConfiguration->getCurrentOffsets().late.sf with (presentLatency % interval).
+ // mVsyncConfiguration->getCurrentConfigs().late.sf with (presentLatency % interval).
nsecs_t bias = stats.vsyncPeriod / 2;
int64_t extraVsyncs = (compositeToPresentLatency - idealLatency + bias) / stats.vsyncPeriod;
nsecs_t snappedCompositeToPresentLatency =
@@ -2086,16 +2107,10 @@
getBE().mCompositorTiming.presentLatency = snappedCompositeToPresentLatency;
}
-void SurfaceFlinger::postComposition()
-{
+void SurfaceFlinger::postComposition() {
ATRACE_CALL();
ALOGV("postComposition");
- nsecs_t dequeueReadyTime = systemTime();
- for (auto& layer : mLayersWithQueuedFrames) {
- layer->releasePendingBuffer(dequeueReadyTime);
- }
-
const auto* display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked()).get();
getBE().mGlCompositionDoneTimeline.updateSignalTimes();
@@ -2112,19 +2127,31 @@
getBE().mDisplayTimeline.updateSignalTimes();
mPreviousPresentFences[1] = mPreviousPresentFences[0];
- mPreviousPresentFences[0] =
- display ? getHwComposer().getPresentFence(*display->getId()) : Fence::NO_FENCE;
- auto presentFenceTime = std::make_shared<FenceTime>(mPreviousPresentFences[0]);
- getBE().mDisplayTimeline.push(presentFenceTime);
+ mPreviousPresentFences[0].fence =
+ display ? getHwComposer().getPresentFence(display->getPhysicalId()) : Fence::NO_FENCE;
+ mPreviousPresentFences[0].fenceTime =
+ std::make_shared<FenceTime>(mPreviousPresentFences[0].fence);
- DisplayStatInfo stats;
- mScheduler->getDisplayStatInfo(&stats);
+ getBE().mDisplayTimeline.push(mPreviousPresentFences[0].fenceTime);
+
+ // Set presentation information before calling Layer::releasePendingBuffer, such that jank
+ // information from previous' frame classification is already available when sending jank info
+ // to clients, so they get jank classification as early as possible.
+ mFrameTimeline->setSfPresent(systemTime(), mPreviousPresentFences[0].fenceTime,
+ glCompositionDoneFenceTime);
+
+ nsecs_t dequeueReadyTime = systemTime();
+ for (const auto& layer : mLayersWithQueuedFrames) {
+ layer->releasePendingBuffer(dequeueReadyTime);
+ }
+
+ const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(systemTime());
// We use the CompositionEngine::getLastFrameRefreshTimestamp() which might
// be sampled a little later than when we started doing work for this frame,
// but that should be okay since updateCompositorTiming has snapping logic.
updateCompositorTiming(stats, mCompositionEngine->getLastFrameRefreshTimestamp(),
- presentFenceTime);
+ mPreviousPresentFences[0].fenceTime);
CompositorTiming compositorTiming;
{
std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock);
@@ -2132,22 +2159,78 @@
}
mDrawingState.traverse([&](Layer* layer) {
- const bool frameLatched = layer->onPostComposition(display, glCompositionDoneFenceTime,
- presentFenceTime, compositorTiming);
+ const bool frameLatched =
+ layer->onPostComposition(display, glCompositionDoneFenceTime,
+ mPreviousPresentFences[0].fenceTime, compositorTiming);
if (frameLatched) {
recordBufferingStats(layer->getName(), layer->getOccupancyHistory(false));
}
});
- mTransactionCompletedThread.addPresentFence(mPreviousPresentFences[0]);
- mTransactionCompletedThread.sendCallbacks();
-
- if (display && display->isPrimary() && display->getPowerMode() == hal::PowerMode::ON &&
- presentFenceTime->isValid()) {
- mScheduler->addPresentFence(presentFenceTime);
+ std::vector<std::pair<std::shared_ptr<compositionengine::Display>, sp<HdrLayerInfoReporter>>>
+ hdrInfoListeners;
+ {
+ Mutex::Autolock lock(mStateLock);
+ if (mFpsReporter) {
+ mFpsReporter->dispatchLayerFps();
+ }
+ hdrInfoListeners.reserve(mHdrLayerInfoListeners.size());
+ for (auto& [key, value] : mHdrLayerInfoListeners) {
+ if (value && value->hasListeners()) {
+ auto listenersDisplay = getDisplayById(key);
+ if (listenersDisplay) {
+ hdrInfoListeners.emplace_back(listenersDisplay->getCompositionDisplay(), value);
+ }
+ }
+ }
}
- const bool isDisplayConnected = display && getHwComposer().isConnected(*display->getId());
+ for (auto& [compositionDisplay, listener] : hdrInfoListeners) {
+ HdrLayerInfoReporter::HdrLayerInfo info;
+ int32_t maxArea = 0;
+ mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) {
+ if (layer->isVisible() &&
+ compositionDisplay->belongsInOutput(layer->getCompositionEngineLayerFE())) {
+ bool isHdr = false;
+ switch (layer->getDataSpace()) {
+ case ui::Dataspace::BT2020:
+ case ui::Dataspace::BT2020_HLG:
+ case ui::Dataspace::BT2020_PQ:
+ case ui::Dataspace::BT2020_ITU:
+ case ui::Dataspace::BT2020_ITU_HLG:
+ case ui::Dataspace::BT2020_ITU_PQ:
+ isHdr = true;
+ break;
+ default:
+ isHdr = false;
+ break;
+ }
+
+ if (isHdr) {
+ info.numberOfHdrLayers++;
+ auto bufferRect = layer->getCompositionState()->geomBufferSize;
+ int32_t area = bufferRect.width() * bufferRect.height();
+ if (area > maxArea) {
+ maxArea = area;
+ info.maxW = bufferRect.width();
+ info.maxH = bufferRect.height();
+ }
+ }
+ }
+ });
+ listener->dispatchHdrLayerInfo(info);
+ }
+
+ mTransactionCallbackInvoker.addPresentFence(mPreviousPresentFences[0].fence);
+ mTransactionCallbackInvoker.sendCallbacks();
+
+ if (display && display->isPrimary() && display->getPowerMode() == hal::PowerMode::ON &&
+ mPreviousPresentFences[0].fenceTime->isValid()) {
+ mScheduler->addPresentFence(mPreviousPresentFences[0].fenceTime);
+ }
+
+ const bool isDisplayConnected =
+ display && getHwComposer().isConnected(display->getPhysicalId());
if (!hasSyncFramework) {
if (isDisplayConnected && display->isPoweredOn()) {
@@ -2158,13 +2241,12 @@
if (mAnimCompositionPending) {
mAnimCompositionPending = false;
- if (presentFenceTime->isValid()) {
- mAnimFrameTracker.setActualPresentFence(
- std::move(presentFenceTime));
+ if (mPreviousPresentFences[0].fenceTime->isValid()) {
+ mAnimFrameTracker.setActualPresentFence(mPreviousPresentFences[0].fenceTime);
} else if (isDisplayConnected) {
// The HWC doesn't support present fences, so use the refresh
// timestamp instead.
- const nsecs_t presentTime = getHwComposer().getRefreshTimestamp(*display->getId());
+ const nsecs_t presentTime = display->getRefreshTimestamp();
mAnimFrameTracker.setActualPresentTime(presentTime);
}
mAnimFrameTracker.advanceFrame();
@@ -2179,20 +2261,12 @@
mTimeStats->incrementClientCompositionReusedFrames();
}
- mTimeStats->setPresentFenceGlobal(presentFenceTime);
+ mTimeStats->setPresentFenceGlobal(mPreviousPresentFences[0].fenceTime);
const size_t sfConnections = mScheduler->getEventThreadConnectionCount(mSfConnectionHandle);
const size_t appConnections = mScheduler->getEventThreadConnectionCount(mAppConnectionHandle);
mTimeStats->recordDisplayEventConnectionCount(sfConnections + appConnections);
- if (mLastJankDuration > 0) {
- ATRACE_NAME("Jank detected");
- const int32_t jankyDurationMillis = mLastJankDuration / (1000 * 1000);
- android::util::stats_write(android::util::DISPLAY_JANK_REPORTED, jankyDurationMillis,
- mMissedFrameJankCount);
- mLastJankDuration = -1;
- }
-
if (isDisplayConnected && !display->isPoweredOn()) {
return;
}
@@ -2245,7 +2319,7 @@
}
FloatRect SurfaceFlinger::getLayerClipBoundsForDisplay(const DisplayDevice& displayDevice) const {
- return displayDevice.getViewport().toFloatRect();
+ return displayDevice.getLayerStackSpaceRect().toFloatRect();
}
void SurfaceFlinger::computeLayerBounds() {
@@ -2266,7 +2340,7 @@
void SurfaceFlinger::postFrame() {
const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
- if (display && getHwComposer().isConnected(*display->getId())) {
+ if (display && getHwComposer().isConnected(display->getPhysicalId())) {
uint32_t flipCount = display->getPageFlipCount();
if (flipCount % LOG_FRAME_STATS_PERIOD == 0) {
logFrameStats();
@@ -2274,8 +2348,7 @@
}
}
-void SurfaceFlinger::handleTransaction(uint32_t transactionFlags)
-{
+void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) {
ATRACE_CALL();
// here we keep a copy of the drawing state (that is the state that's
@@ -2293,7 +2366,7 @@
// with mStateLock held to guarantee that mCurrentState won't change
// until the transaction is committed.
- mVSyncModulator->onTransactionHandled();
+ modulateVsync(&VsyncModulator::onTransactionCommit);
transactionFlags = getTransactionFlags(eTransactionMask);
handleTransactionLocked(transactionFlags);
@@ -2302,43 +2375,126 @@
// here the transaction has been committed
}
+void SurfaceFlinger::loadDisplayModes(PhysicalDisplayId displayId, DisplayModes& outModes,
+ DisplayModePtr& outActiveMode) const {
+ std::vector<HWComposer::HWCDisplayMode> hwcModes;
+ std::optional<hal::HWDisplayId> activeModeHwcId;
+ bool activeModeIsSupported;
+ int attempt = 0;
+ constexpr int kMaxAttempts = 3;
+ do {
+ hwcModes = getHwComposer().getModes(displayId);
+ activeModeHwcId = getHwComposer().getActiveMode(displayId);
+ LOG_ALWAYS_FATAL_IF(!activeModeHwcId, "HWC returned no active mode");
+
+ activeModeIsSupported =
+ std::any_of(hwcModes.begin(), hwcModes.end(),
+ [activeModeHwcId](const HWComposer::HWCDisplayMode& mode) {
+ return mode.hwcId == *activeModeHwcId;
+ });
+ } while (!activeModeIsSupported && ++attempt < kMaxAttempts);
+ LOG_ALWAYS_FATAL_IF(!activeModeIsSupported,
+ "After %d attempts HWC still returns an active mode which is not"
+ " supported. Active mode ID = %" PRIu64 " . Supported modes = %s",
+ kMaxAttempts, *activeModeHwcId, base::Join(hwcModes, ", ").c_str());
+
+ DisplayModes oldModes;
+
+ if (const auto token = getPhysicalDisplayTokenLocked(displayId)) {
+ oldModes = getDisplayDeviceLocked(token)->getSupportedModes();
+ }
+
+ int largestUsedModeId = -1; // Use int instead of DisplayModeId for signedness
+ for (const auto& mode : oldModes) {
+ const auto id = static_cast<int>(mode->getId().value());
+ if (id > largestUsedModeId) {
+ largestUsedModeId = id;
+ }
+ }
+
+ DisplayModes newModes;
+ int32_t nextModeId = largestUsedModeId + 1;
+ for (const auto& hwcMode : hwcModes) {
+ newModes.push_back(DisplayMode::Builder(hwcMode.hwcId)
+ .setId(DisplayModeId{nextModeId++})
+ .setWidth(hwcMode.width)
+ .setHeight(hwcMode.height)
+ .setVsyncPeriod(hwcMode.vsyncPeriod)
+ .setDpiX(hwcMode.dpiX)
+ .setDpiY(hwcMode.dpiY)
+ .setGroup(hwcMode.configGroup)
+ .build());
+ }
+
+ const bool modesAreSame =
+ std::equal(newModes.begin(), newModes.end(), oldModes.begin(), oldModes.end(),
+ [](DisplayModePtr left, DisplayModePtr right) {
+ return left->equalsExceptDisplayModeId(right);
+ });
+
+ if (modesAreSame) {
+ // The supported modes have not changed, keep the old IDs.
+ outModes = oldModes;
+ } else {
+ outModes = newModes;
+ }
+
+ outActiveMode = *std::find_if(outModes.begin(), outModes.end(),
+ [activeModeHwcId](const DisplayModePtr& mode) {
+ return mode->getHwcId() == *activeModeHwcId;
+ });
+}
+
void SurfaceFlinger::processDisplayHotplugEventsLocked() {
for (const auto& event : mPendingHotplugEvents) {
- const std::optional<DisplayIdentificationInfo> info =
+ std::optional<DisplayIdentificationInfo> info =
getHwComposer().onHotplug(event.hwcDisplayId, event.connection);
if (!info) {
continue;
}
- const DisplayId displayId = info->id;
+ const auto displayId = info->id;
const auto it = mPhysicalDisplayTokens.find(displayId);
if (event.connection == hal::Connection::CONNECTED) {
+ DisplayModes supportedModes;
+ DisplayModePtr activeMode;
+ loadDisplayModes(displayId, supportedModes, activeMode);
+
if (it == mPhysicalDisplayTokens.end()) {
ALOGV("Creating display %s", to_string(displayId).c_str());
- if (event.hwcDisplayId == getHwComposer().getInternalHwcDisplayId()) {
- initScheduler(displayId);
- }
-
DisplayDeviceState state;
- state.physical = {displayId, getHwComposer().getDisplayConnectionType(displayId),
- event.hwcDisplayId};
+ state.physical = {.id = displayId,
+ .type = getHwComposer().getDisplayConnectionType(displayId),
+ .hwcDisplayId = event.hwcDisplayId,
+ .deviceProductInfo = std::move(info->deviceProductInfo),
+ .supportedModes = std::move(supportedModes),
+ .activeMode = activeMode};
state.isSecure = true; // All physical displays are currently considered secure.
- state.displayName = info->name;
+ state.displayName = std::move(info->name);
sp<IBinder> token = new BBinder();
mCurrentState.displays.add(token, state);
mPhysicalDisplayTokens.emplace(displayId, std::move(token));
+ if (event.hwcDisplayId == getHwComposer().getInternalHwcDisplayId()) {
+ initScheduler(state);
+ }
+
mInterceptor->saveDisplayCreation(state);
} else {
ALOGV("Recreating display %s", to_string(displayId).c_str());
const auto token = it->second;
auto& state = mCurrentState.displays.editValueFor(token);
- state.sequenceId = DisplayDeviceState{}.sequenceId;
+ state.sequenceId = DisplayDeviceState{}.sequenceId; // Generate new sequenceId
+ state.physical->supportedModes = std::move(supportedModes);
+ state.physical->activeMode = activeMode;
+ if (getHwComposer().updatesDeviceProductInfoOnHotplugReconnect()) {
+ state.physical->deviceProductInfo = std::move(info->deviceProductInfo);
+ }
}
} else {
ALOGV("Removing display %s", to_string(displayId).c_str());
@@ -2359,6 +2515,8 @@
}
void SurfaceFlinger::dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected) {
+ ALOGI("Dispatching display hotplug event displayId=%s, connected=%d",
+ to_string(displayId).c_str(), connected);
mScheduler->onHotplugReceived(mAppConnectionHandle, displayId, connected);
mScheduler->onHotplugReceived(mSfConnectionHandle, displayId, connected);
}
@@ -2369,8 +2527,7 @@
const DisplayDeviceState& state,
const sp<compositionengine::DisplaySurface>& displaySurface,
const sp<IGraphicBufferProducer>& producer) {
- auto displayId = compositionDisplay->getDisplayId();
- DisplayDeviceCreationArgs creationArgs(this, displayToken, compositionDisplay);
+ DisplayDeviceCreationArgs creationArgs(this, getHwComposer(), displayToken, compositionDisplay);
creationArgs.sequenceId = state.sequenceId;
creationArgs.isSecure = state.isSecure;
creationArgs.displaySurface = displaySurface;
@@ -2379,28 +2536,29 @@
if (const auto& physical = state.physical) {
creationArgs.connectionType = physical->type;
+ creationArgs.supportedModes = physical->supportedModes;
}
- const bool isInternalDisplay = displayId && displayId == getInternalDisplayIdLocked();
- creationArgs.isPrimary = isInternalDisplay;
+ if (const auto id = PhysicalDisplayId::tryCast(compositionDisplay->getId())) {
+ creationArgs.isPrimary = id == getInternalDisplayIdLocked();
- if (useColorManagement && displayId) {
- std::vector<ColorMode> modes = getHwComposer().getColorModes(*displayId);
- for (ColorMode colorMode : modes) {
- if (isWideColorMode(colorMode)) {
- creationArgs.hasWideColorGamut = true;
+ if (useColorManagement) {
+ std::vector<ColorMode> modes = getHwComposer().getColorModes(*id);
+ for (ColorMode colorMode : modes) {
+ if (isWideColorMode(colorMode)) {
+ creationArgs.hasWideColorGamut = true;
+ }
+
+ std::vector<RenderIntent> renderIntents =
+ getHwComposer().getRenderIntents(*id, colorMode);
+ creationArgs.hwcColorModes.emplace(colorMode, renderIntents);
}
-
- std::vector<RenderIntent> renderIntents =
- getHwComposer().getRenderIntents(*displayId, colorMode);
- creationArgs.hwcColorModes.emplace(colorMode, renderIntents);
}
}
- if (displayId) {
- getHwComposer().getHdrCapabilities(*displayId, &creationArgs.hdrCapabilities);
- creationArgs.supportedPerFrameMetadata =
- getHwComposer().getSupportedPerFrameMetadata(*displayId);
+ if (const auto id = HalDisplayId::tryCast(compositionDisplay->getId())) {
+ getHwComposer().getHdrCapabilities(*id, &creationArgs.hdrCapabilities);
+ creationArgs.supportedPerFrameMetadata = getHwComposer().getSupportedPerFrameMetadata(*id);
}
auto nativeWindowSurface = getFactory().createNativeWindowSurface(producer);
@@ -2415,7 +2573,7 @@
}
creationArgs.physicalOrientation =
- isInternalDisplay ? internalDisplayOrientation : ui::ROTATION_0;
+ creationArgs.isPrimary ? internalDisplayOrientation : ui::ROTATION_0;
// virtual displays are always considered enabled
creationArgs.initialPowerMode = state.isVirtual() ? hal::PowerMode::ON : hal::PowerMode::OFF;
@@ -2437,13 +2595,13 @@
RenderIntent::COLORIMETRIC,
Dataspace::UNKNOWN});
if (!state.isVirtual()) {
- LOG_ALWAYS_FATAL_IF(!displayId);
- auto activeConfigId = HwcConfigIndexType(getHwComposer().getActiveConfigIndex(*displayId));
- display->setActiveConfig(activeConfigId);
+ display->setActiveMode(state.physical->activeMode->getId());
+ display->setDeviceProductInfo(state.physical->deviceProductInfo);
}
display->setLayerStack(state.layerStack);
- display->setProjection(state.orientation, state.viewport, state.frame);
+ display->setProjection(state.orientation, state.layerStackSpaceRect,
+ state.orientedDisplaySpaceRect);
display->setDisplayName(state.displayName);
return display;
@@ -2451,19 +2609,15 @@
void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken,
const DisplayDeviceState& state) {
- int width = 0;
- int height = 0;
+ ui::Size resolution(0, 0);
ui::PixelFormat pixelFormat = static_cast<ui::PixelFormat>(PIXEL_FORMAT_UNKNOWN);
if (state.physical) {
- const auto& activeConfig =
- getCompositionEngine().getHwComposer().getActiveConfig(state.physical->id);
- width = activeConfig->getWidth();
- height = activeConfig->getHeight();
+ resolution = state.physical->activeMode->getSize();
pixelFormat = static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888);
} else if (state.surface != nullptr) {
- int status = state.surface->query(NATIVE_WINDOW_WIDTH, &width);
+ int status = state.surface->query(NATIVE_WINDOW_WIDTH, &resolution.width);
ALOGE_IF(status != NO_ERROR, "Unable to query width (%d)", status);
- status = state.surface->query(NATIVE_WINDOW_HEIGHT, &height);
+ status = state.surface->query(NATIVE_WINDOW_HEIGHT, &resolution.height);
ALOGE_IF(status != NO_ERROR, "Unable to query height (%d)", status);
int intPixelFormat;
status = state.surface->query(NATIVE_WINDOW_FORMAT, &intPixelFormat);
@@ -2480,12 +2634,13 @@
if (const auto& physical = state.physical) {
builder.setPhysical({physical->id, physical->type});
}
- builder.setPixels(ui::Size(width, height));
+ builder.setPixels(resolution);
builder.setPixelFormat(pixelFormat);
builder.setIsSecure(state.isSecure);
builder.setLayerStackId(state.layerStack);
builder.setPowerAdvisor(&mPowerAdvisor);
builder.setUseHwcVirtualDisplays(mUseHwcVirtualDisplays);
+ builder.setGpuVirtualDisplayIdGenerator(mGpuVirtualDisplayIdGenerator);
builder.setName(state.displayName);
const auto compositionDisplay = getCompositionEngine().createDisplay(builder.build());
@@ -2495,11 +2650,13 @@
sp<IGraphicBufferConsumer> bqConsumer;
getFactory().createBufferQueue(&bqProducer, &bqConsumer, /*consumerIsSurfaceFlinger =*/false);
- std::optional<DisplayId> displayId = compositionDisplay->getId();
+ DisplayId displayId = compositionDisplay->getId();
if (state.isVirtual()) {
+ const auto virtualId = VirtualDisplayId::tryCast(displayId);
+ LOG_FATAL_IF(!virtualId);
sp<VirtualDisplaySurface> vds =
- new VirtualDisplaySurface(getHwComposer(), displayId, state.surface, bqProducer,
+ new VirtualDisplaySurface(getHwComposer(), *virtualId, state.surface, bqProducer,
bqConsumer, state.displayName);
displaySurface = vds;
@@ -2509,10 +2666,11 @@
"adding a supported display, but rendering "
"surface is provided (%p), ignoring it",
state.surface.get());
-
- LOG_ALWAYS_FATAL_IF(!displayId);
- displaySurface = new FramebufferSurface(getHwComposer(), *displayId, bqConsumer,
- maxGraphicsWidth, maxGraphicsHeight);
+ const auto physicalId = PhysicalDisplayId::tryCast(displayId);
+ LOG_FATAL_IF(!physicalId);
+ displaySurface = new FramebufferSurface(getHwComposer(), *physicalId, bqConsumer,
+ state.physical->activeMode->getSize(),
+ ui::Size(maxGraphicsWidth, maxGraphicsHeight));
producer = bqProducer;
}
@@ -2521,28 +2679,41 @@
displaySurface, producer);
mDisplays.emplace(displayToken, display);
if (!state.isVirtual()) {
- LOG_FATAL_IF(!displayId);
- dispatchDisplayHotplugEvent(displayId->value, true);
+ dispatchDisplayHotplugEvent(display->getPhysicalId(), true);
}
if (display->isPrimary()) {
mScheduler->onPrimaryDisplayAreaChanged(display->getWidth() * display->getHeight());
+ getRenderEngine().onPrimaryDisplaySizeChanged(display->getSize());
}
}
void SurfaceFlinger::processDisplayRemoved(const wp<IBinder>& displayToken) {
- if (const auto display = getDisplayDeviceLocked(displayToken)) {
- // Save display ID before disconnecting.
- const auto displayId = display->getId();
+ auto display = getDisplayDeviceLocked(displayToken);
+ if (display) {
display->disconnect();
-
if (!display->isVirtual()) {
- LOG_FATAL_IF(!displayId);
- dispatchDisplayHotplugEvent(displayId->value, false);
+ dispatchDisplayHotplugEvent(display->getPhysicalId(), false);
}
}
mDisplays.erase(displayToken);
+
+ if (display && display->isVirtual()) {
+ static_cast<void>(schedule([display = std::move(display)] {
+ // Destroy the display without holding the mStateLock.
+ // This is a temporary solution until we can manage transaction queues without
+ // holding the mStateLock.
+ // With blast, the IGBP that is passed to the VirtualDisplaySurface is owned by the
+ // client. When the IGBP is disconnected, its buffer cache in SF will be cleared
+ // via SurfaceComposerClient::doUncacheBufferTransaction. This call from the client
+ // ends up running on the main thread causing a deadlock since setTransactionstate
+ // will try to acquire the mStateLock. Instead we extend the lifetime of
+ // DisplayDevice and destroy it in the main thread without holding the mStateLock.
+ // The display will be disconnected and removed from the mDisplays list so it will
+ // not be accessible.
+ }));
+ }
}
void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken,
@@ -2552,6 +2723,7 @@
const sp<IBinder> drawingBinder = IInterface::asBinder(drawingState.surface);
if (currentBinder != drawingBinder || currentState.sequenceId != drawingState.sequenceId) {
// changing the surface is like destroying and recreating the DisplayDevice
+ getRenderEngine().cleanFramebufferCache();
if (const auto display = getDisplayDeviceLocked(displayToken)) {
display->disconnect();
}
@@ -2563,6 +2735,17 @@
if (currentState.physical) {
const auto display = getDisplayDeviceLocked(displayToken);
setPowerModeInternal(display, hal::PowerMode::ON);
+
+ // TODO(b/175678251) Call a listener instead.
+ if (currentState.physical->hwcDisplayId == getHwComposer().getInternalHwcDisplayId()) {
+ mRefreshRateConfigs->updateDisplayModes(currentState.physical->supportedModes,
+ currentState.physical->activeMode->getId());
+ mVsyncConfiguration->reset();
+ updatePhaseConfiguration(mRefreshRateConfigs->getCurrentRefreshRate().getFps());
+ if (mRefreshRateOverlay) {
+ mRefreshRateOverlay->reset();
+ }
+ }
}
return;
}
@@ -2572,10 +2755,11 @@
display->setLayerStack(currentState.layerStack);
}
if ((currentState.orientation != drawingState.orientation) ||
- (currentState.viewport != drawingState.viewport) ||
- (currentState.frame != drawingState.frame)) {
- display->setProjection(currentState.orientation, currentState.viewport,
- currentState.frame);
+ (currentState.layerStackSpaceRect != drawingState.layerStackSpaceRect) ||
+ (currentState.orientedDisplaySpaceRect != drawingState.orientedDisplaySpaceRect)) {
+ display->setProjection(currentState.orientation, currentState.layerStackSpaceRect,
+ currentState.orientedDisplaySpaceRect);
+ mDefaultDisplayTransformHint = display->getTransformHint();
}
if (currentState.width != drawingState.width ||
currentState.height != drawingState.height) {
@@ -2632,15 +2816,7 @@
mDrawingState.displays = mCurrentState.displays;
}
-void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags)
-{
- const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
-
- // Notify all layers of available frames
- mCurrentState.traverse([expectedPresentTime](Layer* layer) {
- layer->notifyAvailableFrames(expectedPresentTime);
- });
-
+void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) {
/*
* Traversal of the children
* (perform the transaction for each of them if needed)
@@ -2764,7 +2940,6 @@
});
}
- commitInputWindowCommands();
commitTransaction();
}
@@ -2783,34 +2958,49 @@
setInputWindowsFinished();
}
+ for (const auto& focusRequest : mInputWindowCommands.focusRequests) {
+ mInputFlinger->setFocusedWindow(focusRequest);
+ }
mInputWindowCommands.clear();
}
-void SurfaceFlinger::updateInputWindowInfo() {
- std::vector<InputWindowInfo> inputHandles;
-
- mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
- if (layer->needsInputInfo()) {
- // When calculating the screen bounds we ignore the transparent region since it may
- // result in an unwanted offset.
- inputHandles.push_back(layer->fillInputInfo());
- }
- });
-
- mInputFlinger->setInputWindows(inputHandles,
- mInputWindowCommands.syncInputWindows ? mSetInputWindowsListener
- : nullptr);
+bool enablePerWindowInputRotation() {
+ static bool value =
+ android::base::GetBoolProperty("persist.debug.per_window_input_rotation", false);
+ return value;
}
-void SurfaceFlinger::commitInputWindowCommands() {
- mInputWindowCommands.merge(mPendingInputWindowCommands);
- mPendingInputWindowCommands.clear();
+void SurfaceFlinger::updateInputWindowInfo() {
+ std::vector<InputWindowInfo> inputInfos;
+
+ mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
+ if (!layer->needsInputInfo()) return;
+ sp<DisplayDevice> display;
+ if (enablePerWindowInputRotation()) {
+ for (const auto& pair : ON_MAIN_THREAD(mDisplays)) {
+ const auto& displayDevice = pair.second;
+ if (!displayDevice->getCompositionDisplay()
+ ->belongsInOutput(layer->getLayerStack(),
+ layer->getPrimaryDisplayOnly())) {
+ continue;
+ }
+ display = displayDevice;
+ }
+ }
+ // When calculating the screen bounds we ignore the transparent region since it may
+ // result in an unwanted offset.
+ inputInfos.push_back(layer->fillInputInfo(display));
+ });
+
+ mInputFlinger->setInputWindows(inputInfos,
+ mInputWindowCommands.syncInputWindows ? mSetInputWindowsListener
+ : nullptr);
}
void SurfaceFlinger::updateCursorAsync() {
compositionengine::CompositionRefreshArgs refreshArgs;
for (const auto& [_, display] : ON_MAIN_THREAD(mDisplays)) {
- if (display->getId()) {
+ if (HalDisplayId::tryCast(display->getId())) {
refreshArgs.outputs.push_back(display->getCompositionDisplay());
}
}
@@ -2818,75 +3008,105 @@
mCompositionEngine->updateCursorAsync(refreshArgs);
}
-void SurfaceFlinger::changeRefreshRate(const RefreshRate& refreshRate,
- Scheduler::ConfigEvent event) {
+void SurfaceFlinger::changeRefreshRate(const RefreshRate& refreshRate, Scheduler::ModeEvent event) {
// If this is called from the main thread mStateLock must be locked before
// Currently the only way to call this function from the main thread is from
- // Sheduler::chooseRefreshRateForContent
+ // Scheduler::chooseRefreshRateForContent
ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
changeRefreshRateLocked(refreshRate, event);
}
-void SurfaceFlinger::initScheduler(DisplayId primaryDisplayId) {
+void SurfaceFlinger::triggerOnFrameRateOverridesChanged() {
+ PhysicalDisplayId displayId = [&]() {
+ ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
+ return getDefaultDisplayDeviceLocked()->getPhysicalId();
+ }();
+
+ mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId);
+}
+
+void SurfaceFlinger::initScheduler(const DisplayDeviceState& displayState) {
if (mScheduler) {
// In practice it's not allowed to hotplug in/out the primary display once it's been
// connected during startup, but some tests do it, so just warn and return.
ALOGW("Can't re-init scheduler");
return;
}
+ const auto displayId = displayState.physical->id;
+ mRefreshRateConfigs = std::make_unique<
+ scheduler::RefreshRateConfigs>(displayState.physical->supportedModes,
+ displayState.physical->activeMode->getId(),
+ android::sysprop::enable_frame_rate_override(false));
+ const auto currRefreshRate = displayState.physical->activeMode->getFps();
+ mRefreshRateStats = std::make_unique<scheduler::RefreshRateStats>(*mTimeStats, currRefreshRate,
+ hal::PowerMode::OFF);
- auto currentConfig = HwcConfigIndexType(getHwComposer().getActiveConfigIndex(primaryDisplayId));
- mRefreshRateConfigs =
- std::make_unique<scheduler::RefreshRateConfigs>(getHwComposer().getConfigs(
- primaryDisplayId),
- currentConfig);
- mRefreshRateStats =
- std::make_unique<scheduler::RefreshRateStats>(*mRefreshRateConfigs, *mTimeStats,
- currentConfig, hal::PowerMode::OFF);
- mRefreshRateStats->setConfigMode(currentConfig);
-
- mPhaseConfiguration = getFactory().createPhaseConfiguration(*mRefreshRateConfigs);
+ mVsyncConfiguration = getFactory().createVsyncConfiguration(currRefreshRate);
+ mVsyncModulator.emplace(mVsyncConfiguration->getCurrentConfigs());
// start the EventThread
- mScheduler =
- getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); },
- *mRefreshRateConfigs, *this);
+ mScheduler = getFactory().createScheduler(*mRefreshRateConfigs, *this);
+ const auto configs = mVsyncConfiguration->getCurrentConfigs();
+ const nsecs_t vsyncPeriod = currRefreshRate.getPeriodNsecs();
mAppConnectionHandle =
- mScheduler->createConnection("app", mPhaseConfiguration->getCurrentOffsets().late.app,
+ mScheduler->createConnection("app", mFrameTimeline->getTokenManager(),
+ /*workDuration=*/configs.late.appWorkDuration,
+ /*readyDuration=*/configs.late.sfWorkDuration,
impl::EventThread::InterceptVSyncsCallback());
mSfConnectionHandle =
- mScheduler->createConnection("sf", mPhaseConfiguration->getCurrentOffsets().late.sf,
+ mScheduler->createConnection("appSf", mFrameTimeline->getTokenManager(),
+ /*workDuration=*/std::chrono::nanoseconds(vsyncPeriod),
+ /*readyDuration=*/configs.late.sfWorkDuration,
[this](nsecs_t timestamp) {
mInterceptor->saveVSyncEvent(timestamp);
});
- mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
- mVSyncModulator.emplace(*mScheduler, mAppConnectionHandle, mSfConnectionHandle,
- mPhaseConfiguration->getCurrentOffsets());
+ mEventQueue->initVsync(mScheduler->getVsyncDispatch(), *mFrameTimeline->getTokenManager(),
+ configs.late.sfWorkDuration);
mRegionSamplingThread =
new RegionSamplingThread(*this, *mScheduler,
RegionSamplingThread::EnvironmentTimingTunables());
- // Dispatch a config change request for the primary display on scheduler
+ mFpsReporter = new FpsReporter(*mFrameTimeline, *this);
+ // Dispatch a mode change request for the primary display on scheduler
// initialization, so that the EventThreads always contain a reference to a
// prior configuration.
//
// This is a bit hacky, but this avoids a back-pointer into the main SF
// classes from EventThread, and there should be no run-time binder cost
// anyway since there are no connected apps at this point.
- const nsecs_t vsyncPeriod =
- mRefreshRateConfigs->getRefreshRateFromConfigId(currentConfig).getVsyncPeriod();
- mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, primaryDisplayId.value,
- currentConfig, vsyncPeriod);
+ mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, displayId,
+ displayState.physical->activeMode->getId(),
+ vsyncPeriod);
+ static auto ignorePresentFences =
+ base::GetBoolProperty("debug.sf.vsync_reactor_ignore_present_fences"s, false);
+ mScheduler->setIgnorePresentFences(
+ ignorePresentFences ||
+ getHwComposer().hasCapability(hal::Capability::PRESENT_FENCE_IS_NOT_RELIABLE));
}
-void SurfaceFlinger::commitTransaction()
-{
+void SurfaceFlinger::updatePhaseConfiguration(const Fps& refreshRate) {
+ mVsyncConfiguration->setRefreshRateFps(refreshRate);
+ setVsyncConfig(mVsyncModulator->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs()),
+ refreshRate.getPeriodNsecs());
+}
+
+void SurfaceFlinger::setVsyncConfig(const VsyncModulator::VsyncConfig& config,
+ nsecs_t vsyncPeriod) {
+ mScheduler->setDuration(mAppConnectionHandle,
+ /*workDuration=*/config.appWorkDuration,
+ /*readyDuration=*/config.sfWorkDuration);
+ mScheduler->setDuration(mSfConnectionHandle,
+ /*workDuration=*/std::chrono::nanoseconds(vsyncPeriod),
+ /*readyDuration=*/config.sfWorkDuration);
+ mEventQueue->setDuration(config.sfWorkDuration);
+}
+
+void SurfaceFlinger::commitTransaction() {
commitTransactionLocked();
- mTransactionPending = false;
+ signalSynchronousTransactions();
mAnimTransactionPending = false;
- mTransactionCV.broadcast();
}
void SurfaceFlinger::commitTransactionLocked() {
@@ -2918,15 +3138,19 @@
// clear the "changed" flags in current state
mCurrentState.colorMatrixChanged = false;
- mDrawingState.traverse([&](Layer* layer) {
- layer->commitChildList();
-
- // If the layer can be reached when traversing mDrawingState, then the layer is no
- // longer offscreen. Remove the layer from the offscreenLayer set.
- if (mOffscreenLayers.count(layer)) {
- mOffscreenLayers.erase(layer);
- }
- });
+ for (const auto& rootLayer : mDrawingState.layersSortedByZ) {
+ rootLayer->commitChildList();
+ }
+ // TODO(b/163019109): See if this traversal is needed at all...
+ if (!mOffscreenLayers.empty()) {
+ mDrawingState.traverse([&](Layer* layer) {
+ // If the layer can be reached when traversing mDrawingState, then the layer is no
+ // longer offscreen. Remove the layer from the offscreenLayer set.
+ if (mOffscreenLayers.count(layer)) {
+ mOffscreenLayers.erase(layer);
+ }
+ });
+ }
commitOffscreenLayers();
mDrawingState.traverse([&](Layer* layer) { layer->updateMirrorInfo(); });
@@ -2953,8 +3177,7 @@
}
}
-bool SurfaceFlinger::handlePageFlip()
-{
+bool SurfaceFlinger::handlePageFlip() {
ATRACE_CALL();
ALOGV("handlePageFlip");
@@ -2979,7 +3202,7 @@
if (layer->hasReadyFrame()) {
frameQueued = true;
if (layer->shouldPresentNow(expectedPresentTime)) {
- mLayersWithQueuedFrames.push_back(layer);
+ mLayersWithQueuedFrames.emplace(layer);
} else {
ATRACE_NAME("!layer->shouldPresentNow()");
layer->useEmptyDamage();
@@ -3002,7 +3225,7 @@
// writes to Layer current state. See also b/119481871
Mutex::Autolock lock(mStateLock);
- for (auto& layer : mLayersWithQueuedFrames) {
+ for (const auto& layer : mLayersWithQueuedFrames) {
if (layer->latchBuffer(visibleRegions, latchTime, expectedPresentTime)) {
mLayersPendingRefresh.push_back(layer);
}
@@ -3034,8 +3257,7 @@
return !mLayersWithQueuedFrames.empty() && newDataLatched;
}
-void SurfaceFlinger::invalidateHwcGeometry()
-{
+void SurfaceFlinger::invalidateHwcGeometry() {
mGeometryInvalid = true;
}
@@ -3044,61 +3266,38 @@
const sp<IBinder>& parentHandle,
const sp<Layer>& parentLayer, bool addToCurrentState,
uint32_t* outTransformHint) {
- // add this layer to the current state list
- {
- Mutex::Autolock _l(mStateLock);
- sp<Layer> parent;
- if (parentHandle != nullptr) {
- parent = fromHandleLocked(parentHandle).promote();
- if (parent == nullptr) {
- return NAME_NOT_FOUND;
- }
- } else {
- parent = parentLayer;
- }
-
- if (mNumLayers >= ISurfaceComposer::MAX_LAYERS) {
- ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(),
- ISurfaceComposer::MAX_LAYERS);
- return NO_MEMORY;
- }
-
- mLayersByLocalBinderToken.emplace(handle->localBinder(), lbc);
-
- if (parent == nullptr && addToCurrentState) {
- mCurrentState.layersSortedByZ.add(lbc);
- } else if (parent == nullptr) {
- lbc->onRemovedFromCurrentState();
- } else if (parent->isRemovedFromCurrentState()) {
- parent->addChild(lbc);
- lbc->onRemovedFromCurrentState();
- } else {
- parent->addChild(lbc);
- }
-
- if (gbc != nullptr) {
- mGraphicBufferProducerList.insert(IInterface::asBinder(gbc).get());
- LOG_ALWAYS_FATAL_IF(mGraphicBufferProducerList.size() >
- mMaxGraphicBufferProducerListSize,
- "Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers",
- mGraphicBufferProducerList.size(),
- mMaxGraphicBufferProducerListSize, mNumLayers.load());
- }
-
- if (const auto display = getDefaultDisplayDeviceLocked()) {
- lbc->updateTransformHint(display->getTransformHint());
- }
- if (outTransformHint) {
- *outTransformHint = lbc->getTransformHint();
- }
-
- mLayersAdded = true;
+ if (mNumLayers >= ISurfaceComposer::MAX_LAYERS) {
+ ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(),
+ ISurfaceComposer::MAX_LAYERS);
+ return NO_MEMORY;
}
+ wp<IBinder> initialProducer;
+ if (gbc != nullptr) {
+ initialProducer = IInterface::asBinder(gbc);
+ }
+ setLayerCreatedState(handle, lbc, parentHandle, parentLayer, initialProducer);
+
+ // Create a transaction includes the initial parent and producer.
+ Vector<ComposerState> states;
+ Vector<DisplayState> displays;
+
+ ComposerState composerState;
+ composerState.state.what = layer_state_t::eLayerCreated;
+ composerState.state.surface = handle;
+ states.add(composerState);
+
+ lbc->updateTransformHint(mDefaultDisplayTransformHint);
+ if (outTransformHint) {
+ *outTransformHint = mDefaultDisplayTransformHint;
+ }
// attach this layer to the client
client->attachLayer(handle, lbc);
- return NO_ERROR;
+ return setTransactionState(FrameTimelineInfo{}, states, displays, 0 /* flags */, nullptr,
+ InputWindowCommands{}, -1 /* desiredPresentTime */,
+ true /* isAutoTimestamp */, {}, false /* hasListenerCallbacks */, {},
+ 0 /* Undefined transactionId */);
}
void SurfaceFlinger::removeGraphicBufferProducerAsync(const wp<IBinder>& binder) {
@@ -3117,16 +3316,13 @@
}
uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) {
- return setTransactionFlags(flags, Scheduler::TransactionStart::Normal);
+ return setTransactionFlags(flags, TransactionSchedule::Late);
}
-uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags,
- Scheduler::TransactionStart transactionStart) {
+uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags, TransactionSchedule schedule) {
uint32_t old = mTransactionFlags.fetch_or(flags);
- mVSyncModulator->setTransactionStart(transactionStart);
- if ((old & flags)==0) { // wake the server up
- signalTransaction();
- }
+ modulateVsync(&VsyncModulator::setTransactionSchedule, schedule);
+ if ((old & flags) == 0) signalTransaction();
return old;
}
@@ -3134,148 +3330,301 @@
mForceTraversal = true;
}
-bool SurfaceFlinger::flushTransactionQueues() {
+void SurfaceFlinger::flushTransactionQueues() {
// to prevent onHandleDestroyed from being called while the lock is held,
// we must keep a copy of the transactions (specifically the composer
// states) around outside the scope of the lock
std::vector<const TransactionState> transactions;
- bool flushedATransaction = false;
+ // Layer handles that have transactions with buffers that are ready to be applied.
+ std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> bufferLayersReadyToPresent;
{
Mutex::Autolock _l(mStateLock);
+ {
+ Mutex::Autolock _l(mQueueLock);
+ // Collect transactions from pending transaction queue.
+ auto it = mPendingTransactionQueues.begin();
+ while (it != mPendingTransactionQueues.end()) {
+ auto& [applyToken, transactionQueue] = *it;
- auto it = mTransactionQueues.begin();
- while (it != mTransactionQueues.end()) {
- auto& [applyToken, transactionQueue] = *it;
-
- while (!transactionQueue.empty()) {
- const auto& transaction = transactionQueue.front();
- if (!transactionIsReadyToBeApplied(transaction.desiredPresentTime,
- transaction.states)) {
- setTransactionFlags(eTransactionFlushNeeded);
- break;
+ while (!transactionQueue.empty()) {
+ auto& transaction = transactionQueue.front();
+ if (!transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
+ transaction.isAutoTimestamp,
+ transaction.desiredPresentTime,
+ transaction.originUid, transaction.states,
+ bufferLayersReadyToPresent)) {
+ setTransactionFlags(eTransactionFlushNeeded);
+ break;
+ }
+ transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
+ bufferLayersReadyToPresent.insert(state.surface);
+ });
+ transactions.emplace_back(std::move(transaction));
+ transactionQueue.pop();
}
- transactions.push_back(transaction);
- applyTransactionState(transaction.states, transaction.displays, transaction.flags,
- mPendingInputWindowCommands, transaction.desiredPresentTime,
- transaction.buffer, transaction.postTime,
- transaction.privileged, transaction.hasListenerCallbacks,
- transaction.listenerCallbacks, /*isMainThread*/ true);
- transactionQueue.pop();
- flushedATransaction = true;
+
+ if (transactionQueue.empty()) {
+ it = mPendingTransactionQueues.erase(it);
+ mTransactionQueueCV.broadcast();
+ } else {
+ it = std::next(it, 1);
+ }
}
- if (transactionQueue.empty()) {
- it = mTransactionQueues.erase(it);
- mTransactionCV.broadcast();
- } else {
- it = std::next(it, 1);
+ // Collect transactions from current transaction queue or queue to pending transactions.
+ // Case 1: push to pending when transactionIsReadyToBeApplied is false.
+ // Case 2: push to pending when there exist a pending queue.
+ // Case 3: others are ready to apply.
+ while (!mTransactionQueue.empty()) {
+ auto& transaction = mTransactionQueue.front();
+ bool pendingTransactions = mPendingTransactionQueues.find(transaction.applyToken) !=
+ mPendingTransactionQueues.end();
+ if (pendingTransactions ||
+ !transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
+ transaction.isAutoTimestamp,
+ transaction.desiredPresentTime,
+ transaction.originUid, transaction.states,
+ bufferLayersReadyToPresent)) {
+ mPendingTransactionQueues[transaction.applyToken].push(std::move(transaction));
+ } else {
+ transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
+ bufferLayersReadyToPresent.insert(state.surface);
+ });
+ transactions.emplace_back(std::move(transaction));
+ }
+ mTransactionQueue.pop();
+ ATRACE_INT("TransactionQueue", mTransactionQueue.size());
+ }
+ }
+
+ // Now apply all transactions.
+ for (const auto& transaction : transactions) {
+ applyTransactionState(transaction.frameTimelineInfo, transaction.states,
+ transaction.displays, transaction.flags,
+ transaction.inputWindowCommands, transaction.desiredPresentTime,
+ transaction.isAutoTimestamp, transaction.buffer,
+ transaction.postTime, transaction.permissions,
+ transaction.hasListenerCallbacks, transaction.listenerCallbacks,
+ transaction.originPid, transaction.originUid, transaction.id);
+ if (transaction.transactionCommittedSignal) {
+ mTransactionCommittedSignals.emplace_back(
+ std::move(transaction.transactionCommittedSignal));
}
}
}
- return flushedATransaction;
}
bool SurfaceFlinger::transactionFlushNeeded() {
- return !mTransactionQueues.empty();
+ Mutex::Autolock _l(mQueueLock);
+ return !mPendingTransactionQueues.empty() || !mTransactionQueue.empty();
}
+bool SurfaceFlinger::frameIsEarly(nsecs_t expectedPresentTime, int64_t vsyncId) const {
+ // The amount of time SF can delay a frame if it is considered early based
+ // on the VsyncModulator::VsyncConfig::appWorkDuration
+ constexpr static std::chrono::nanoseconds kEarlyLatchMaxThreshold = 100ms;
-bool SurfaceFlinger::transactionIsReadyToBeApplied(int64_t desiredPresentTime,
- const Vector<ComposerState>& states) {
+ const auto currentVsyncPeriod = mScheduler->getDisplayStatInfo(systemTime()).vsyncPeriod;
+ const auto earlyLatchVsyncThreshold = currentVsyncPeriod / 2;
+ const auto prediction = mFrameTimeline->getTokenManager()->getPredictionsForToken(vsyncId);
+ if (!prediction.has_value()) {
+ return false;
+ }
+
+ if (std::abs(prediction->presentTime - expectedPresentTime) >=
+ kEarlyLatchMaxThreshold.count()) {
+ return false;
+ }
+
+ return prediction->presentTime >= expectedPresentTime &&
+ prediction->presentTime - expectedPresentTime >= earlyLatchVsyncThreshold;
+}
+
+bool SurfaceFlinger::transactionIsReadyToBeApplied(
+ const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
+ uid_t originUid, const Vector<ComposerState>& states,
+ const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>&
+ bufferLayersReadyToPresent) const {
+ ATRACE_CALL();
const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
// Do not present if the desiredPresentTime has not passed unless it is more than one second
// in the future. We ignore timestamps more than 1 second in the future for stability reasons.
- if (desiredPresentTime >= 0 && desiredPresentTime >= expectedPresentTime &&
+ if (!isAutoTimestamp && desiredPresentTime >= expectedPresentTime &&
desiredPresentTime < expectedPresentTime + s2ns(1)) {
+ ATRACE_NAME("not current");
+ return false;
+ }
+
+ if (!mScheduler->isVsyncValid(expectedPresentTime, originUid)) {
+ ATRACE_NAME("!isVsyncValid");
+ return false;
+ }
+
+ // If the client didn't specify desiredPresentTime, use the vsyncId to determine the expected
+ // present time of this transaction.
+ if (isAutoTimestamp && frameIsEarly(expectedPresentTime, info.vsyncId)) {
+ ATRACE_NAME("frameIsEarly");
return false;
}
for (const ComposerState& state : states) {
const layer_state_t& s = state.state;
- if (!(s.what & layer_state_t::eAcquireFenceChanged)) {
+ const bool acquireFenceChanged = (s.what & layer_state_t::eAcquireFenceChanged);
+ if (acquireFenceChanged && s.acquireFence &&
+ s.acquireFence->getStatus() == Fence::Status::Unsignaled) {
+ ATRACE_NAME("fence unsignaled");
+ return false;
+ }
+
+ sp<Layer> layer = nullptr;
+ if (s.surface) {
+ layer = fromHandleLocked(s.surface).promote();
+ } else if (s.hasBufferChanges()) {
+ ALOGW("Transaction with buffer, but no Layer?");
continue;
}
- if (s.acquireFence && s.acquireFence->getStatus() == Fence::Status::Unsignaled) {
- return false;
+ if (!layer) {
+ continue;
+ }
+
+ ATRACE_NAME(layer->getName().c_str());
+
+ if (s.hasBufferChanges()) {
+ // If backpressure is enabled and we already have a buffer to commit, keep the
+ // transaction in the queue.
+ const bool hasPendingBuffer =
+ bufferLayersReadyToPresent.find(s.surface) != bufferLayersReadyToPresent.end();
+ if (layer->backpressureEnabled() && hasPendingBuffer && isAutoTimestamp) {
+ ATRACE_NAME("hasPendingBuffer");
+ return false;
+ }
}
}
return true;
}
-void SurfaceFlinger::setTransactionState(
- const Vector<ComposerState>& states, const Vector<DisplayState>& displays, uint32_t flags,
- const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands,
- int64_t desiredPresentTime, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
- const std::vector<ListenerCallbacks>& listenerCallbacks) {
- ATRACE_CALL();
-
- const int64_t postTime = systemTime();
-
- bool privileged = callingThreadHasUnscopedSurfaceFlingerAccess();
-
- Mutex::Autolock _l(mStateLock);
+void SurfaceFlinger::queueTransaction(TransactionState& state) {
+ Mutex::Autolock _l(mQueueLock);
// If its TransactionQueue already has a pending TransactionState or if it is pending
- auto itr = mTransactionQueues.find(applyToken);
+ auto itr = mPendingTransactionQueues.find(state.applyToken);
// if this is an animation frame, wait until prior animation frame has
// been applied by SF
- if (flags & eAnimation) {
- while (itr != mTransactionQueues.end()) {
- status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5));
+ if (state.flags & eAnimation) {
+ while (itr != mPendingTransactionQueues.end()) {
+ status_t err = mTransactionQueueCV.waitRelative(mQueueLock, s2ns(5));
if (CC_UNLIKELY(err != NO_ERROR)) {
ALOGW_IF(err == TIMED_OUT,
"setTransactionState timed out "
"waiting for animation frame to apply");
break;
}
- itr = mTransactionQueues.find(applyToken);
+ itr = mPendingTransactionQueues.find(state.applyToken);
}
}
- const bool pendingTransactions = itr != mTransactionQueues.end();
- // Expected present time is computed and cached on invalidate, so it may be stale.
- if (!pendingTransactions) {
- mExpectedPresentTime = calculateExpectedPresentTime(systemTime());
+ // Generate a CountDownLatch pending state if this is a synchronous transaction.
+ if ((state.flags & eSynchronous) || state.inputWindowCommands.syncInputWindows) {
+ state.transactionCommittedSignal = std::make_shared<CountDownLatch>(
+ (state.inputWindowCommands.syncInputWindows ? 2 : 1));
}
- if (pendingTransactions || !transactionIsReadyToBeApplied(desiredPresentTime, states)) {
- mTransactionQueues[applyToken].emplace(states, displays, flags, desiredPresentTime,
- uncacheBuffer, postTime, privileged,
- hasListenerCallbacks, listenerCallbacks);
- setTransactionFlags(eTransactionFlushNeeded);
- return;
- }
+ mTransactionQueue.emplace(state);
+ ATRACE_INT("TransactionQueue", mTransactionQueue.size());
- applyTransactionState(states, displays, flags, inputWindowCommands, desiredPresentTime,
- uncacheBuffer, postTime, privileged, hasListenerCallbacks,
- listenerCallbacks);
+ const auto schedule = [](uint32_t flags) {
+ if (flags & eEarlyWakeupEnd) return TransactionSchedule::EarlyEnd;
+ if (flags & eEarlyWakeupStart) return TransactionSchedule::EarlyStart;
+ return TransactionSchedule::Late;
+ }(state.flags);
+
+ setTransactionFlags(eTransactionFlushNeeded, schedule);
}
-void SurfaceFlinger::applyTransactionState(
- const Vector<ComposerState>& states, const Vector<DisplayState>& displays, uint32_t flags,
- const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime,
- const client_cache_t& uncacheBuffer, const int64_t postTime, bool privileged,
- bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
- bool isMainThread) {
- uint32_t transactionFlags = 0;
+void SurfaceFlinger::waitForSynchronousTransaction(
+ const CountDownLatch& transactionCommittedSignal) {
+ // applyTransactionState is called on the main SF thread. While a given process may wish
+ // to wait on synchronous transactions, the main SF thread should apply the transaction and
+ // set the value to notify this after committed.
+ if (!transactionCommittedSignal.wait_until(std::chrono::seconds(5))) {
+ ALOGE("setTransactionState timed out!");
+ }
+}
- if (flags & eAnimation) {
- // For window updates that are part of an animation we must wait for
- // previous animation "frames" to be handled.
- while (!isMainThread && mAnimTransactionPending) {
- status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5));
- if (CC_UNLIKELY(err != NO_ERROR)) {
- // just in case something goes wrong in SF, return to the
- // caller after a few seconds.
- ALOGW_IF(err == TIMED_OUT, "setTransactionState timed out "
- "waiting for previous animation frame");
- mAnimTransactionPending = false;
- break;
- }
+void SurfaceFlinger::signalSynchronousTransactions() {
+ for (auto it = mTransactionCommittedSignals.begin();
+ it != mTransactionCommittedSignals.end();) {
+ if ((*it)->countDown() == 0) {
+ it = mTransactionCommittedSignals.erase(it);
+ } else {
+ it++;
}
}
+}
+status_t SurfaceFlinger::setTransactionState(
+ const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& states,
+ const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
+ bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+ const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
+ ATRACE_CALL();
+
+ uint32_t permissions =
+ callingThreadHasUnscopedSurfaceFlingerAccess() ? Permission::ACCESS_SURFACE_FLINGER : 0;
+ // Avoid checking for rotation permissions if the caller already has ACCESS_SURFACE_FLINGER
+ // permissions.
+ if ((permissions & Permission::ACCESS_SURFACE_FLINGER) ||
+ callingThreadHasRotateSurfaceFlingerAccess()) {
+ permissions |= Permission::ROTATE_SURFACE_FLINGER;
+ }
+
+ if (!(permissions & Permission::ACCESS_SURFACE_FLINGER) &&
+ (flags & (eEarlyWakeupStart | eEarlyWakeupEnd))) {
+ ALOGE("Only WindowManager is allowed to use eEarlyWakeup[Start|End] flags");
+ flags &= ~(eEarlyWakeupStart | eEarlyWakeupEnd);
+ }
+
+ const int64_t postTime = systemTime();
+
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int originPid = ipc->getCallingPid();
+ const int originUid = ipc->getCallingUid();
+ TransactionState state{frameTimelineInfo, states,
+ displays, flags,
+ applyToken, inputWindowCommands,
+ desiredPresentTime, isAutoTimestamp,
+ uncacheBuffer, postTime,
+ permissions, hasListenerCallbacks,
+ listenerCallbacks, originPid,
+ originUid, transactionId};
+
+ // Check for incoming buffer updates and increment the pending buffer count.
+ state.traverseStatesWithBuffers([&](const layer_state_t& state) {
+ mBufferCountTracker.increment(state.surface->localBinder());
+ });
+ queueTransaction(state);
+
+ // Check the pending state to make sure the transaction is synchronous.
+ if (state.transactionCommittedSignal) {
+ waitForSynchronousTransaction(*state.transactionCommittedSignal);
+ }
+
+ return NO_ERROR;
+}
+
+void SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelineInfo,
+ const Vector<ComposerState>& states,
+ const Vector<DisplayState>& displays, uint32_t flags,
+ const InputWindowCommands& inputWindowCommands,
+ const int64_t desiredPresentTime, bool isAutoTimestamp,
+ const client_cache_t& uncacheBuffer,
+ const int64_t postTime, uint32_t permissions,
+ bool hasListenerCallbacks,
+ const std::vector<ListenerCallbacks>& listenerCallbacks,
+ int originPid, int originUid, uint64_t transactionId) {
+ uint32_t transactionFlags = 0;
for (const DisplayState& display : displays) {
transactionFlags |= setDisplayStateLocked(display);
}
@@ -3284,38 +3633,43 @@
// that listeners with SurfaceControls will start registration during setClientStateLocked
// below.
for (const auto& listener : listenerCallbacks) {
- mTransactionCompletedThread.startRegistration(listener);
- mTransactionCompletedThread.endRegistration(listener);
+ mTransactionCallbackInvoker.startRegistration(listener);
+ mTransactionCallbackInvoker.endRegistration(listener);
}
std::unordered_set<ListenerCallbacks, ListenerCallbacksHash> listenerCallbacksWithSurfaces;
uint32_t clientStateFlags = 0;
for (const ComposerState& state : states) {
- clientStateFlags |= setClientStateLocked(state, desiredPresentTime, postTime, privileged,
- listenerCallbacksWithSurfaces);
+ clientStateFlags |=
+ setClientStateLocked(frameTimelineInfo, state, desiredPresentTime, isAutoTimestamp,
+ postTime, permissions, listenerCallbacksWithSurfaces);
if ((flags & eAnimation) && state.state.surface) {
if (const auto layer = fromHandleLocked(state.state.surface).promote(); layer) {
- mScheduler->recordLayerHistory(layer.get(), desiredPresentTime,
+ mScheduler->recordLayerHistory(layer.get(),
+ isAutoTimestamp ? 0 : desiredPresentTime,
LayerHistory::LayerUpdateType::AnimationTX);
}
}
}
for (const auto& listenerCallback : listenerCallbacksWithSurfaces) {
- mTransactionCompletedThread.endRegistration(listenerCallback);
+ mTransactionCallbackInvoker.endRegistration(listenerCallback);
}
// If the state doesn't require a traversal and there are callbacks, send them now
if (!(clientStateFlags & eTraversalNeeded) && hasListenerCallbacks) {
- mTransactionCompletedThread.sendCallbacks();
+ mTransactionCallbackInvoker.sendCallbacks();
}
transactionFlags |= clientStateFlags;
- transactionFlags |= addInputWindowCommands(inputWindowCommands);
+ if (permissions & Permission::ACCESS_SURFACE_FLINGER) {
+ transactionFlags |= addInputWindowCommands(inputWindowCommands);
+ } else if (!inputWindowCommands.empty()) {
+ ALOGE("Only privileged callers are allowed to send input commands.");
+ }
if (uncacheBuffer.isValid()) {
ClientCache::getInstance().erase(uncacheBuffer);
- getRenderEngine().unbindExternalTextureBuffer(uncacheBuffer.id);
}
// If a synchronous transaction is explicitly requested without any changes, force a transaction
@@ -3327,86 +3681,25 @@
transactionFlags = eTransactionNeeded;
}
- // If we are on the main thread, we are about to preform a traversal. Clear the traversal bit
- // so we don't have to wake up again next frame to preform an uneeded traversal.
- if (isMainThread && (transactionFlags & eTraversalNeeded)) {
- transactionFlags = transactionFlags & (~eTraversalNeeded);
- mForceTraversal = true;
- }
-
- const auto transactionStart = [](uint32_t flags) {
- if (flags & eEarlyWakeup) {
- return Scheduler::TransactionStart::Early;
- }
- if (flags & eExplicitEarlyWakeupEnd) {
- return Scheduler::TransactionStart::EarlyEnd;
- }
- if (flags & eExplicitEarlyWakeupStart) {
- return Scheduler::TransactionStart::EarlyStart;
- }
- return Scheduler::TransactionStart::Normal;
- }(flags);
-
if (transactionFlags) {
if (mInterceptor->isEnabled()) {
- mInterceptor->saveTransaction(states, mCurrentState.displays, displays, flags);
+ mInterceptor->saveTransaction(states, mCurrentState.displays, displays, flags,
+ originPid, originUid, transactionId);
}
- // TODO(b/159125966): Remove eEarlyWakeup completly as no client should use this flag
- if (flags & eEarlyWakeup) {
- ALOGW("eEarlyWakeup is deprecated. Use eExplicitEarlyWakeup[Start|End]");
+ // We are on the main thread, we are about to preform a traversal. Clear the traversal bit
+ // so we don't have to wake up again next frame to preform an unnecessary traversal.
+ if (transactionFlags & eTraversalNeeded) {
+ transactionFlags = transactionFlags & (~eTraversalNeeded);
+ mForceTraversal = true;
}
-
- if (!privileged && (flags & (eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd))) {
- ALOGE("Only WindowManager is allowed to use eExplicitEarlyWakeup[Start|End] flags");
- flags &= ~(eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd);
+ if (transactionFlags) {
+ setTransactionFlags(transactionFlags);
}
- // this triggers the transaction
- setTransactionFlags(transactionFlags, transactionStart);
-
if (flags & eAnimation) {
mAnimTransactionPending = true;
}
-
- // if this is a synchronous transaction, wait for it to take effect
- // before returning.
- const bool synchronous = flags & eSynchronous;
- const bool syncInput = inputWindowCommands.syncInputWindows;
- if (!synchronous && !syncInput) {
- return;
- }
-
- if (synchronous) {
- mTransactionPending = true;
- }
- if (syncInput) {
- mPendingSyncInputWindows = true;
- }
-
-
- // applyTransactionState can be called by either the main SF thread or by
- // another process through setTransactionState. While a given process may wish
- // to wait on synchronous transactions, the main SF thread should never
- // be blocked. Therefore, we only wait if isMainThread is false.
- while (!isMainThread && (mTransactionPending || mPendingSyncInputWindows)) {
- status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5));
- if (CC_UNLIKELY(err != NO_ERROR)) {
- // just in case something goes wrong in SF, return to the
- // called after a few seconds.
- ALOGW_IF(err == TIMED_OUT, "setTransactionState timed out!");
- mTransactionPending = false;
- mPendingSyncInputWindows = false;
- break;
- }
- }
- } else {
- // even if a transaction is not needed, we need to update VsyncModulator
- // about explicit early indications
- if (transactionStart == Scheduler::TransactionStart::EarlyStart ||
- transactionStart == Scheduler::TransactionStart::EarlyEnd) {
- mVSyncModulator->setTransactionStart(transactionStart);
- }
}
}
@@ -3435,12 +3728,12 @@
state.orientation = s.orientation;
flags |= eDisplayTransactionNeeded;
}
- if (state.frame != s.frame) {
- state.frame = s.frame;
+ if (state.orientedDisplaySpaceRect != s.orientedDisplaySpaceRect) {
+ state.orientedDisplaySpaceRect = s.orientedDisplaySpaceRect;
flags |= eDisplayTransactionNeeded;
}
- if (state.viewport != s.viewport) {
- state.viewport = s.viewport;
+ if (state.layerStackSpaceRect != s.layerStackSpaceRect) {
+ state.layerStackSpaceRect = s.layerStackSpaceRect;
flags |= eDisplayTransactionNeeded;
}
}
@@ -3471,43 +3764,61 @@
}
uint32_t SurfaceFlinger::setClientStateLocked(
- const ComposerState& composerState, int64_t desiredPresentTime, int64_t postTime,
- bool privileged,
- std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks) {
+ const FrameTimelineInfo& frameTimelineInfo, const ComposerState& composerState,
+ int64_t desiredPresentTime, bool isAutoTimestamp, int64_t postTime, uint32_t permissions,
+ std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& outListenerCallbacks) {
const layer_state_t& s = composerState.state;
+ const bool privileged = permissions & Permission::ACCESS_SURFACE_FLINGER;
+ std::vector<ListenerCallbacks> filteredListeners;
for (auto& listener : s.listeners) {
+ // Starts a registration but separates the callback ids according to callback type. This
+ // allows the callback invoker to send on latch callbacks earlier.
// note that startRegistration will not re-register if the listener has
// already be registered for a prior surface control
- mTransactionCompletedThread.startRegistration(listener);
- listenerCallbacks.insert(listener);
+
+ ListenerCallbacks onCommitCallbacks = listener.filter(CallbackId::Type::ON_COMMIT);
+ if (!onCommitCallbacks.callbackIds.empty()) {
+ mTransactionCallbackInvoker.startRegistration(onCommitCallbacks);
+ filteredListeners.push_back(onCommitCallbacks);
+ outListenerCallbacks.insert(onCommitCallbacks);
+ }
+
+ ListenerCallbacks onCompleteCallbacks = listener.filter(CallbackId::Type::ON_COMPLETE);
+ if (!onCompleteCallbacks.callbackIds.empty()) {
+ mTransactionCallbackInvoker.startRegistration(onCompleteCallbacks);
+ filteredListeners.push_back(onCompleteCallbacks);
+ outListenerCallbacks.insert(onCompleteCallbacks);
+ }
}
+ const uint64_t what = s.what;
+ uint32_t flags = 0;
sp<Layer> layer = nullptr;
if (s.surface) {
- layer = fromHandleLocked(s.surface).promote();
+ if (what & layer_state_t::eLayerCreated) {
+ layer = handleLayerCreatedLocked(s.surface, privileged);
+ if (layer) {
+ // put the created layer into mLayersByLocalBinderToken.
+ mLayersByLocalBinderToken.emplace(s.surface->localBinder(), layer);
+ flags |= eTransactionNeeded | eTraversalNeeded;
+ mLayersAdded = true;
+ }
+ } else {
+ layer = fromHandleLocked(s.surface).promote();
+ }
} else {
// The client may provide us a null handle. Treat it as if the layer was removed.
ALOGW("Attempt to set client state with a null layer handle");
}
if (layer == nullptr) {
for (auto& [listener, callbackIds] : s.listeners) {
- mTransactionCompletedThread.registerUnpresentedCallbackHandle(
+ mTransactionCallbackInvoker.registerUnpresentedCallbackHandle(
new CallbackHandle(listener, callbackIds, s.surface));
}
return 0;
}
- uint32_t flags = 0;
-
- const uint64_t what = s.what;
-
- // If we are deferring transaction, make sure to push the pending state, as otherwise the
- // pending state will also be deferred.
- if (what & layer_state_t::eDeferTransaction_legacy) {
- layer->pushPendingState();
- }
-
// Only set by BLAST adapter layers
if (what & layer_state_t::eProducerDisconnect) {
layer->onDisconnect();
@@ -3539,9 +3850,12 @@
if (what & layer_state_t::eRelativeLayerChanged) {
// NOTE: index needs to be calculated before we update the state
const auto& p = layer->getParent();
+ const auto& relativeHandle = s.relativeLayerSurfaceControl ?
+ s.relativeLayerSurfaceControl->getHandle() : nullptr;
if (p == nullptr) {
ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
- if (layer->setRelativeLayer(s.relativeLayerHandle, s.z) && idx >= 0) {
+ if (layer->setRelativeLayer(relativeHandle, s.z) &&
+ idx >= 0) {
mCurrentState.layersSortedByZ.removeAt(idx);
mCurrentState.layersSortedByZ.add(layer);
// we need traversal (state changed)
@@ -3549,7 +3863,7 @@
flags |= eTransactionNeeded|eTraversalNeeded;
}
} else {
- if (p->setChildRelativeLayer(layer, s.relativeLayerHandle, s.z)) {
+ if (p->setChildRelativeLayer(layer, relativeHandle, s.z)) {
flags |= eTransactionNeeded|eTraversalNeeded;
}
}
@@ -3586,15 +3900,20 @@
// must now be cropped to a non rectangular 8 sided region.
//
// Of course we can fix this in the future. For now, we are lucky, SurfaceControl is
- // private API, and the WindowManager only uses rotation in one case, which is on a top
- // level layer in which cropping is not an issue.
+ // private API, and arbitrary rotation is used in limited use cases, for instance:
+ // - WindowManager only uses rotation in one case, which is on a top level layer in which
+ // cropping is not an issue.
+ // - Launcher, as a privileged app, uses this to transition an application to PiP
+ // (picture-in-picture) mode.
//
// However given that abuse of rotation matrices could lead to surfaces extending outside
- // of cropped areas, we need to prevent non-root clients without permission ACCESS_SURFACE_FLINGER
- // (a.k.a. everyone except WindowManager and tests) from setting non rectangle preserving
- // transformations.
- if (layer->setMatrix(s.matrix, privileged))
- flags |= eTraversalNeeded;
+ // of cropped areas, we need to prevent non-root clients without permission
+ // ACCESS_SURFACE_FLINGER nor ROTATE_SURFACE_FLINGER
+ // (a.k.a. everyone except WindowManager / tests / Launcher) from setting non rectangle
+ // preserving transformations.
+ const bool allowNonRectPreservingTransforms =
+ permissions & Permission::ROTATE_SURFACE_FLINGER;
+ if (layer->setMatrix(s.matrix, allowNonRectPreservingTransforms)) flags |= eTraversalNeeded;
}
if (what & layer_state_t::eTransparentRegionChanged) {
if (layer->setTransparentRegionHint(s.transparentRegion))
@@ -3604,9 +3923,6 @@
if (layer->setFlags(s.flags, s.mask))
flags |= eTraversalNeeded;
}
- if (what & layer_state_t::eCropChanged_legacy) {
- if (layer->setCrop_legacy(s.crop_legacy)) flags |= eTraversalNeeded;
- }
if (what & layer_state_t::eCornerRadiusChanged) {
if (layer->setCornerRadius(s.cornerRadius))
flags |= eTraversalNeeded;
@@ -3614,6 +3930,9 @@
if (what & layer_state_t::eBackgroundBlurRadiusChanged && !mDisableBlurs && mSupportsBlur) {
if (layer->setBackgroundBlurRadius(s.backgroundBlurRadius)) flags |= eTraversalNeeded;
}
+ if (what & layer_state_t::eBlurRegionsChanged) {
+ if (layer->setBlurRegions(s.blurRegions)) flags |= eTraversalNeeded;
+ }
if (what & layer_state_t::eLayerStackChanged) {
ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
// We only allow setting layer stacks for top level layers,
@@ -3634,36 +3953,6 @@
flags |= eTransactionNeeded | eTraversalNeeded | eTransformHintUpdateNeeded;
}
}
- if (what & layer_state_t::eDeferTransaction_legacy) {
- if (s.barrierHandle_legacy != nullptr) {
- layer->deferTransactionUntil_legacy(s.barrierHandle_legacy, s.frameNumber_legacy);
- } else if (s.barrierGbp_legacy != nullptr) {
- const sp<IGraphicBufferProducer>& gbp = s.barrierGbp_legacy;
- if (authenticateSurfaceTextureLocked(gbp)) {
- const auto& otherLayer =
- (static_cast<MonitoredProducer*>(gbp.get()))->getLayer();
- layer->deferTransactionUntil_legacy(otherLayer, s.frameNumber_legacy);
- } else {
- ALOGE("Attempt to defer transaction to to an"
- " unrecognized GraphicBufferProducer");
- }
- }
- // We don't trigger a traversal here because if no other state is
- // changed, we don't want this to cause any more work
- }
- if (what & layer_state_t::eReparentChildren) {
- if (layer->reparentChildren(s.reparentHandle)) {
- flags |= eTransactionNeeded|eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eDetachChildren) {
- layer->detachChildren();
- }
- if (what & layer_state_t::eOverrideScalingModeChanged) {
- layer->setOverrideScalingMode(s.overrideScalingMode);
- // We don't trigger a traversal here because if no other state is
- // changed, we don't want this to cause any more work
- }
if (what & layer_state_t::eTransformChanged) {
if (layer->setTransform(s.transform)) flags |= eTraversalNeeded;
}
@@ -3674,9 +3963,6 @@
if (what & layer_state_t::eCropChanged) {
if (layer->setCrop(s.crop)) flags |= eTraversalNeeded;
}
- if (what & layer_state_t::eFrameChanged) {
- if (layer->setFrame(s.frame)) flags |= eTraversalNeeded;
- }
if (what & layer_state_t::eAcquireFenceChanged) {
if (layer->setAcquireFence(s.acquireFence)) flags |= eTraversalNeeded;
}
@@ -3697,13 +3983,15 @@
}
if (what & layer_state_t::eInputInfoChanged) {
if (privileged) {
- layer->setInputInfo(s.inputInfo);
+ layer->setInputInfo(*s.inputHandle->getInfo());
flags |= eTraversalNeeded;
} else {
ALOGE("Attempt to update InputWindowInfo without permission ACCESS_SURFACE_FLINGER");
}
}
+ std::optional<nsecs_t> dequeueBufferTimestamp;
if (what & layer_state_t::eMetadataChanged) {
+ dequeueBufferTimestamp = s.metadata.getInt64(METADATA_DEQUEUE_TIME);
if (layer->setMetadata(s.metadata)) flags |= eTraversalNeeded;
}
if (what & layer_state_t::eColorSpaceAgnosticChanged) {
@@ -3720,12 +4008,16 @@
}
}
if (what & layer_state_t::eFrameRateChanged) {
- if (ValidateFrameRate(s.frameRate, s.frameRateCompatibility,
- "SurfaceFlinger::setClientStateLocked") &&
- layer->setFrameRate(Layer::FrameRate(s.frameRate,
- Layer::FrameRate::convertCompatibility(
- s.frameRateCompatibility)))) {
- flags |= eTraversalNeeded;
+ if (ValidateFrameRate(s.frameRate, s.frameRateCompatibility, s.changeFrameRateStrategy,
+ "SurfaceFlinger::setClientStateLocked", privileged)) {
+ const auto compatibility =
+ Layer::FrameRate::convertCompatibility(s.frameRateCompatibility);
+ const auto strategy =
+ Layer::FrameRate::convertChangeFrameRateStrategy(s.changeFrameRateStrategy);
+
+ if (layer->setFrameRate(Layer::FrameRate(Fps(s.frameRate), compatibility, strategy))) {
+ flags |= eTraversalNeeded;
+ }
}
}
if (what & layer_state_t::eFixedTransformHintChanged) {
@@ -3733,13 +4025,24 @@
flags |= eTraversalNeeded | eTransformHintUpdateNeeded;
}
}
+ if (what & layer_state_t::eAutoRefreshChanged) {
+ layer->setAutoRefresh(s.autoRefresh);
+ }
+ if (what & layer_state_t::eStretchChanged) {
+ if (layer->setStretchEffect(s.stretchEffect)) {
+ flags |= eTraversalNeeded;
+ }
+ }
// This has to happen after we reparent children because when we reparent to null we remove
// child layers from current state and remove its relative z. If the children are reparented in
// the same transaction, then we have to make sure we reparent the children first so we do not
// lose its relative z order.
if (what & layer_state_t::eReparent) {
bool hadParent = layer->hasParent();
- if (layer->reparent(s.parentHandleForChild)) {
+ auto parentHandle = (s.parentSurfaceControlForChild)
+ ? s.parentSurfaceControlForChild->getHandle()
+ : nullptr;
+ if (layer->reparent(parentHandle)) {
if (!hadParent) {
mCurrentState.layersSortedByZ.remove(layer);
}
@@ -3747,37 +4050,39 @@
}
}
std::vector<sp<CallbackHandle>> callbackHandles;
- if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!s.listeners.empty())) {
- for (auto& [listener, callbackIds] : s.listeners) {
+ if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!filteredListeners.empty())) {
+ for (auto& [listener, callbackIds] : filteredListeners) {
callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface));
}
}
bool bufferChanged = what & layer_state_t::eBufferChanged;
bool cacheIdChanged = what & layer_state_t::eCachedBufferChanged;
- sp<GraphicBuffer> buffer;
+ std::shared_ptr<renderengine::ExternalTexture> buffer;
if (bufferChanged && cacheIdChanged && s.buffer != nullptr) {
- buffer = s.buffer;
- bool success = ClientCache::getInstance().add(s.cachedBuffer, s.buffer);
- if (success) {
- getRenderEngine().cacheExternalTextureBuffer(s.buffer);
- success = ClientCache::getInstance()
- .registerErasedRecipient(s.cachedBuffer,
- wp<ClientCache::ErasedRecipient>(this));
- if (!success) {
- getRenderEngine().unbindExternalTextureBuffer(s.buffer->getId());
- }
- }
+ ClientCache::getInstance().add(s.cachedBuffer, s.buffer);
+ buffer = ClientCache::getInstance().get(s.cachedBuffer);
} else if (cacheIdChanged) {
buffer = ClientCache::getInstance().get(s.cachedBuffer);
- } else if (bufferChanged) {
- buffer = s.buffer;
+ } else if (bufferChanged && s.buffer != nullptr) {
+ buffer = std::make_shared<
+ renderengine::ExternalTexture>(s.buffer, getRenderEngine(),
+ renderengine::ExternalTexture::Usage::READABLE);
}
if (buffer) {
- if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime,
- s.cachedBuffer)) {
+ const bool frameNumberChanged = what & layer_state_t::eFrameNumberChanged;
+ const uint64_t frameNumber = frameNumberChanged
+ ? s.frameNumber
+ : layer->getHeadFrameNumber(-1 /* expectedPresentTime */) + 1;
+
+ if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime, isAutoTimestamp,
+ s.cachedBuffer, frameNumber, dequeueBufferTimestamp, frameTimelineInfo,
+ s.releaseBufferListener)) {
flags |= eTraversalNeeded;
}
+ } else if (frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
+ layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime);
}
+
if (layer->setTransactionCompletedListeners(callbackHandles)) flags |= eTraversalNeeded;
// Do not put anything that updates layer state or modifies flags after
// setTransactionCompletedListener
@@ -3785,17 +4090,12 @@
}
uint32_t SurfaceFlinger::addInputWindowCommands(const InputWindowCommands& inputWindowCommands) {
- uint32_t flags = 0;
- if (inputWindowCommands.syncInputWindows) {
- flags |= eTraversalNeeded;
- }
-
- mPendingInputWindowCommands.merge(inputWindowCommands);
- return flags;
+ bool hasChanges = mInputWindowCommands.merge(inputWindowCommands);
+ return hasChanges ? eTraversalNeeded : 0;
}
status_t SurfaceFlinger::mirrorLayer(const sp<Client>& client, const sp<IBinder>& mirrorFromHandle,
- sp<IBinder>* outHandle) {
+ sp<IBinder>* outHandle, int32_t* outLayerId) {
if (!mirrorFromHandle) {
return NAME_NOT_FOUND;
}
@@ -3820,6 +4120,7 @@
mirrorLayer->mClonedChild = mirrorFrom->createClone();
}
+ *outLayerId = mirrorLayer->sequence;
return addClientLayer(client, *outHandle, nullptr, mirrorLayer, nullptr, nullptr, false,
nullptr /* outTransformHint */);
}
@@ -3828,8 +4129,8 @@
uint32_t h, PixelFormat format, uint32_t flags,
LayerMetadata metadata, sp<IBinder>* handle,
sp<IGraphicBufferProducer>* gbp,
- const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer,
- uint32_t* outTransformHint) {
+ const sp<IBinder>& parentHandle, int32_t* outLayerId,
+ const sp<Layer>& parentLayer, uint32_t* outTransformHint) {
if (int32_t(w|h) < 0) {
ALOGE("createLayer() failed, w or h is negative (w=%d, h=%d)",
int(w), int(h));
@@ -3846,28 +4147,18 @@
std::string uniqueName = getUniqueLayerName(name.string());
- bool primaryDisplayOnly = false;
-
- // window type is WINDOW_TYPE_DONT_SCREENSHOT from SurfaceControl.java
- // TODO b/64227542
- if (metadata.has(METADATA_WINDOW_TYPE)) {
- int32_t windowType = metadata.getInt32(METADATA_WINDOW_TYPE, 0);
- if (windowType == 441731) {
- metadata.setInt32(METADATA_WINDOW_TYPE, InputWindowInfo::TYPE_NAVIGATION_BAR_PANEL);
- primaryDisplayOnly = true;
- }
- }
-
switch (flags & ISurfaceComposerClient::eFXSurfaceMask) {
case ISurfaceComposerClient::eFXSurfaceBufferQueue:
- result = createBufferQueueLayer(client, std::move(uniqueName), w, h, flags,
- std::move(metadata), format, handle, gbp, &layer);
-
- break;
- case ISurfaceComposerClient::eFXSurfaceBufferState:
+ case ISurfaceComposerClient::eFXSurfaceBufferState: {
result = createBufferStateLayer(client, std::move(uniqueName), w, h, flags,
std::move(metadata), handle, &layer);
- break;
+ std::atomic<int32_t>* pendingBufferCounter = layer->getPendingBufferCounter();
+ if (pendingBufferCounter) {
+ std::string counterName = layer->getPendingBufferCounterName();
+ mBufferCountTracker.add((*handle)->localBinder(), counterName,
+ pendingBufferCounter);
+ }
+ } break;
case ISurfaceComposerClient::eFXSurfaceEffect:
// check if buffer size is set for color layer.
if (w > 0 || h > 0) {
@@ -3898,10 +4189,6 @@
return result;
}
- if (primaryDisplayOnly) {
- layer->setPrimaryDisplayOnly();
- }
-
bool addToCurrentState = callingThreadHasUnscopedSurfaceFlingerAccess();
result = addClientLayer(client, *handle, *gbp, layer, parentHandle, parentLayer,
addToCurrentState, outTransformHint);
@@ -3911,6 +4198,7 @@
mInterceptor->saveSurfaceCreation(layer);
setTransactionFlags(eTransactionNeeded);
+ *outLayerId = layer->sequence;
return result;
}
@@ -4016,8 +4304,7 @@
setTransactionFlags(eTransactionNeeded);
}
-void SurfaceFlinger::onHandleDestroyed(sp<Layer>& layer)
-{
+void SurfaceFlinger::onHandleDestroyed(sp<Layer>& layer) {
Mutex::Autolock lock(mStateLock);
// If a layer has a parent, we allow it to out-live it's handle
// with the idea that the parent holds a reference and will eventually
@@ -4031,6 +4318,7 @@
auto it = mLayersByLocalBinderToken.begin();
while (it != mLayersByLocalBinderToken.end()) {
if (it->second == layer) {
+ mBufferCountTracker.remove(it->first->localBinder());
it = mLayersByLocalBinderToken.erase(it);
} else {
it++;
@@ -4058,18 +4346,21 @@
d.token = token;
d.layerStack = 0;
d.orientation = ui::ROTATION_0;
- d.frame.makeInvalid();
- d.viewport.makeInvalid();
+ d.orientedDisplaySpaceRect.makeInvalid();
+ d.layerStackSpaceRect.makeInvalid();
d.width = 0;
d.height = 0;
displays.add(d);
- setTransactionState(state, displays, 0, nullptr, mPendingInputWindowCommands, -1, {}, false,
- {});
+
+ // It should be on the main thread, apply it directly.
+ applyTransactionState(FrameTimelineInfo{}, state, displays, 0, mInputWindowCommands,
+ systemTime(), true, {}, systemTime(), true, false, {}, getpid(), getuid(),
+ 0 /* Undefined transactionId */);
setPowerModeInternal(display, hal::PowerMode::ON);
const nsecs_t vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod);
-
+ mDefaultDisplayTransformHint = display->getTransformHint();
// Use phase of 0 since phase is not known.
// Use latency of 0, which will snap to the ideal latency.
DisplayStatInfo stats{0 /* vsyncTime */, vsyncPeriod};
@@ -4087,10 +4378,8 @@
return;
}
- const auto displayId = display->getId();
- LOG_ALWAYS_FATAL_IF(!displayId);
-
- ALOGD("Setting power mode %d on display %s", mode, to_string(*displayId).c_str());
+ const auto displayId = display->getPhysicalId();
+ ALOGD("Setting power mode %d on display %s", mode, to_string(displayId).c_str());
const hal::PowerMode currentMode = display->getPowerMode();
if (mode == currentMode) {
@@ -4107,9 +4396,9 @@
if (SurfaceFlinger::setSchedFifo(true) != NO_ERROR) {
ALOGW("Couldn't set SCHED_FIFO on display on: %s\n", strerror(errno));
}
- getHwComposer().setPowerMode(*displayId, mode);
+ getHwComposer().setPowerMode(displayId, mode);
if (display->isPrimary() && mode != hal::PowerMode::DOZE_SUSPEND) {
- getHwComposer().setVsyncEnabled(*displayId, mHWCVsyncPendingState);
+ getHwComposer().setVsyncEnabled(displayId, mHWCVsyncPendingState);
mScheduler->onScreenAcquired(mAppConnectionHandle);
mScheduler->resyncToHardwareVsync(true, vsyncPeriod);
}
@@ -4128,14 +4417,14 @@
}
// Make sure HWVsync is disabled before turning off the display
- getHwComposer().setVsyncEnabled(*displayId, hal::Vsync::DISABLE);
+ getHwComposer().setVsyncEnabled(displayId, hal::Vsync::DISABLE);
- getHwComposer().setPowerMode(*displayId, mode);
+ getHwComposer().setPowerMode(displayId, mode);
mVisibleRegionsDirty = true;
// from this point on, SF will stop drawing on this display
} else if (mode == hal::PowerMode::DOZE || mode == hal::PowerMode::ON) {
// Update display while dozing
- getHwComposer().setPowerMode(*displayId, mode);
+ getHwComposer().setPowerMode(displayId, mode);
if (display->isPrimary() && currentMode == hal::PowerMode::DOZE_SUSPEND) {
mScheduler->onScreenAcquired(mAppConnectionHandle);
mScheduler->resyncToHardwareVsync(true, vsyncPeriod);
@@ -4146,10 +4435,10 @@
mScheduler->disableHardwareVsync(true);
mScheduler->onScreenReleased(mAppConnectionHandle);
}
- getHwComposer().setPowerMode(*displayId, mode);
+ getHwComposer().setPowerMode(displayId, mode);
} else {
ALOGE("Attempting to set unknown power mode: %d\n", mode);
- getHwComposer().setPowerMode(*displayId, mode);
+ getHwComposer().setPowerMode(displayId, mode);
}
if (display->isPrimary()) {
@@ -4158,7 +4447,7 @@
mScheduler->setDisplayPowerState(mode == hal::PowerMode::ON);
}
- ALOGD("Finished setting power mode %d on display %s", mode, to_string(*displayId).c_str());
+ ALOGD("Finished setting power mode %d on display %s", mode, to_string(displayId).c_str());
}
void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) {
@@ -4189,17 +4478,18 @@
} else {
static const std::unordered_map<std::string, Dumper> dumpers = {
{"--display-id"s, dumper(&SurfaceFlinger::dumpDisplayIdentificationData)},
- {"--dispsync"s,
- dumper([this](std::string& s) { mScheduler->getPrimaryDispSync().dump(s); })},
+ {"--dispsync"s, dumper([this](std::string& s) { mScheduler->dumpVsync(s); })},
{"--edid"s, argsDumper(&SurfaceFlinger::dumpRawDisplayIdentificationData)},
{"--frame-events"s, dumper(&SurfaceFlinger::dumpFrameEventsLocked)},
{"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)},
{"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)},
{"--list"s, dumper(&SurfaceFlinger::listLayersLocked)},
+ {"--planner"s, argsDumper(&SurfaceFlinger::dumpPlannerInfo)},
{"--static-screen"s, dumper(&SurfaceFlinger::dumpStaticScreenStats)},
{"--timestats"s, protoDumper(&SurfaceFlinger::dumpTimeStats)},
{"--vsync"s, dumper(&SurfaceFlinger::dumpVSync)},
{"--wide-color"s, dumper(&SurfaceFlinger::dumpWideColorInfo)},
+ {"--frametimeline"s, argsDumper(&SurfaceFlinger::dumpFrameTimeline)},
};
const auto flag = args.empty() ? ""s : std::string(String8(args[0]));
@@ -4239,7 +4529,7 @@
status_t SurfaceFlinger::dumpCritical(int fd, const DumpArgs&, bool asProto) {
if (asProto && mTracing.isEnabled()) {
- mTracing.writeToFileAsync();
+ mTracing.writeToFile();
}
return doDump(fd, DumpArgs(), asProto);
@@ -4282,6 +4572,10 @@
mTimeStats->parseArgs(asProto, args, result);
}
+void SurfaceFlinger::dumpFrameTimeline(const DumpArgs& args, std::string& result) const {
+ mFrameTimeline->parseArgs(args, result);
+}
+
// This should only be called from the main thread. Otherwise it would need
// the lock and should use mCurrentState rather than mDrawingState.
void SurfaceFlinger::logFrameStats() {
@@ -4295,9 +4589,6 @@
void SurfaceFlinger::appendSfConfigString(std::string& result) const {
result.append(" [sf");
- if (isLayerTripleBufferingDisabled())
- result.append(" DISABLE_TRIPLE_BUFFERING");
-
StringAppendF(&result, " PRESENT_TIME_OFFSET=%" PRId64, dispSyncPresentTimeOffset);
StringAppendF(&result, " FORCE_HWC_FOR_RBG_TO_YUV=%d", useHwcForRgbToYuv);
StringAppendF(&result, " MAX_VIRT_DISPLAY_DIM=%" PRIu64, maxVirtualDisplaySize);
@@ -4313,31 +4604,25 @@
mRefreshRateStats->dump(result);
result.append("\n");
- mPhaseConfiguration->dump(result);
+ mVsyncConfiguration->dump(result);
StringAppendF(&result,
" present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 " ns\n\n",
dispSyncPresentTimeOffset, getVsyncPeriodFromHWC());
- scheduler::RefreshRateConfigs::Policy policy = mRefreshRateConfigs->getDisplayManagerPolicy();
- StringAppendF(&result,
- "DesiredDisplayConfigSpecs (DisplayManager): default config ID: %d"
- ", primary range: [%.2f %.2f], app request range: [%.2f %.2f]\n\n",
- policy.defaultConfig.value(), policy.primaryRange.min, policy.primaryRange.max,
- policy.appRequestRange.min, policy.appRequestRange.max);
- StringAppendF(&result, "(config override by backdoor: %s)\n\n",
- mDebugDisplayConfigSetByBackdoor ? "yes" : "no");
- scheduler::RefreshRateConfigs::Policy currentPolicy = mRefreshRateConfigs->getCurrentPolicy();
- if (currentPolicy != policy) {
- StringAppendF(&result,
- "DesiredDisplayConfigSpecs (Override): default config ID: %d"
- ", primary range: [%.2f %.2f], app request range: [%.2f %.2f]\n\n",
- currentPolicy.defaultConfig.value(), currentPolicy.primaryRange.min,
- currentPolicy.primaryRange.max, currentPolicy.appRequestRange.min,
- currentPolicy.appRequestRange.max);
- }
+ mRefreshRateConfigs->dump(result);
+
+ StringAppendF(&result, "(mode override by backdoor: %s)\n\n",
+ mDebugDisplayModeSetByBackdoor ? "yes" : "no");
mScheduler->dump(mAppConnectionHandle, result);
- mScheduler->getPrimaryDispSync().dump(result);
+ mScheduler->dumpVsync(result);
+}
+
+void SurfaceFlinger::dumpPlannerInfo(const DumpArgs& args, std::string& result) const {
+ for (const auto& [token, display] : mDisplays) {
+ const auto compositionDisplay = display->getCompositionDisplay();
+ compositionDisplay->dumpPlannerInfo(args, result);
+ }
}
void SurfaceFlinger::dumpStaticScreenStats(std::string& result) const {
@@ -4414,7 +4699,7 @@
void SurfaceFlinger::dumpDisplayIdentificationData(std::string& result) const {
for (const auto& [token, display] : mDisplays) {
- const auto displayId = display->getId();
+ const auto displayId = PhysicalDisplayId::tryCast(display->getId());
if (!displayId) {
continue;
}
@@ -4471,7 +4756,7 @@
// TODO: print out if wide-color mode is active or not
for (const auto& [token, display] : mDisplays) {
- const auto displayId = display->getId();
+ const auto displayId = PhysicalDisplayId::tryCast(display->getId());
if (!displayId) {
continue;
}
@@ -4560,8 +4845,6 @@
result.append("Build configuration:");
colorizer.reset(result);
appendSfConfigString(result);
- appendUiConfigString(result);
- appendGuiConfigString(result);
result.append("\n");
result.append("\nDisplay identification data:\n");
@@ -4604,7 +4887,7 @@
StringAppendF(&result, "Composition layers\n");
mDrawingState.traverseInZOrder([&](Layer* layer) {
auto* compositionState = layer->getCompositionState();
- if (!compositionState) return;
+ if (!compositionState || !compositionState->isVisible) return;
android::base::StringAppendF(&result, "* Layer %p (%s)\n", layer,
layer->getDebugName() ? layer->getDebugName()
@@ -4641,6 +4924,8 @@
getRenderEngine().dump(result);
+ result.append("ClientCache state:\n");
+ ClientCache::getInstance().dump(result);
DebugEGLImageTracker::getInstance()->dump(result);
if (const auto display = getDefaultDisplayDeviceLocked()) {
@@ -4654,15 +4939,22 @@
" gpu_to_cpu_unsupported : %d\n",
mTransactionFlags.load(), !mGpuToCpuSupported);
- if (const auto displayId = getInternalDisplayIdLocked();
- displayId && getHwComposer().isConnected(*displayId)) {
- const auto activeConfig = getHwComposer().getActiveConfig(*displayId);
+ if (const auto display = getDefaultDisplayDeviceLocked()) {
+ std::string fps, xDpi, yDpi;
+ if (const auto activeMode = display->getActiveMode()) {
+ fps = to_string(activeMode->getFps());
+ xDpi = base::StringPrintf("%.2f", activeMode->getDpiX());
+ yDpi = base::StringPrintf("%.2f", activeMode->getDpiY());
+ } else {
+ fps = "unknown";
+ xDpi = "unknown";
+ yDpi = "unknown";
+ }
StringAppendF(&result,
- " refresh-rate : %f fps\n"
- " x-dpi : %f\n"
- " y-dpi : %f\n",
- 1e9 / getHwComposer().getDisplayVsyncPeriod(*displayId),
- activeConfig->getDpiX(), activeConfig->getDpiY());
+ " refresh-rate : %s\n"
+ " x-dpi : %s\n"
+ " y-dpi : %s\n",
+ fps.c_str(), xDpi.c_str(), yDpi.c_str());
}
StringAppendF(&result, " transaction time: %f us\n", inTransactionDuration / 1000.0);
@@ -4677,7 +4969,7 @@
* HWC layer minidump
*/
for (const auto& [token, display] : mDisplays) {
- const auto displayId = display->getId();
+ const auto displayId = HalDisplayId::tryCast(display->getId());
if (!displayId) {
continue;
}
@@ -4710,23 +5002,24 @@
result.append("\n");
}
-void SurfaceFlinger::updateColorMatrixLocked() {
- mat4 colorMatrix;
- if (mGlobalSaturationFactor != 1.0f) {
- // Rec.709 luma coefficients
- float3 luminance{0.213f, 0.715f, 0.072f};
- luminance *= 1.0f - mGlobalSaturationFactor;
- mat4 saturationMatrix = mat4(
- vec4{luminance.r + mGlobalSaturationFactor, luminance.r, luminance.r, 0.0f},
- vec4{luminance.g, luminance.g + mGlobalSaturationFactor, luminance.g, 0.0f},
- vec4{luminance.b, luminance.b, luminance.b + mGlobalSaturationFactor, 0.0f},
- vec4{0.0f, 0.0f, 0.0f, 1.0f}
- );
- colorMatrix = mClientColorMatrix * saturationMatrix * mDaltonizer();
- } else {
- colorMatrix = mClientColorMatrix * mDaltonizer();
+mat4 SurfaceFlinger::calculateColorMatrix(float saturation) {
+ if (saturation == 1) {
+ return mat4();
}
+ float3 luminance{0.213f, 0.715f, 0.072f};
+ luminance *= 1.0f - saturation;
+ mat4 saturationMatrix = mat4(vec4{luminance.r + saturation, luminance.r, luminance.r, 0.0f},
+ vec4{luminance.g, luminance.g + saturation, luminance.g, 0.0f},
+ vec4{luminance.b, luminance.b, luminance.b + saturation, 0.0f},
+ vec4{0.0f, 0.0f, 0.0f, 1.0f});
+ return saturationMatrix;
+}
+
+void SurfaceFlinger::updateColorMatrixLocked() {
+ mat4 colorMatrix =
+ mClientColorMatrix * calculateColorMatrix(mGlobalSaturationFactor) * mDaltonizer();
+
if (mCurrentState.colorMatrix != colorMatrix) {
mCurrentState.colorMatrix = colorMatrix;
mCurrentState.colorMatrixChanged = true;
@@ -4738,33 +5031,38 @@
#pragma clang diagnostic push
#pragma clang diagnostic error "-Wswitch-enum"
switch (static_cast<ISurfaceComposerTag>(code)) {
+ case ENABLE_VSYNC_INJECTIONS:
+ case INJECT_VSYNC:
+ if (!hasMockHwc()) return PERMISSION_DENIED;
+ [[fallthrough]];
// These methods should at minimum make sure that the client requested
// access to SF.
case BOOT_FINISHED:
case CLEAR_ANIMATION_FRAME_STATS:
case CREATE_DISPLAY:
case DESTROY_DISPLAY:
- case ENABLE_VSYNC_INJECTIONS:
case GET_ANIMATION_FRAME_STATS:
+ case OVERRIDE_HDR_TYPES:
case GET_HDR_CAPABILITIES:
- case SET_DESIRED_DISPLAY_CONFIG_SPECS:
- case GET_DESIRED_DISPLAY_CONFIG_SPECS:
+ case SET_DESIRED_DISPLAY_MODE_SPECS:
+ case GET_DESIRED_DISPLAY_MODE_SPECS:
case SET_ACTIVE_COLOR_MODE:
case GET_AUTO_LOW_LATENCY_MODE_SUPPORT:
case SET_AUTO_LOW_LATENCY_MODE:
case GET_GAME_CONTENT_TYPE_SUPPORT:
case SET_GAME_CONTENT_TYPE:
- case INJECT_VSYNC:
case SET_POWER_MODE:
case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES:
case SET_DISPLAY_CONTENT_SAMPLING_ENABLED:
case GET_DISPLAYED_CONTENT_SAMPLE:
- case NOTIFY_POWER_HINT:
+ case NOTIFY_POWER_BOOST:
case SET_GLOBAL_SHADOW_SETTINGS:
case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: {
- // ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN is used by CTS tests, which acquire the
- // necessary permission dynamically. Don't use the permission cache for this check.
- bool usePermissionCache = code != ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN;
+ // ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN and OVERRIDE_HDR_TYPES are used by CTS tests,
+ // which acquire the necessary permission dynamically. Don't use the permission cache
+ // for this check.
+ bool usePermissionCache =
+ code != ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN && code != OVERRIDE_HDR_TYPES;
if (!callingThreadHasUnscopedSurfaceFlingerAccess(usePermissionCache)) {
IPCThreadState* ipc = IPCThreadState::self();
ALOGE("Permission Denial: can't access SurfaceFlinger pid=%d, uid=%d",
@@ -4790,13 +5088,14 @@
// information, so it is OK to pass them.
case AUTHENTICATE_SURFACE:
case GET_ACTIVE_COLOR_MODE:
- case GET_ACTIVE_CONFIG:
+ case GET_ACTIVE_DISPLAY_MODE:
case GET_PHYSICAL_DISPLAY_IDS:
case GET_PHYSICAL_DISPLAY_TOKEN:
case GET_DISPLAY_COLOR_MODES:
case GET_DISPLAY_NATIVE_PRIMARIES:
- case GET_DISPLAY_INFO:
- case GET_DISPLAY_CONFIGS:
+ case GET_STATIC_DISPLAY_INFO:
+ case GET_DYNAMIC_DISPLAY_INFO:
+ case GET_DISPLAY_MODES:
case GET_DISPLAY_STATE:
case GET_DISPLAY_STATS:
case GET_SUPPORTED_FRAME_TIMESTAMPS:
@@ -4812,11 +5111,30 @@
// special permissions.
case SET_FRAME_RATE:
case GET_DISPLAY_BRIGHTNESS_SUPPORT:
- case SET_DISPLAY_BRIGHTNESS: {
+ // captureLayers and captureDisplay will handle the permission check in the function
+ case CAPTURE_LAYERS:
+ case CAPTURE_DISPLAY:
+ case SET_FRAME_TIMELINE_INFO:
+ case GET_GPU_CONTEXT_PRIORITY:
+ case GET_EXTRA_BUFFER_COUNT: {
+ // This is not sensitive information, so should not require permission control.
return OK;
}
- case CAPTURE_LAYERS:
- case CAPTURE_SCREEN:
+ case SET_DISPLAY_BRIGHTNESS:
+ case ADD_HDR_LAYER_INFO_LISTENER:
+ case REMOVE_HDR_LAYER_INFO_LISTENER: {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+ if ((uid != AID_GRAPHICS) &&
+ !PermissionCache::checkPermission(sControlDisplayBrightness, pid, uid)) {
+ ALOGE("Permission Denial: can't control brightness pid=%d, uid=%d", pid, uid);
+ return PERMISSION_DENIED;
+ }
+ return OK;
+ }
+ case ADD_FPS_LISTENER:
+ case REMOVE_FPS_LISTENER:
case ADD_REGION_SAMPLING_LISTENER:
case REMOVE_REGION_SAMPLING_LISTENER: {
// codes that require permission check
@@ -4830,7 +5148,8 @@
}
return OK;
}
- case CAPTURE_SCREEN_BY_ID: {
+ case ADD_TRANSACTION_TRACE_LISTENER:
+ case CAPTURE_DISPLAY_BY_ID: {
IPCThreadState* ipc = IPCThreadState::self();
const int uid = ipc->getCallingUid();
if (uid == AID_ROOT || uid == AID_GRAPHICS || uid == AID_SYSTEM || uid == AID_SHELL) {
@@ -4838,6 +5157,13 @@
}
return PERMISSION_DENIED;
}
+ case ON_PULL_ATOM: {
+ const int uid = IPCThreadState::self()->getCallingUid();
+ if (uid == AID_SYSTEM) {
+ return OK;
+ }
+ return PERMISSION_DENIED;
+ }
}
// These codes are used for the IBinder protocol to either interrogate the recipient
@@ -4848,9 +5174,9 @@
code == IBinder::SYSPROPS_TRANSACTION) {
return OK;
}
- // Numbers from 1000 to 1036 are currently used for backdoors. The code
+ // Numbers from 1000 to 1038 are currently used for backdoors. The code
// in onTransact verifies that the user is root, and has access to use SF.
- if (code >= 1000 && code <= 1036) {
+ if (code >= 1000 && code <= 1039) {
ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
return OK;
}
@@ -4993,14 +5319,14 @@
mForceFullDamage = n != 0;
return NO_ERROR;
}
- case 1018: { // Modify Choreographer's phase offset
+ case 1018: { // Modify Choreographer's duration
n = data.readInt32();
- mScheduler->setPhaseOffset(mAppConnectionHandle, static_cast<nsecs_t>(n));
+ mScheduler->setDuration(mAppConnectionHandle, std::chrono::nanoseconds(n), 0ns);
return NO_ERROR;
}
- case 1019: { // Modify SurfaceFlinger's phase offset
+ case 1019: { // Modify SurfaceFlinger's duration
n = data.readInt32();
- mScheduler->setPhaseOffset(mSfConnectionHandle, static_cast<nsecs_t>(n));
+ mScheduler->setDuration(mSfConnectionHandle, std::chrono::nanoseconds(n), 0ns);
return NO_ERROR;
}
case 1020: { // Layer updates interceptor
@@ -5044,19 +5370,19 @@
}
case 1025: { // Set layer tracing
n = data.readInt32();
+ bool tracingEnabledChanged;
if (n) {
ALOGD("LayerTracing enabled");
- mTracingEnabledChanged = mTracing.enable();
- reply->writeInt32(NO_ERROR);
+ tracingEnabledChanged = mTracing.enable();
+ if (tracingEnabledChanged) {
+ schedule([&]() MAIN_THREAD { mTracing.notify("start"); }).wait();
+ }
} else {
ALOGD("LayerTracing disabled");
- mTracingEnabledChanged = mTracing.disable();
- if (mTracingEnabledChanged) {
- reply->writeInt32(mTracing.writeToFile());
- } else {
- reply->writeInt32(NO_ERROR);
- }
+ tracingEnabledChanged = mTracing.disable();
}
+ mTracingEnabledChanged = tracingEnabledChanged;
+ reply->writeInt32(NO_ERROR);
return NO_ERROR;
}
case 1026: { // Get layer tracing status
@@ -5164,16 +5490,37 @@
return NO_ERROR;
}
case 1035: {
- n = data.readInt32();
- mDebugDisplayConfigSetByBackdoor = false;
- if (n >= 0) {
- const auto displayToken = getInternalDisplayToken();
- status_t result = setActiveConfig(displayToken, n);
- if (result != NO_ERROR) {
- return result;
+ const int modeId = data.readInt32();
+ mDebugDisplayModeSetByBackdoor = false;
+
+ const auto displayId = [&]() -> std::optional<PhysicalDisplayId> {
+ uint64_t inputDisplayId = 0;
+ if (data.readUint64(&inputDisplayId) == NO_ERROR) {
+ const auto token = getPhysicalDisplayToken(
+ static_cast<PhysicalDisplayId>(inputDisplayId));
+ if (!token) {
+ ALOGE("No display with id: %" PRIu64, inputDisplayId);
+ return std::nullopt;
+ }
+
+ return std::make_optional<PhysicalDisplayId>(inputDisplayId);
}
- mDebugDisplayConfigSetByBackdoor = true;
+
+ return getInternalDisplayId();
+ }();
+
+ if (!displayId) {
+ ALOGE("No display found");
+ return NO_ERROR;
}
+
+ status_t result = setActiveMode(getPhysicalDisplayToken(*displayId), modeId);
+ if (result != NO_ERROR) {
+ return result;
+ }
+
+ mDebugDisplayModeSetByBackdoor = true;
+
return NO_ERROR;
}
case 1036: {
@@ -5188,6 +5535,44 @@
}
return NO_ERROR;
}
+ // Inject a hotplug connected event for the primary display. This will deallocate and
+ // reallocate the display state including framebuffers.
+ case 1037: {
+ std::optional<hal::HWDisplayId> hwcId;
+ {
+ Mutex::Autolock lock(mStateLock);
+ hwcId = getHwComposer().getInternalHwcDisplayId();
+ }
+ onHotplugReceived(getBE().mComposerSequenceId, *hwcId, hal::Connection::CONNECTED);
+ return NO_ERROR;
+ }
+ // Modify the max number of display frames stored within FrameTimeline
+ case 1038: {
+ n = data.readInt32();
+ if (n < 0 || n > MAX_ALLOWED_DISPLAY_FRAMES) {
+ ALOGW("Invalid max size. Maximum allowed is %d", MAX_ALLOWED_DISPLAY_FRAMES);
+ return BAD_VALUE;
+ }
+ if (n == 0) {
+ // restore to default
+ mFrameTimeline->reset();
+ return NO_ERROR;
+ }
+ mFrameTimeline->setMaxDisplayFrames(n);
+ return NO_ERROR;
+ }
+ case 1039: {
+ PhysicalDisplayId displayId = [&]() {
+ Mutex::Autolock lock(mStateLock);
+ return getDefaultDisplayDeviceLocked()->getPhysicalId();
+ }();
+
+ auto inUid = static_cast<uid_t>(data.readInt32());
+ const auto refreshRate = data.readFloat();
+ mScheduler->setPreferredRefreshRateForUid(FrameRateOverride{inUid, refreshRate});
+ mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId);
+ return NO_ERROR;
+ }
}
}
return err;
@@ -5213,17 +5598,16 @@
// Update the overlay on the main thread to avoid race conditions with
// mRefreshRateConfigs->getCurrentRefreshRate()
static_cast<void>(schedule([=] {
- const auto desiredActiveConfig = getDesiredActiveConfig();
- const auto& current = desiredActiveConfig
- ? mRefreshRateConfigs->getRefreshRateFromConfigId(desiredActiveConfig->configId)
- : mRefreshRateConfigs->getCurrentRefreshRate();
- const auto& min = mRefreshRateConfigs->getMinRefreshRate();
+ const auto desiredActiveMode = getDesiredActiveMode();
+ const std::optional<DisplayModeId> desiredModeId =
+ desiredActiveMode ? std::make_optional(desiredActiveMode->modeId) : std::nullopt;
- if (current != min) {
- const bool timerExpired = mKernelIdleTimerEnabled && expired;
-
+ const bool timerExpired = mKernelIdleTimerEnabled && expired;
+ const auto newRefreshRate =
+ mRefreshRateConfigs->onKernelTimerChanged(desiredModeId, timerExpired);
+ if (newRefreshRate) {
if (Mutex::Autolock lock(mStateLock); mRefreshRateOverlay) {
- mRefreshRateOverlay->changeRefreshRate(timerExpired ? min : current);
+ mRefreshRateOverlay->changeRefreshRate(*newRefreshRate);
}
mEventQueue->invalidate();
}
@@ -5272,45 +5656,6 @@
const int mApi;
};
-status_t SurfaceFlinger::captureScreen(const sp<IBinder>& displayToken,
- sp<GraphicBuffer>* outBuffer, bool& outCapturedSecureLayers,
- Dataspace reqDataspace, ui::PixelFormat reqPixelFormat,
- const Rect& sourceCrop, uint32_t reqWidth,
- uint32_t reqHeight, bool useIdentityTransform,
- ui::Rotation rotation, bool captureSecureLayers) {
- ATRACE_CALL();
-
- if (!displayToken) return BAD_VALUE;
-
- auto renderAreaRotation = ui::Transform::toRotationFlags(rotation);
- if (renderAreaRotation == ui::Transform::ROT_INVALID) {
- ALOGE("%s: Invalid rotation: %s", __FUNCTION__, toCString(rotation));
- renderAreaRotation = ui::Transform::ROT_0;
- }
-
- sp<DisplayDevice> display;
- {
- Mutex::Autolock lock(mStateLock);
-
- display = getDisplayDeviceLocked(displayToken);
- if (!display) return NAME_NOT_FOUND;
-
- // set the requested width/height to the logical display viewport size
- // by default
- if (reqWidth == 0 || reqHeight == 0) {
- reqWidth = uint32_t(display->getViewport().width());
- reqHeight = uint32_t(display->getViewport().height());
- }
- }
-
- DisplayRenderArea renderArea(display, sourceCrop, reqWidth, reqHeight, reqDataspace,
- renderAreaRotation, captureSecureLayers);
- auto traverseLayers = std::bind(&SurfaceFlinger::traverseLayersInDisplay, this, display,
- std::placeholders::_1);
- return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat,
- useIdentityTransform, outCapturedSecureLayers);
-}
-
static Dataspace pickDataspaceFromColorMode(const ColorMode colorMode) {
switch (colorMode) {
case ColorMode::DISPLAY_P3:
@@ -5323,6 +5668,24 @@
}
}
+static status_t validateScreenshotPermissions(const CaptureArgs& captureArgs) {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+ if (uid == AID_GRAPHICS || PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) {
+ return OK;
+ }
+
+ // If the caller doesn't have the correct permissions but is only attempting to screenshot
+ // itself, we allow it to continue.
+ if (captureArgs.uid == uid) {
+ return OK;
+ }
+
+ ALOGE("Permission Denial: can't take screenshot pid=%d, uid=%d", pid, uid);
+ return PERMISSION_DENIED;
+}
+
status_t SurfaceFlinger::setSchedFifo(bool enabled) {
static constexpr int kFifoPriority = 2;
static constexpr int kOtherPriority = 0;
@@ -5344,8 +5707,8 @@
}
sp<DisplayDevice> SurfaceFlinger::getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) {
- const sp<IBinder> displayToken = getPhysicalDisplayTokenLocked(DisplayId{displayOrLayerStack});
- if (displayToken) {
+ if (const sp<IBinder> displayToken =
+ getPhysicalDisplayTokenLocked(PhysicalDisplayId{displayOrLayerStack})) {
return getDisplayDeviceLocked(displayToken);
}
// Couldn't find display by displayId. Try to get display by layerStack since virtual displays
@@ -5353,6 +5716,15 @@
return getDisplayByLayerStack(displayOrLayerStack);
}
+sp<DisplayDevice> SurfaceFlinger::getDisplayById(DisplayId displayId) const {
+ for (const auto& [token, display] : mDisplays) {
+ if (display->getId() == displayId) {
+ return display;
+ }
+ }
+ return nullptr;
+}
+
sp<DisplayDevice> SurfaceFlinger::getDisplayByLayerStack(uint64_t layerStack) {
for (const auto& [token, display] : mDisplays) {
if (display->getLayerStack() == layerStack) {
@@ -5362,154 +5734,114 @@
return nullptr;
}
-status_t SurfaceFlinger::captureScreen(uint64_t displayOrLayerStack, Dataspace* outDataspace,
- sp<GraphicBuffer>* outBuffer) {
- sp<DisplayDevice> display;
- uint32_t width;
- uint32_t height;
- ui::Transform::RotationFlags captureOrientation;
+status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args,
+ const sp<IScreenCaptureListener>& captureListener) {
+ ATRACE_CALL();
+
+ status_t validate = validateScreenshotPermissions(args);
+ if (validate != OK) {
+ return validate;
+ }
+
+ if (!args.displayToken) return BAD_VALUE;
+
+ wp<DisplayDevice> displayWeak;
+ ui::LayerStack layerStack;
+ ui::Size reqSize(args.width, args.height);
+ ui::Dataspace dataspace;
{
Mutex::Autolock lock(mStateLock);
- display = getDisplayByIdOrLayerStack(displayOrLayerStack);
+ sp<DisplayDevice> display = getDisplayDeviceLocked(args.displayToken);
+ if (!display) return NAME_NOT_FOUND;
+ displayWeak = display;
+ layerStack = display->getLayerStack();
+
+ // set the requested width/height to the logical display layer stack rect size by default
+ if (args.width == 0 || args.height == 0) {
+ reqSize = display->getLayerStackSpaceRect().getSize();
+ }
+
+ // The dataspace is depended on the color mode of display, that could use non-native mode
+ // (ex. displayP3) to enhance the content, but some cases are checking native RGB in bytes,
+ // and failed if display is not in native mode. This provide a way to force using native
+ // colors when capture.
+ dataspace = args.dataspace;
+ if (dataspace == ui::Dataspace::UNKNOWN) {
+ const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode;
+ dataspace = pickDataspaceFromColorMode(colorMode);
+ }
+ }
+
+ RenderAreaFuture renderAreaFuture = ftl::defer([=] {
+ return DisplayRenderArea::create(displayWeak, args.sourceCrop, reqSize, dataspace,
+ args.useIdentityTransform, args.captureSecureLayers);
+ });
+
+ auto traverseLayers = [this, args, layerStack](const LayerVector::Visitor& visitor) {
+ traverseLayersInLayerStack(layerStack, args.uid, visitor);
+ };
+
+ return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize,
+ args.pixelFormat, args.allowProtected, args.grayscale,
+ captureListener);
+}
+
+status_t SurfaceFlinger::captureDisplay(uint64_t displayOrLayerStack,
+ const sp<IScreenCaptureListener>& captureListener) {
+ ui::LayerStack layerStack;
+ wp<DisplayDevice> displayWeak;
+ ui::Size size;
+ ui::Dataspace dataspace;
+ {
+ Mutex::Autolock lock(mStateLock);
+ sp<DisplayDevice> display = getDisplayByIdOrLayerStack(displayOrLayerStack);
if (!display) {
return NAME_NOT_FOUND;
}
+ layerStack = display->getLayerStack();
+ displayWeak = display;
- width = uint32_t(display->getViewport().width());
- height = uint32_t(display->getViewport().height());
+ size = display->getLayerStackSpaceRect().getSize();
- const auto orientation = display->getOrientation();
- captureOrientation = ui::Transform::toRotationFlags(orientation);
-
- switch (captureOrientation) {
- case ui::Transform::ROT_90:
- captureOrientation = ui::Transform::ROT_270;
- break;
-
- case ui::Transform::ROT_270:
- captureOrientation = ui::Transform::ROT_90;
- break;
-
- case ui::Transform::ROT_INVALID:
- ALOGE("%s: Invalid orientation: %s", __FUNCTION__, toCString(orientation));
- captureOrientation = ui::Transform::ROT_0;
- break;
-
- default:
- break;
- }
- *outDataspace =
+ dataspace =
pickDataspaceFromColorMode(display->getCompositionDisplay()->getState().colorMode);
}
- DisplayRenderArea renderArea(display, Rect(), width, height, *outDataspace, captureOrientation,
- false /* captureSecureLayers */);
+ RenderAreaFuture renderAreaFuture = ftl::defer([=] {
+ return DisplayRenderArea::create(displayWeak, Rect(), size, dataspace,
+ false /* useIdentityTransform */,
+ false /* captureSecureLayers */);
+ });
- auto traverseLayers = std::bind(&SurfaceFlinger::traverseLayersInDisplay, this, display,
- std::placeholders::_1);
- bool ignored = false;
- return captureScreenCommon(renderArea, traverseLayers, outBuffer, ui::PixelFormat::RGBA_8888,
- false /* useIdentityTransform */,
- ignored /* outCapturedSecureLayers */);
-}
-
-status_t SurfaceFlinger::captureLayers(
- const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer,
- const Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
- const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& excludeHandles,
- float frameScale, bool childrenOnly) {
- ATRACE_CALL();
-
- class LayerRenderArea : public RenderArea {
- public:
- LayerRenderArea(SurfaceFlinger* flinger, const sp<Layer>& layer, const Rect crop,
- int32_t reqWidth, int32_t reqHeight, Dataspace reqDataSpace,
- bool childrenOnly, const Rect& displayViewport)
- : RenderArea(reqWidth, reqHeight, CaptureFill::CLEAR, reqDataSpace, displayViewport),
- mLayer(layer),
- mCrop(crop),
- mNeedsFiltering(false),
- mFlinger(flinger),
- mChildrenOnly(childrenOnly) {}
- const ui::Transform& getTransform() const override { return mTransform; }
- Rect getBounds() const override { return mLayer->getBufferSize(mLayer->getDrawingState()); }
- int getHeight() const override {
- return mLayer->getBufferSize(mLayer->getDrawingState()).getHeight();
- }
- int getWidth() const override {
- return mLayer->getBufferSize(mLayer->getDrawingState()).getWidth();
- }
- bool isSecure() const override { return false; }
- bool needsFiltering() const override { return mNeedsFiltering; }
- sp<const DisplayDevice> getDisplayDevice() const override { return nullptr; }
- Rect getSourceCrop() const override {
- if (mCrop.isEmpty()) {
- return getBounds();
- } else {
- return mCrop;
- }
- }
- class ReparentForDrawing {
- public:
- const sp<Layer>& oldParent;
- const sp<Layer>& newParent;
-
- ReparentForDrawing(const sp<Layer>& oldParent, const sp<Layer>& newParent,
- const Rect& drawingBounds)
- : oldParent(oldParent), newParent(newParent) {
- // Compute and cache the bounds for the new parent layer.
- newParent->computeBounds(drawingBounds.toFloatRect(), ui::Transform(),
- 0.f /* shadowRadius */);
- oldParent->setChildrenDrawingParent(newParent);
- }
- ~ReparentForDrawing() { oldParent->setChildrenDrawingParent(oldParent); }
- };
-
- void render(std::function<void()> drawLayers) override {
- const Rect sourceCrop = getSourceCrop();
- // no need to check rotation because there is none
- mNeedsFiltering = sourceCrop.width() != getReqWidth() ||
- sourceCrop.height() != getReqHeight();
-
- if (!mChildrenOnly) {
- mTransform = mLayer->getTransform().inverse();
- drawLayers();
- } else {
- uint32_t w = static_cast<uint32_t>(getWidth());
- uint32_t h = static_cast<uint32_t>(getHeight());
- // In the "childrenOnly" case we reparent the children to a screenshot
- // layer which has no properties set and which does not draw.
- sp<ContainerLayer> screenshotParentLayer =
- mFlinger->getFactory().createContainerLayer({mFlinger, nullptr,
- "Screenshot Parent"s, w, h, 0,
- LayerMetadata()});
-
- ReparentForDrawing reparent(mLayer, screenshotParentLayer, sourceCrop);
- drawLayers();
- }
- }
-
- private:
- const sp<Layer> mLayer;
- const Rect mCrop;
-
- ui::Transform mTransform;
- bool mNeedsFiltering;
-
- SurfaceFlinger* mFlinger;
- const bool mChildrenOnly;
+ auto traverseLayers = [this, layerStack](const LayerVector::Visitor& visitor) {
+ traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, visitor);
};
- int reqWidth = 0;
- int reqHeight = 0;
+ return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, size,
+ ui::PixelFormat::RGBA_8888, false /* allowProtected */,
+ false /* grayscale */, captureListener);
+}
+
+status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
+ const sp<IScreenCaptureListener>& captureListener) {
+ ATRACE_CALL();
+
+ status_t validate = validateScreenshotPermissions(args);
+ if (validate != OK) {
+ return validate;
+ }
+
+ ui::Size reqSize;
sp<Layer> parent;
- Rect crop(sourceCrop);
+ Rect crop(args.sourceCrop);
std::unordered_set<sp<Layer>, ISurfaceComposer::SpHash<Layer>> excludeLayers;
- Rect displayViewport;
+ Rect layerStackSpaceRect;
+ ui::Dataspace dataspace;
+ bool captureSecureLayers;
{
Mutex::Autolock lock(mStateLock);
- parent = fromHandleLocked(layerHandleBinder).promote();
+ parent = fromHandleLocked(args.layerHandle).promote();
if (parent == nullptr || parent->isRemovedFromCurrentState()) {
ALOGE("captureLayers called with an invalid or removed parent");
return NAME_NOT_FOUND;
@@ -5523,25 +5855,24 @@
}
Rect parentSourceBounds = parent->getCroppedBufferSize(parent->getCurrentState());
- if (sourceCrop.width() <= 0) {
+ if (args.sourceCrop.width() <= 0) {
crop.left = 0;
crop.right = parentSourceBounds.getWidth();
}
- if (sourceCrop.height() <= 0) {
+ if (args.sourceCrop.height() <= 0) {
crop.top = 0;
crop.bottom = parentSourceBounds.getHeight();
}
- if (crop.isEmpty() || frameScale <= 0.0f) {
+ if (crop.isEmpty() || args.frameScaleX <= 0.0f || args.frameScaleY <= 0.0f) {
// Error out if the layer has no source bounds (i.e. they are boundless) and a source
// crop was not specified, or an invalid frame scale was provided.
return BAD_VALUE;
}
- reqWidth = crop.width() * frameScale;
- reqHeight = crop.height() * frameScale;
+ reqSize = ui::Size(crop.width() * args.frameScaleX, crop.height() * args.frameScaleY);
- for (const auto& handle : excludeHandles) {
+ for (const auto& handle : args.excludeHandles) {
sp<Layer> excludeLayer = fromHandleLocked(handle).promote();
if (excludeLayer != nullptr) {
excludeLayers.emplace(excludeLayer);
@@ -5556,25 +5887,43 @@
return NAME_NOT_FOUND;
}
- displayViewport = display->getViewport();
+ layerStackSpaceRect = display->getLayerStackSpaceRect();
+
+ // The dataspace is depended on the color mode of display, that could use non-native mode
+ // (ex. displayP3) to enhance the content, but some cases are checking native RGB in bytes,
+ // and failed if display is not in native mode. This provide a way to force using native
+ // colors when capture.
+ dataspace = args.dataspace;
+ if (dataspace == ui::Dataspace::UNKNOWN) {
+ const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode;
+ dataspace = pickDataspaceFromColorMode(colorMode);
+ }
+
+ captureSecureLayers = args.captureSecureLayers && display->isSecure();
} // mStateLock
// really small crop or frameScale
- if (reqWidth <= 0) {
- reqWidth = 1;
+ if (reqSize.width <= 0) {
+ reqSize.width = 1;
}
- if (reqHeight <= 0) {
- reqHeight = 1;
+ if (reqSize.height <= 0) {
+ reqSize.height = 1;
}
- LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, reqDataspace, childrenOnly,
- displayViewport);
- auto traverseLayers = [parent, childrenOnly,
- &excludeLayers](const LayerVector::Visitor& visitor) {
+ bool childrenOnly = args.childrenOnly;
+ RenderAreaFuture renderAreaFuture = ftl::defer([=]() -> std::unique_ptr<RenderArea> {
+ return std::make_unique<LayerRenderArea>(*this, parent, crop, reqSize, dataspace,
+ childrenOnly, layerStackSpaceRect,
+ captureSecureLayers);
+ });
+
+ auto traverseLayers = [parent, args, excludeLayers](const LayerVector::Visitor& visitor) {
parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
if (!layer->isVisible()) {
return;
- } else if (childrenOnly && layer == parent.get()) {
+ } else if (args.childrenOnly && layer == parent.get()) {
+ return;
+ } else if (args.uid != CaptureArgs::UNSET_UID && args.uid != layer->getOwnerUid()) {
return;
}
@@ -5590,84 +5939,128 @@
});
};
- bool outCapturedSecureLayers = false;
- return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat, false,
- outCapturedSecureLayers);
+ return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize,
+ args.pixelFormat, args.allowProtected, args.grayscale,
+ captureListener);
}
-status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea,
+status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture,
TraverseLayersFunction traverseLayers,
- sp<GraphicBuffer>* outBuffer,
- const ui::PixelFormat reqPixelFormat,
- bool useIdentityTransform,
- bool& outCapturedSecureLayers) {
+ ui::Size bufferSize, ui::PixelFormat reqPixelFormat,
+ bool allowProtected, bool grayscale,
+ const sp<IScreenCaptureListener>& captureListener) {
ATRACE_CALL();
- // TODO(b/116112787) Make buffer usage a parameter.
- const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
- GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER;
- *outBuffer =
- getFactory().createGraphicBuffer(renderArea.getReqWidth(), renderArea.getReqHeight(),
- static_cast<android_pixel_format>(reqPixelFormat), 1,
- usage, "screenshot");
+ // Loop over all visible layers to see whether there's any protected layer. A protected layer is
+ // typically a layer with DRM contents, or have the GRALLOC_USAGE_PROTECTED set on the buffer.
+ // A protected layer has no implication on whether it's secure, which is explicitly set by
+ // application to avoid being screenshot or drawn via unsecure display.
+ const bool supportsProtected = getRenderEngine().supportsProtectedContent();
+ bool hasProtectedLayer = false;
+ if (allowProtected && supportsProtected) {
+ hasProtectedLayer = schedule([=]() {
+ bool protectedLayerFound = false;
+ traverseLayers([&](Layer* layer) {
+ protectedLayerFound = protectedLayerFound ||
+ (layer->isVisible() && layer->isProtected());
+ });
+ return protectedLayerFound;
+ }).get();
+ }
- return captureScreenCommon(renderArea, traverseLayers, *outBuffer, useIdentityTransform,
- false /* regionSampling */, outCapturedSecureLayers);
+ const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER |
+ GRALLOC_USAGE_HW_TEXTURE |
+ (hasProtectedLayer && allowProtected && supportsProtected
+ ? GRALLOC_USAGE_PROTECTED
+ : GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
+ sp<GraphicBuffer> buffer =
+ getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(),
+ static_cast<android_pixel_format>(reqPixelFormat),
+ 1 /* layerCount */, usage, "screenshot");
+
+ const status_t bufferStatus = buffer->initCheck();
+ LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "captureScreenCommon: Buffer failed to allocate: %d",
+ bufferStatus);
+ const auto texture = std::make_shared<
+ renderengine::ExternalTexture>(buffer, getRenderEngine(),
+ renderengine::ExternalTexture::Usage::WRITEABLE);
+ return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, texture,
+ false /* regionSampling */, grayscale, captureListener);
}
-status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea,
- TraverseLayersFunction traverseLayers,
- const sp<GraphicBuffer>& buffer,
- bool useIdentityTransform, bool regionSampling,
- bool& outCapturedSecureLayers) {
+status_t SurfaceFlinger::captureScreenCommon(
+ RenderAreaFuture renderAreaFuture, TraverseLayersFunction traverseLayers,
+ const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
+ bool grayscale, const sp<IScreenCaptureListener>& captureListener) {
+ ATRACE_CALL();
+
+ if (captureListener == nullptr) {
+ ALOGE("capture screen must provide a capture listener callback");
+ return BAD_VALUE;
+ }
+
const int uid = IPCThreadState::self()->getCallingUid();
const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM;
- status_t result;
- int syncFd;
+ static_cast<void>(schedule([=, renderAreaFuture = std::move(renderAreaFuture)]() mutable {
+ if (mRefreshPending) {
+ ALOGW("Skipping screenshot for now");
+ captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer, regionSampling,
+ grayscale, captureListener);
+ return;
+ }
+ ScreenCaptureResults captureResults;
+ std::unique_ptr<RenderArea> renderArea = renderAreaFuture.get();
+ if (!renderArea) {
+ ALOGW("Skipping screen capture because of invalid render area.");
+ captureResults.result = NO_MEMORY;
+ captureListener->onScreenCaptureCompleted(captureResults);
+ return;
+ }
- do {
- std::tie(result, syncFd) =
- schedule([&] {
- if (mRefreshPending) {
- ATRACE_NAME("Skipping screenshot for now");
- return std::make_pair(EAGAIN, -1);
- }
+ status_t result = NO_ERROR;
+ renderArea->render([&] {
+ result = renderScreenImplLocked(*renderArea, traverseLayers, buffer, forSystem,
+ regionSampling, grayscale, captureResults);
+ });
- status_t result = NO_ERROR;
- int fd = -1;
+ captureResults.result = result;
+ captureListener->onScreenCaptureCompleted(captureResults);
+ }));
- Mutex::Autolock lock(mStateLock);
- renderArea.render([&] {
- result = captureScreenImplLocked(renderArea, traverseLayers, buffer.get(),
- useIdentityTransform, forSystem, &fd,
- regionSampling, outCapturedSecureLayers);
- });
-
- return std::make_pair(result, fd);
- }).get();
- } while (result == EAGAIN);
-
- if (result == NO_ERROR) {
- sync_wait(syncFd, -1);
- close(syncFd);
- }
-
- return result;
+ return NO_ERROR;
}
-void SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea,
- TraverseLayersFunction traverseLayers,
- ANativeWindowBuffer* buffer, bool useIdentityTransform,
- bool regionSampling, int* outSyncFd) {
+status_t SurfaceFlinger::renderScreenImplLocked(
+ const RenderArea& renderArea, TraverseLayersFunction traverseLayers,
+ const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool forSystem,
+ bool regionSampling, bool grayscale, ScreenCaptureResults& captureResults) {
ATRACE_CALL();
+ traverseLayers([&](Layer* layer) {
+ captureResults.capturedSecureLayers =
+ captureResults.capturedSecureLayers || (layer->isVisible() && layer->isSecure());
+ });
+
+ const bool useProtected = buffer->getBuffer()->getUsage() & GRALLOC_USAGE_PROTECTED;
+
+ // We allow the system server to take screenshots of secure layers for
+ // use in situations like the Screen-rotation animation and place
+ // the impetus on WindowManager to not persist them.
+ if (captureResults.capturedSecureLayers && !forSystem) {
+ ALOGW("FB is protected: PERMISSION_DENIED");
+ return PERMISSION_DENIED;
+ }
+
+ captureResults.buffer = buffer->getBuffer();
+ captureResults.capturedDataspace = renderArea.getReqDataSpace();
+
const auto reqWidth = renderArea.getReqWidth();
const auto reqHeight = renderArea.getReqHeight();
const auto sourceCrop = renderArea.getSourceCrop();
const auto transform = renderArea.getTransform();
const auto rotation = renderArea.getRotationFlags();
- const auto& displayViewport = renderArea.getDisplayViewport();
+ const auto& layerStackSpaceRect = renderArea.getLayerStackSpaceRect();
renderengine::DisplaySettings clientCompositionDisplay;
std::vector<compositionengine::LayerFE::LayerSettings> clientCompositionLayers;
@@ -5681,6 +6074,9 @@
clientCompositionDisplay.outputDataspace = renderArea.getReqDataSpace();
clientCompositionDisplay.maxLuminance = DisplayDevice::sDefaultMaxLumiance;
+ const float colorSaturation = grayscale ? 0 : 1;
+ clientCompositionDisplay.colorTransform = calculateColorMatrix(colorSaturation);
+
const float alpha = RenderArea::getCaptureFillValue(renderArea.getCaptureFill());
compositionengine::LayerFE::LayerSettings fillLayer;
@@ -5694,21 +6090,23 @@
const auto display = renderArea.getDisplayDevice();
std::vector<Layer*> renderedLayers;
Region clearRegion = Region::INVALID_REGION;
+ bool disableBlurs = false;
traverseLayers([&](Layer* layer) {
- const bool supportProtectedContent = false;
+ disableBlurs |= layer->getCurrentState().sidebandStream != nullptr;
+
Region clip(renderArea.getBounds());
compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{
clip,
- useIdentityTransform,
layer->needsFilteringForScreenshots(display.get(), transform) ||
renderArea.needsFiltering(),
renderArea.isSecure(),
- supportProtectedContent,
+ useProtected,
clearRegion,
- displayViewport,
+ layerStackSpaceRect,
clientCompositionDisplay.outputDataspace,
- true, /* realContentIsVisible */
+ true, /* realContentIsVisible */
false, /* clearContent */
+ disableBlurs,
};
std::vector<compositionengine::LayerFE::LayerSettings> results =
layer->prepareClientCompositionList(targetSettings);
@@ -5723,11 +6121,13 @@
settings.backgroundBlurRadius = 0;
}
}
+
clientCompositionLayers.insert(clientCompositionLayers.end(),
std::make_move_iterator(results.begin()),
std::make_move_iterator(results.end()));
renderedLayers.push_back(layer);
}
+
});
std::vector<const renderengine::LayerSettings*> clientCompositionLayerPointers(
@@ -5741,51 +6141,29 @@
// there is no need for synchronization with the GPU.
base::unique_fd bufferFence;
base::unique_fd drawFence;
- getRenderEngine().useProtectedContext(false);
+ getRenderEngine().useProtectedContext(useProtected);
+
+ const constexpr bool kUseFramebufferCache = false;
getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, buffer,
- /*useFramebufferCache=*/false, std::move(bufferFence), &drawFence);
+ kUseFramebufferCache, std::move(bufferFence), &drawFence);
- *outSyncFd = drawFence.release();
-
- if (*outSyncFd >= 0) {
- sp<Fence> releaseFence = new Fence(dup(*outSyncFd));
+ if (drawFence >= 0) {
+ sp<Fence> releaseFence = new Fence(dup(drawFence));
for (auto* layer : renderedLayers) {
layer->onLayerDisplayed(releaseFence);
}
}
-}
-status_t SurfaceFlinger::captureScreenImplLocked(const RenderArea& renderArea,
- TraverseLayersFunction traverseLayers,
- ANativeWindowBuffer* buffer,
- bool useIdentityTransform, bool forSystem,
- int* outSyncFd, bool regionSampling,
- bool& outCapturedSecureLayers) {
- ATRACE_CALL();
+ captureResults.fence = new Fence(drawFence.release());
+ // Always switch back to unprotected context.
+ getRenderEngine().useProtectedContext(false);
- traverseLayers([&](Layer* layer) {
- outCapturedSecureLayers =
- outCapturedSecureLayers || (layer->isVisible() && layer->isSecure());
- });
-
- // We allow the system server to take screenshots of secure layers for
- // use in situations like the Screen-rotation animation and place
- // the impetus on WindowManager to not persist them.
- if (outCapturedSecureLayers && !forSystem) {
- ALOGW("FB is protected: PERMISSION_DENIED");
- return PERMISSION_DENIED;
- }
- renderScreenImplLocked(renderArea, traverseLayers, buffer, useIdentityTransform, regionSampling,
- outSyncFd);
return NO_ERROR;
}
void SurfaceFlinger::setInputWindowsFinished() {
Mutex::Autolock _l(mStateLock);
-
- mPendingSyncInputWindows = false;
-
- mTransactionCV.broadcast();
+ signalSynchronousTransactions();
}
// ---------------------------------------------------------------------------
@@ -5802,28 +6180,31 @@
layersSortedByZ.traverseInReverseZOrder(stateSet, visitor);
}
-void SurfaceFlinger::traverseLayersInDisplay(const sp<const DisplayDevice>& display,
- const LayerVector::Visitor& visitor) {
+void SurfaceFlinger::traverseLayersInLayerStack(ui::LayerStack layerStack, const int32_t uid,
+ const LayerVector::Visitor& visitor) {
// We loop through the first level of layers without traversing,
// as we need to determine which layers belong to the requested display.
for (const auto& layer : mDrawingState.layersSortedByZ) {
- if (!layer->belongsToDisplay(display->getLayerStack(), false)) {
+ if (!layer->belongsToDisplay(layerStack)) {
continue;
}
// relative layers are traversed in Layer::traverseInZOrder
layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
- if (!layer->belongsToDisplay(display->getLayerStack(), false)) {
+ if (layer->getPrimaryDisplayOnly()) {
return;
}
if (!layer->isVisible()) {
return;
}
+ if (uid != CaptureArgs::UNSET_UID && layer->getOwnerUid() != uid) {
+ return;
+ }
visitor(layer);
});
}
}
-status_t SurfaceFlinger::setDesiredDisplayConfigSpecsInternal(
+status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal(
const sp<DisplayDevice>& display,
const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy) {
Mutex::Autolock lock(mStateLock);
@@ -5832,39 +6213,34 @@
"Can only set override policy on the primary display");
LOG_ALWAYS_FATAL_IF(!policy && !overridePolicy, "Can only clear the override policy");
+ if (mDebugDisplayModeSetByBackdoor) {
+ // ignore this request as mode is overridden by backdoor
+ return NO_ERROR;
+ }
+
if (!display->isPrimary()) {
- // TODO(b/144711714): For non-primary displays we should be able to set an active config
- // as well. For now, just call directly to setActiveConfigWithConstraints but ideally
- // it should go thru setDesiredActiveConfig, similar to primary display.
- ALOGV("setAllowedDisplayConfigsInternal for non-primary display");
- const auto displayId = display->getId();
- LOG_ALWAYS_FATAL_IF(!displayId);
+ // TODO(b/144711714): For non-primary displays we should be able to set an active mode
+ // as well. For now, just call directly to initiateModeChange but ideally
+ // it should go thru setDesiredActiveMode, similar to primary display.
+ ALOGV("%s for non-primary display", __func__);
+ const auto displayId = display->getPhysicalId();
hal::VsyncPeriodChangeConstraints constraints;
constraints.desiredTimeNanos = systemTime();
constraints.seamlessRequired = false;
hal::VsyncPeriodChangeTimeline timeline = {0, 0, 0};
- if (getHwComposer().setActiveConfigWithConstraints(*displayId,
- policy->defaultConfig.value(),
- constraints, &timeline) < 0) {
+ if (display->initiateModeChange(policy->defaultMode, constraints, &timeline) != NO_ERROR) {
return BAD_VALUE;
}
if (timeline.refreshRequired) {
repaintEverythingForHWC();
}
- display->setActiveConfig(policy->defaultConfig);
- const nsecs_t vsyncPeriod = getHwComposer()
- .getConfigs(*displayId)[policy->defaultConfig.value()]
- ->getVsyncPeriod();
- mScheduler->onNonPrimaryDisplayConfigChanged(mAppConnectionHandle, display->getId()->value,
- policy->defaultConfig, vsyncPeriod);
- return NO_ERROR;
- }
-
- if (mDebugDisplayConfigSetByBackdoor) {
- // ignore this request as config is overridden by backdoor
+ display->setActiveMode(policy->defaultMode);
+ const nsecs_t vsyncPeriod = display->getMode(policy->defaultMode)->getVsyncPeriod();
+ mScheduler->onNonPrimaryDisplayModeChanged(mAppConnectionHandle, displayId,
+ policy->defaultMode, vsyncPeriod);
return NO_ERROR;
}
@@ -5879,48 +6255,41 @@
}
scheduler::RefreshRateConfigs::Policy currentPolicy = mRefreshRateConfigs->getCurrentPolicy();
- ALOGV("Setting desired display config specs: defaultConfig: %d primaryRange: [%.0f %.0f]"
- " expandedRange: [%.0f %.0f]",
- currentPolicy.defaultConfig.value(), currentPolicy.primaryRange.min,
- currentPolicy.primaryRange.max, currentPolicy.appRequestRange.min,
- currentPolicy.appRequestRange.max);
+ ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str());
// TODO(b/140204874): Leave the event in until we do proper testing with all apps that might
// be depending in this callback.
- const nsecs_t vsyncPeriod =
- mRefreshRateConfigs->getRefreshRateFromConfigId(display->getActiveConfig())
- .getVsyncPeriod();
- mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, display->getId()->value,
- display->getActiveConfig(), vsyncPeriod);
+ const auto activeMode = display->getActiveMode();
+ const nsecs_t vsyncPeriod = activeMode->getVsyncPeriod();
+ const auto physicalId = display->getPhysicalId();
+ mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, physicalId, activeMode->getId(),
+ vsyncPeriod);
toggleKernelIdleTimer();
- auto configId = mScheduler->getPreferredConfigId();
- auto& preferredRefreshRate = configId
- ? mRefreshRateConfigs->getRefreshRateFromConfigId(*configId)
- // NOTE: Choose the default config ID, if Scheduler doesn't have one in mind.
- : mRefreshRateConfigs->getRefreshRateFromConfigId(currentPolicy.defaultConfig);
- ALOGV("trying to switch to Scheduler preferred config %d (%s)",
- preferredRefreshRate.getConfigId().value(), preferredRefreshRate.getName().c_str());
+ auto modeId = mScheduler->getPreferredModeId();
+ auto preferredRefreshRate = modeId
+ ? mRefreshRateConfigs->getRefreshRateFromModeId(*modeId)
+ // NOTE: Choose the default mode ID, if Scheduler doesn't have one in mind.
+ : mRefreshRateConfigs->getRefreshRateFromModeId(currentPolicy.defaultMode);
+ ALOGV("trying to switch to Scheduler preferred mode %d (%s)",
+ preferredRefreshRate.getModeId().value(), preferredRefreshRate.getName().c_str());
- if (isDisplayConfigAllowed(preferredRefreshRate.getConfigId())) {
- ALOGV("switching to Scheduler preferred config %d",
- preferredRefreshRate.getConfigId().value());
- setDesiredActiveConfig(
- {preferredRefreshRate.getConfigId(), Scheduler::ConfigEvent::Changed});
+ if (isDisplayModeAllowed(preferredRefreshRate.getModeId())) {
+ ALOGV("switching to Scheduler preferred display mode %d",
+ preferredRefreshRate.getModeId().value());
+ setDesiredActiveMode({preferredRefreshRate.getModeId(), Scheduler::ModeEvent::Changed});
} else {
- LOG_ALWAYS_FATAL("Desired config not allowed: %d",
- preferredRefreshRate.getConfigId().value());
+ LOG_ALWAYS_FATAL("Desired display mode not allowed: %d",
+ preferredRefreshRate.getModeId().value());
}
return NO_ERROR;
}
-status_t SurfaceFlinger::setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
- int32_t defaultConfig,
- float primaryRefreshRateMin,
- float primaryRefreshRateMax,
- float appRequestRefreshRateMin,
- float appRequestRefreshRateMax) {
+status_t SurfaceFlinger::setDesiredDisplayModeSpecs(
+ const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode, bool allowGroupSwitching,
+ float primaryRefreshRateMin, float primaryRefreshRateMax, float appRequestRefreshRateMin,
+ float appRequestRefreshRateMax) {
ATRACE_CALL();
if (!displayToken) {
@@ -5930,35 +6299,37 @@
auto future = schedule([=]() -> status_t {
const auto display = ON_MAIN_THREAD(getDisplayDeviceLocked(displayToken));
if (!display) {
- ALOGE("Attempt to set desired display configs for invalid display token %p",
+ ALOGE("Attempt to set desired display modes for invalid display token %p",
displayToken.get());
return NAME_NOT_FOUND;
} else if (display->isVirtual()) {
- ALOGW("Attempt to set desired display configs for virtual display");
+ ALOGW("Attempt to set desired display modes for virtual display");
return INVALID_OPERATION;
} else {
using Policy = scheduler::RefreshRateConfigs::Policy;
- const Policy policy{HwcConfigIndexType(defaultConfig),
- {primaryRefreshRateMin, primaryRefreshRateMax},
- {appRequestRefreshRateMin, appRequestRefreshRateMax}};
+ const Policy policy{DisplayModeId(defaultMode),
+ allowGroupSwitching,
+ {Fps(primaryRefreshRateMin), Fps(primaryRefreshRateMax)},
+ {Fps(appRequestRefreshRateMin), Fps(appRequestRefreshRateMax)}};
constexpr bool kOverridePolicy = false;
- return setDesiredDisplayConfigSpecsInternal(display, policy, kOverridePolicy);
+ return setDesiredDisplayModeSpecsInternal(display, policy, kOverridePolicy);
}
});
return future.get();
}
-status_t SurfaceFlinger::getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
- int32_t* outDefaultConfig,
- float* outPrimaryRefreshRateMin,
- float* outPrimaryRefreshRateMax,
- float* outAppRequestRefreshRateMin,
- float* outAppRequestRefreshRateMax) {
+status_t SurfaceFlinger::getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
+ ui::DisplayModeId* outDefaultMode,
+ bool* outAllowGroupSwitching,
+ float* outPrimaryRefreshRateMin,
+ float* outPrimaryRefreshRateMax,
+ float* outAppRequestRefreshRateMin,
+ float* outAppRequestRefreshRateMax) {
ATRACE_CALL();
- if (!displayToken || !outDefaultConfig || !outPrimaryRefreshRateMin ||
+ if (!displayToken || !outDefaultMode || !outPrimaryRefreshRateMin ||
!outPrimaryRefreshRateMax || !outAppRequestRefreshRateMin || !outAppRequestRefreshRateMax) {
return BAD_VALUE;
}
@@ -5972,38 +6343,34 @@
if (display->isPrimary()) {
scheduler::RefreshRateConfigs::Policy policy =
mRefreshRateConfigs->getDisplayManagerPolicy();
- *outDefaultConfig = policy.defaultConfig.value();
- *outPrimaryRefreshRateMin = policy.primaryRange.min;
- *outPrimaryRefreshRateMax = policy.primaryRange.max;
- *outAppRequestRefreshRateMin = policy.appRequestRange.min;
- *outAppRequestRefreshRateMax = policy.appRequestRange.max;
+ *outDefaultMode = policy.defaultMode.value();
+ *outAllowGroupSwitching = policy.allowGroupSwitching;
+ *outPrimaryRefreshRateMin = policy.primaryRange.min.getValue();
+ *outPrimaryRefreshRateMax = policy.primaryRange.max.getValue();
+ *outAppRequestRefreshRateMin = policy.appRequestRange.min.getValue();
+ *outAppRequestRefreshRateMax = policy.appRequestRange.max.getValue();
return NO_ERROR;
} else if (display->isVirtual()) {
return INVALID_OPERATION;
} else {
- const auto displayId = display->getId();
- LOG_FATAL_IF(!displayId);
-
- *outDefaultConfig = getHwComposer().getActiveConfigIndex(*displayId);
- auto vsyncPeriod = getHwComposer().getActiveConfig(*displayId)->getVsyncPeriod();
- *outPrimaryRefreshRateMin = 1e9f / vsyncPeriod;
- *outPrimaryRefreshRateMax = 1e9f / vsyncPeriod;
- *outAppRequestRefreshRateMin = 1e9f / vsyncPeriod;
- *outAppRequestRefreshRateMax = 1e9f / vsyncPeriod;
+ const auto activeMode = display->getActiveMode();
+ *outDefaultMode = activeMode->getId().value();
+ *outAllowGroupSwitching = false;
+ auto vsyncPeriod = activeMode->getVsyncPeriod();
+ *outPrimaryRefreshRateMin = Fps::fromPeriodNsecs(vsyncPeriod).getValue();
+ *outPrimaryRefreshRateMax = Fps::fromPeriodNsecs(vsyncPeriod).getValue();
+ *outAppRequestRefreshRateMin = Fps::fromPeriodNsecs(vsyncPeriod).getValue();
+ *outAppRequestRefreshRateMax = Fps::fromPeriodNsecs(vsyncPeriod).getValue();
return NO_ERROR;
}
}
-void SurfaceFlinger::SetInputWindowsListener::onSetInputWindowsFinished() {
- mFlinger->setInputWindowsFinished();
-}
-
wp<Layer> SurfaceFlinger::fromHandle(const sp<IBinder>& handle) {
Mutex::Autolock _l(mStateLock);
return fromHandleLocked(handle);
}
-wp<Layer> SurfaceFlinger::fromHandleLocked(const sp<IBinder>& handle) {
+wp<Layer> SurfaceFlinger::fromHandleLocked(const sp<IBinder>& handle) const {
BBinder* b = nullptr;
if (handle) {
b = handle->localBinder();
@@ -6024,6 +6391,7 @@
}
void SurfaceFlinger::onLayerDestroyed(Layer* layer) {
+ mScheduler->deregisterLayer(layer);
mNumLayers--;
removeFromOffscreenLayers(layer);
}
@@ -6042,10 +6410,6 @@
mOffscreenLayers.erase(layer);
}
-void SurfaceFlinger::bufferErased(const client_cache_t& clientCacheId) {
- getRenderEngine().unbindExternalTextureBuffer(clientCacheId.id);
-}
-
status_t SurfaceFlinger::setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
float lightPosY, float lightPosZ,
float lightRadius) {
@@ -6075,8 +6439,9 @@
}
status_t SurfaceFlinger::setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
- int8_t compatibility) {
- if (!ValidateFrameRate(frameRate, compatibility, "SurfaceFlinger::setFrameRate")) {
+ int8_t compatibility, int8_t changeFrameRateStrategy) {
+ if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy,
+ "SurfaceFlinger::setFrameRate")) {
return BAD_VALUE;
}
@@ -6088,10 +6453,12 @@
ALOGE("Attempt to set frame rate on a layer that no longer exists");
return BAD_VALUE;
}
-
+ const auto strategy =
+ Layer::FrameRate::convertChangeFrameRateStrategy(changeFrameRateStrategy);
if (layer->setFrameRate(
- Layer::FrameRate(frameRate,
- Layer::FrameRate::convertCompatibility(compatibility)))) {
+ Layer::FrameRate(Fps{frameRate},
+ Layer::FrameRate::convertCompatibility(compatibility),
+ strategy))) {
setTransactionFlags(eTraversalNeeded);
}
} else {
@@ -6117,16 +6484,15 @@
const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
// This is a little racy, but not in a way that hurts anything. As we grab the
- // defaultConfig from the display manager policy, we could be setting a new display
- // manager policy, leaving us using a stale defaultConfig. The defaultConfig doesn't
- // matter for the override policy though, since we set allowGroupSwitching to true, so
- // it's not a problem.
+ // defaultMode from the display manager policy, we could be setting a new display
+ // manager policy, leaving us using a stale defaultMode. The defaultMode doesn't
+ // matter for the override policy though, since we set allowGroupSwitching to
+ // true, so it's not a problem.
scheduler::RefreshRateConfigs::Policy overridePolicy;
- overridePolicy.defaultConfig =
- mRefreshRateConfigs->getDisplayManagerPolicy().defaultConfig;
+ overridePolicy.defaultMode = mRefreshRateConfigs->getDisplayManagerPolicy().defaultMode;
overridePolicy.allowGroupSwitching = true;
constexpr bool kOverridePolicy = true;
- result = setDesiredDisplayConfigSpecsInternal(display, overridePolicy, kOverridePolicy);
+ result = setDesiredDisplayModeSpecsInternal(display, overridePolicy, kOverridePolicy);
}
if (result == NO_ERROR) {
@@ -6166,17 +6532,35 @@
if (mFrameRateFlexibilityTokenCount == 0) {
const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
constexpr bool kOverridePolicy = true;
- status_t result = setDesiredDisplayConfigSpecsInternal(display, {}, kOverridePolicy);
+ status_t result = setDesiredDisplayModeSpecsInternal(display, {}, kOverridePolicy);
LOG_ALWAYS_FATAL_IF(result < 0, "Failed releasing frame rate flexibility token");
}
}));
}
+status_t SurfaceFlinger::setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface,
+ const FrameTimelineInfo& frameTimelineInfo) {
+ Mutex::Autolock lock(mStateLock);
+ if (!authenticateSurfaceTextureLocked(surface)) {
+ ALOGE("Attempt to set frame timeline info on an unrecognized IGraphicBufferProducer");
+ return BAD_VALUE;
+ }
+
+ sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer();
+ if (layer == nullptr) {
+ ALOGE("Attempt to set frame timeline info on a layer that no longer exists");
+ return BAD_VALUE;
+ }
+
+ layer->setFrameTimelineInfoForBuffer(frameTimelineInfo);
+ return NO_ERROR;
+}
+
void SurfaceFlinger::enableRefreshRateOverlay(bool enable) {
static_cast<void>(schedule([=] {
std::unique_ptr<RefreshRateOverlay> overlay;
if (enable) {
- overlay = std::make_unique<RefreshRateOverlay>(*this);
+ overlay = std::make_unique<RefreshRateOverlay>(*this, mRefreshRateOverlaySpinner);
}
{
@@ -6188,13 +6572,136 @@
if (const auto display = getDefaultDisplayDeviceLocked()) {
mRefreshRateOverlay->setViewport(display->getSize());
+ mRefreshRateOverlay->changeRefreshRate(display->getActiveMode()->getFps());
}
-
- mRefreshRateOverlay->changeRefreshRate(mRefreshRateConfigs->getCurrentRefreshRate());
}
}));
}
+status_t SurfaceFlinger::addTransactionTraceListener(
+ const sp<gui::ITransactionTraceListener>& listener) {
+ if (!listener) {
+ return BAD_VALUE;
+ }
+
+ mInterceptor->addTransactionTraceListener(listener);
+
+ return NO_ERROR;
+}
+
+int SurfaceFlinger::getGPUContextPriority() {
+ return getRenderEngine().getContextPriority();
+}
+
+int SurfaceFlinger::calculateExtraBufferCount(Fps maxSupportedRefreshRate,
+ std::chrono::nanoseconds presentLatency) {
+ auto pipelineDepth = presentLatency.count() / maxSupportedRefreshRate.getPeriodNsecs();
+ if (presentLatency.count() % maxSupportedRefreshRate.getPeriodNsecs()) {
+ pipelineDepth++;
+ }
+ return std::max(0ll, pipelineDepth - 2);
+}
+
+status_t SurfaceFlinger::getExtraBufferCount(int* extraBuffers) const {
+ const auto maxSupportedRefreshRate = mRefreshRateConfigs->getSupportedRefreshRateRange().max;
+ const auto vsyncConfig =
+ mVsyncConfiguration->getConfigsForRefreshRate(maxSupportedRefreshRate).late;
+ const auto presentLatency = vsyncConfig.appWorkDuration + vsyncConfig.sfWorkDuration;
+
+ *extraBuffers = calculateExtraBufferCount(maxSupportedRefreshRate, presentLatency);
+ return NO_ERROR;
+}
+
+void SurfaceFlinger::TransactionState::traverseStatesWithBuffers(
+ std::function<void(const layer_state_t&)> visitor) {
+ for (const auto& state : states) {
+ if (state.state.hasBufferChanges() && (state.state.surface)) {
+ visitor(state.state);
+ }
+ }
+}
+
+void SurfaceFlinger::setLayerCreatedState(const sp<IBinder>& handle, const wp<Layer>& layer,
+ const wp<IBinder>& parent, const wp<Layer> parentLayer,
+ const wp<IBinder>& producer) {
+ Mutex::Autolock lock(mCreatedLayersLock);
+ mCreatedLayers[handle->localBinder()] =
+ std::make_unique<LayerCreatedState>(layer, parent, parentLayer, producer);
+}
+
+auto SurfaceFlinger::getLayerCreatedState(const sp<IBinder>& handle) {
+ Mutex::Autolock lock(mCreatedLayersLock);
+ BBinder* b = nullptr;
+ if (handle) {
+ b = handle->localBinder();
+ }
+
+ if (b == nullptr) {
+ return std::unique_ptr<LayerCreatedState>(nullptr);
+ }
+
+ auto it = mCreatedLayers.find(b);
+ if (it == mCreatedLayers.end()) {
+ ALOGE("Can't find layer from handle %p", handle.get());
+ return std::unique_ptr<LayerCreatedState>(nullptr);
+ }
+
+ auto state = std::move(it->second);
+ mCreatedLayers.erase(it);
+ return state;
+}
+
+sp<Layer> SurfaceFlinger::handleLayerCreatedLocked(const sp<IBinder>& handle, bool privileged) {
+ const auto& state = getLayerCreatedState(handle);
+ if (!state) {
+ return nullptr;
+ }
+
+ sp<Layer> layer = state->layer.promote();
+ if (!layer) {
+ ALOGE("Invalid layer %p", state->layer.unsafe_get());
+ return nullptr;
+ }
+
+ sp<Layer> parent;
+ bool allowAddRoot = privileged;
+ if (state->initialParent != nullptr) {
+ parent = fromHandleLocked(state->initialParent.promote()).promote();
+ if (parent == nullptr) {
+ ALOGE("Invalid parent %p", state->initialParent.unsafe_get());
+ allowAddRoot = false;
+ }
+ } else if (state->initialParentLayer != nullptr) {
+ parent = state->initialParentLayer.promote();
+ allowAddRoot = false;
+ }
+
+ if (parent == nullptr && allowAddRoot) {
+ mCurrentState.layersSortedByZ.add(layer);
+ } else if (parent == nullptr) {
+ layer->onRemovedFromCurrentState();
+ } else if (parent->isRemovedFromCurrentState()) {
+ parent->addChild(layer);
+ layer->onRemovedFromCurrentState();
+ } else {
+ parent->addChild(layer);
+ }
+
+ if (state->initialProducer != nullptr) {
+ mGraphicBufferProducerList.insert(state->initialProducer);
+ LOG_ALWAYS_FATAL_IF(mGraphicBufferProducerList.size() > mMaxGraphicBufferProducerListSize,
+ "Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers",
+ mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize,
+ mNumLayers.load());
+ if (mGraphicBufferProducerList.size() > mGraphicBufferProducerListSizeLogThreshold) {
+ ALOGW("Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers",
+ mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize,
+ mNumLayers.load());
+ }
+ }
+
+ return layer;
+}
} // namespace android
#if defined(__gl_h_)
@@ -6206,4 +6713,4 @@
#endif
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 90ac856..cf1a545 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -33,7 +33,6 @@
#include <gui/ITransactionCompletedListener.h>
#include <gui/LayerState.h>
#include <gui/OccupancyTracker.h>
-#include <input/ISetInputWindowsListener.h>
#include <layerproto/LayerProtoHeader.h>
#include <math/mat4.h>
#include <renderengine/LayerSettings.h>
@@ -53,17 +52,19 @@
#include "DisplayDevice.h"
#include "DisplayHardware/HWC2.h"
#include "DisplayHardware/PowerAdvisor.h"
+#include "DisplayIdGenerator.h"
#include "Effects/Daltonizer.h"
+#include "Fps.h"
#include "FrameTracker.h"
#include "LayerVector.h"
#include "Scheduler/RefreshRateConfigs.h"
#include "Scheduler/RefreshRateStats.h"
#include "Scheduler/Scheduler.h"
-#include "Scheduler/VSyncModulator.h"
+#include "Scheduler/VsyncModulator.h"
#include "SurfaceFlingerFactory.h"
#include "SurfaceTracing.h"
#include "TracedOrdinal.h"
-#include "TransactionCompletedThread.h"
+#include "TransactionCallbackInvoker.h"
#include <atomic>
#include <cstdint>
@@ -88,16 +89,29 @@
class Client;
class EventThread;
+class FpsReporter;
+class HdrLayerInfoReporter;
class HWComposer;
+struct SetInputWindowsListener;
class IGraphicBufferProducer;
-class IInputFlinger;
class Layer;
class MessageBase;
class RefreshRateOverlay;
class RegionSamplingThread;
+class RenderArea;
class TimeStats;
class FrameTracer;
+using gui::ScreenCaptureResults;
+
+namespace frametimeline {
+class FrameTimeline;
+}
+
+namespace os {
+ class IInputFlinger;
+}
+
namespace compositionengine {
class DisplaySurface;
class OutputLayer;
@@ -120,13 +134,7 @@
using DisplayColorSetting = compositionengine::OutputColorSetting;
-class SurfaceFlingerBE
-{
-public:
- SurfaceFlingerBE();
-
- const std::string mHwcServiceName; // "default" for real use, something else for testing.
-
+struct SurfaceFlingerBE {
FenceTimeline mGlCompositionDoneTimeline;
FenceTimeline mDisplayTimeline;
@@ -170,13 +178,19 @@
class SurfaceFlinger : public BnSurfaceComposer,
public PriorityDumper,
- public ClientCache::ErasedRecipient,
private IBinder::DeathRecipient,
private HWC2::ComposerCallback,
private ISchedulerCallback {
public:
- SurfaceFlingerBE& getBE() { return mBE; }
- const SurfaceFlingerBE& getBE() const { return mBE; }
+ struct SkipInitializationTag {};
+
+ SurfaceFlinger(surfaceflinger::Factory&, SkipInitializationTag) ANDROID_API;
+ explicit SurfaceFlinger(surfaceflinger::Factory&) ANDROID_API;
+
+ // set main thread scheduling policy
+ static status_t setSchedFifo(bool enabled) ANDROID_API;
+
+ static char const* getServiceName() ANDROID_API { return "SurfaceFlinger"; }
// This is the phase offset in nanoseconds of the software vsync event
// relative to the vsync event reported by HWComposer. The software vsync
@@ -204,7 +218,7 @@
// If fences from sync Framework are supported.
static bool hasSyncFramework;
- // The offset in nanoseconds to use when DispSync timestamps present fence
+ // The offset in nanoseconds to use when VsyncController timestamps present fence
// signaling time.
static int64_t dispSyncPresentTimeOffset;
@@ -255,17 +269,7 @@
// overhead that is caused by reading from sysprop.
static bool useFrameRateApi;
- // set main thread scheduling policy
- static status_t setSchedFifo(bool enabled) ANDROID_API;
-
- static char const* getServiceName() ANDROID_API {
- return "SurfaceFlinger";
- }
-
- struct SkipInitializationTag {};
static constexpr SkipInitializationTag SkipInitialization;
- SurfaceFlinger(surfaceflinger::Factory&, SkipInitializationTag) ANDROID_API;
- explicit SurfaceFlinger(surfaceflinger::Factory&) ANDROID_API;
// must be called before clients can connect
void init() ANDROID_API;
@@ -273,6 +277,9 @@
// starts SurfaceFlinger main loop in the current thread
void run() ANDROID_API;
+ SurfaceFlingerBE& getBE() { return mBE; }
+ const SurfaceFlingerBE& getBE() const { return mBE; }
+
// Schedule an asynchronous or synchronous task on the main thread.
template <typename F, typename T = std::invoke_result_t<F>>
[[nodiscard]] std::future<T> schedule(F&&);
@@ -292,17 +299,10 @@
// utility function to delete a texture on the main thread
void deleteTextureAsync(uint32_t texture);
- // enable/disable h/w composer event
- // TODO: this should be made accessible only to EventThread
- void setPrimaryVsyncEnabled(bool enabled);
-
- // main thread function to enable/disable h/w composer event
- void setPrimaryVsyncEnabledInternal(bool enabled) REQUIRES(mStateLock);
-
// called on the main thread by MessageQueue when an internal message
// is received
// TODO: this should be made accessible only to MessageQueue
- void onMessageReceived(int32_t what, nsecs_t expectedVSyncTime);
+ void onMessageReceived(int32_t what, int64_t vsyncId, nsecs_t expectedVSyncTime);
renderengine::RenderEngine& getRenderEngine() const;
@@ -314,8 +314,8 @@
void removeFromOffscreenLayers(Layer* layer);
- TransactionCompletedThread& getTransactionCompletedThread() {
- return mTransactionCompletedThread;
+ TransactionCallbackInvoker& getTransactionCallbackInvoker() {
+ return mTransactionCallbackInvoker;
}
// Converts from a binder handle to a Layer
@@ -323,20 +323,38 @@
// Otherwise, returns a weak reference so that callers off the main-thread
// won't accidentally hold onto the last strong reference.
wp<Layer> fromHandle(const sp<IBinder>& handle);
- wp<Layer> fromHandleLocked(const sp<IBinder>& handle) REQUIRES(mStateLock);
-
- // Inherit from ClientCache::ErasedRecipient
- void bufferErased(const client_cache_t& clientCacheId) override;
+ wp<Layer> fromHandleLocked(const sp<IBinder>& handle) const REQUIRES(mStateLock);
// If set, disables reusing client composition buffers. This can be set by
// debug.sf.disable_client_composition_cache
bool mDisableClientCompositionCache = false;
+ void setInputWindowsFinished();
+
+protected:
+ // We're reference counted, never destroy SurfaceFlinger directly
+ virtual ~SurfaceFlinger();
+
+ virtual uint32_t setClientStateLocked(
+ const FrameTimelineInfo& info, const ComposerState& composerState,
+ int64_t desiredPresentTime, bool isAutoTimestamp, int64_t postTime,
+ uint32_t permissions,
+ std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks)
+ REQUIRES(mStateLock);
+ virtual void commitTransactionLocked();
+
+ // Used internally by computeLayerBounds() to gets the clip rectangle to use for the
+ // root layers on a particular display in layer-coordinate space. The
+ // layers (and effectively their children) will be clipped against this
+ // rectangle. The base behavior is to clip to the visible region of the
+ // display.
+ virtual FloatRect getLayerClipBoundsForDisplay(const DisplayDevice&) const;
private:
friend class BufferLayer;
friend class BufferQueueLayer;
friend class BufferStateLayer;
friend class Client;
+ friend class FpsReporter;
friend class Layer;
friend class MonitoredProducer;
friend class RefreshRateOverlay;
@@ -347,21 +365,18 @@
friend class TestableSurfaceFlinger;
friend class TransactionApplicationTest;
+ using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
+ using VsyncModulator = scheduler::VsyncModulator;
+ using TransactionSchedule = scheduler::TransactionSchedule;
+ using TraverseLayersFunction = std::function<void(const LayerVector::Visitor&)>;
+ using RenderAreaFuture = std::future<std::unique_ptr<RenderArea>>;
+ using DumpArgs = Vector<String16>;
+ using Dumper = std::function<void(const DumpArgs&, bool asProto, std::string&)>;
+
// This value is specified in number of frames. Log frame stats at most
// every half hour.
enum { LOG_FRAME_STATS_PERIOD = 30*60*60 };
- static const int MAX_TRACING_MEMORY = 100 * 1024 * 1024; // 100MB
-
-protected:
- // We're reference counted, never destroy SurfaceFlinger directly
- virtual ~SurfaceFlinger();
-
-private:
- /* ------------------------------------------------------------------------
- * Internal data structures
- */
-
class State {
public:
explicit State(LayerVector::StateSet set) : stateSet(set), layersSortedByZ(set) {}
@@ -393,72 +408,236 @@
void traverseInReverseZOrder(const LayerVector::Visitor& visitor) const;
};
- /* ------------------------------------------------------------------------
- * IBinder interface
- */
+ // Keeps track of pending buffers per layer handle in the transaction queue or current/drawing
+ // state before the buffers are latched. The layer owns the atomic counters and decrements the
+ // count in the main thread when dropping or latching a buffer.
+ //
+ // The binder threads increment the same counter when a new transaction containing a buffer is
+ // added to the transaction queue. The map is updated with the layer handle lifecycle updates.
+ // This is done to avoid lock contention with the main thread.
+ class BufferCountTracker {
+ public:
+ void increment(BBinder* layerHandle) {
+ std::lock_guard<std::mutex> lock(mLock);
+ auto it = mCounterByLayerHandle.find(layerHandle);
+ if (it != mCounterByLayerHandle.end()) {
+ auto [name, pendingBuffers] = it->second;
+ int32_t count = ++(*pendingBuffers);
+ ATRACE_INT(name.c_str(), count);
+ } else {
+ ALOGW("Handle not found! %p", layerHandle);
+ }
+ }
+
+ void add(BBinder* layerHandle, const std::string& name, std::atomic<int32_t>* counter) {
+ std::lock_guard<std::mutex> lock(mLock);
+ mCounterByLayerHandle[layerHandle] = std::make_pair(name, counter);
+ }
+
+ void remove(BBinder* layerHandle) {
+ std::lock_guard<std::mutex> lock(mLock);
+ mCounterByLayerHandle.erase(layerHandle);
+ }
+
+ private:
+ std::mutex mLock;
+ std::unordered_map<BBinder*, std::pair<std::string, std::atomic<int32_t>*>>
+ mCounterByLayerHandle GUARDED_BY(mLock);
+ };
+
+ struct ActiveModeInfo {
+ DisplayModeId modeId;
+ Scheduler::ModeEvent event = Scheduler::ModeEvent::None;
+
+ bool operator!=(const ActiveModeInfo& other) const {
+ return modeId != other.modeId || event != other.event;
+ }
+ };
+
+ enum class BootStage {
+ BOOTLOADER,
+ BOOTANIMATION,
+ FINISHED,
+ };
+
+ struct HotplugEvent {
+ hal::HWDisplayId hwcDisplayId;
+ hal::Connection connection = hal::Connection::INVALID;
+ };
+
+ class CountDownLatch {
+ public:
+ explicit CountDownLatch(int32_t count) : mCount(count) {}
+
+ int32_t countDown() {
+ std::unique_lock<std::mutex> lock(mMutex);
+ if (mCount == 0) {
+ return 0;
+ }
+ if (--mCount == 0) {
+ mCountDownComplete.notify_all();
+ }
+ return mCount;
+ }
+
+ // Return true if triggered.
+ bool wait_until(const std::chrono::seconds& timeout) const {
+ std::unique_lock<std::mutex> lock(mMutex);
+ const auto untilTime = std::chrono::system_clock::now() + timeout;
+ while (mCount != 0) {
+ // Conditional variables can be woken up sporadically, so we check count
+ // to verify the wakeup was triggered by |countDown|.
+ if (std::cv_status::timeout == mCountDownComplete.wait_until(lock, untilTime)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private:
+ int32_t mCount;
+ mutable std::condition_variable mCountDownComplete;
+ mutable std::mutex mMutex;
+ };
+
+ struct TransactionState {
+ TransactionState(const FrameTimelineInfo& frameTimelineInfo,
+ const Vector<ComposerState>& composerStates,
+ const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
+ const sp<IBinder>& applyToken,
+ const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
+ bool isAutoTimestamp, const client_cache_t& uncacheBuffer,
+ int64_t postTime, uint32_t permissions, bool hasListenerCallbacks,
+ std::vector<ListenerCallbacks> listenerCallbacks, int originPid,
+ int originUid, uint64_t transactionId)
+ : frameTimelineInfo(frameTimelineInfo),
+ states(composerStates),
+ displays(displayStates),
+ flags(transactionFlags),
+ applyToken(applyToken),
+ inputWindowCommands(inputWindowCommands),
+ desiredPresentTime(desiredPresentTime),
+ isAutoTimestamp(isAutoTimestamp),
+ buffer(uncacheBuffer),
+ postTime(postTime),
+ permissions(permissions),
+ hasListenerCallbacks(hasListenerCallbacks),
+ listenerCallbacks(listenerCallbacks),
+ originPid(originPid),
+ originUid(originUid),
+ id(transactionId) {}
+
+ void traverseStatesWithBuffers(std::function<void(const layer_state_t&)> visitor);
+
+ FrameTimelineInfo frameTimelineInfo;
+ Vector<ComposerState> states;
+ Vector<DisplayState> displays;
+ uint32_t flags;
+ sp<IBinder> applyToken;
+ InputWindowCommands inputWindowCommands;
+ const int64_t desiredPresentTime;
+ const bool isAutoTimestamp;
+ client_cache_t buffer;
+ const int64_t postTime;
+ uint32_t permissions;
+ bool hasListenerCallbacks;
+ std::vector<ListenerCallbacks> listenerCallbacks;
+ int originPid;
+ int originUid;
+ uint64_t id;
+ std::shared_ptr<CountDownLatch> transactionCommittedSignal;
+ };
+
+ template <typename F, std::enable_if_t<!std::is_member_function_pointer_v<F>>* = nullptr>
+ static Dumper dumper(F&& dump) {
+ using namespace std::placeholders;
+ return std::bind(std::forward<F>(dump), _3);
+ }
+
+ template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr>
+ Dumper dumper(F dump) {
+ using namespace std::placeholders;
+ return std::bind(dump, this, _3);
+ }
+
+ template <typename F>
+ Dumper argsDumper(F dump) {
+ using namespace std::placeholders;
+ return std::bind(dump, this, _1, _3);
+ }
+
+ template <typename F>
+ Dumper protoDumper(F dump) {
+ using namespace std::placeholders;
+ return std::bind(dump, this, _1, _2, _3);
+ }
+
+ template <typename... Args,
+ typename Handler = VsyncModulator::VsyncConfigOpt (VsyncModulator::*)(Args...)>
+ void modulateVsync(Handler handler, Args... args) {
+ if (const auto config = (*mVsyncModulator.*handler)(args...)) {
+ const auto vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
+ setVsyncConfig(*config, vsyncPeriod);
+ }
+ }
+
+ static const int MAX_TRACING_MEMORY = 100 * 1024 * 1024; // 100MB
+ // Maximum allowed number of display frames that can be set through backdoor
+ static const int MAX_ALLOWED_DISPLAY_FRAMES = 2048;
+
+ // Implements IBinder.
status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override;
status_t dump(int fd, const Vector<String16>& args) override { return priorityDump(fd, args); }
bool callingThreadHasUnscopedSurfaceFlingerAccess(bool usePermissionCache = true)
EXCLUDES(mStateLock);
- /* ------------------------------------------------------------------------
- * ISurfaceComposer interface
- */
+ // Implements ISurfaceComposer
sp<ISurfaceComposerClient> createConnection() override;
sp<IBinder> createDisplay(const String8& displayName, bool secure) override;
void destroyDisplay(const sp<IBinder>& displayToken) override;
std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const override;
sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const override;
- void setTransactionState(const Vector<ComposerState>& state,
- const Vector<DisplayState>& displays, uint32_t flags,
- const sp<IBinder>& applyToken,
- const InputWindowCommands& inputWindowCommands,
- int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
- bool hasListenerCallbacks,
- const std::vector<ListenerCallbacks>& listenerCallbacks) override;
+ status_t setTransactionState(const FrameTimelineInfo& frameTimelineInfo,
+ const Vector<ComposerState>& state,
+ const Vector<DisplayState>& displays, uint32_t flags,
+ const sp<IBinder>& applyToken,
+ const InputWindowCommands& inputWindowCommands,
+ int64_t desiredPresentTime, bool isAutoTimestamp,
+ const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+ const std::vector<ListenerCallbacks>& listenerCallbacks,
+ uint64_t transactionId) override;
void bootFinished() override;
bool authenticateSurfaceTexture(
const sp<IGraphicBufferProducer>& bufferProducer) const override;
status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) const override;
sp<IDisplayEventConnection> createDisplayEventConnection(
ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp,
- ISurfaceComposer::ConfigChanged configChanged =
- ISurfaceComposer::eConfigChangedSuppress) override;
- status_t captureScreen(const sp<IBinder>& displayToken, sp<GraphicBuffer>* outBuffer,
- bool& outCapturedSecureLayers, ui::Dataspace reqDataspace,
- ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
- uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
- ui::Rotation rotation, bool captureSecureLayers) override;
- status_t captureScreen(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace,
- sp<GraphicBuffer>* outBuffer) override;
- status_t captureLayers(
- const sp<IBinder>& parentHandle, sp<GraphicBuffer>* outBuffer,
- const ui::Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat,
- const Rect& sourceCrop,
- const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& exclude,
- float frameScale, bool childrenOnly) override;
+ ISurfaceComposer::EventRegistrationFlags eventRegistration = {}) override;
+ status_t captureDisplay(const DisplayCaptureArgs& args,
+ const sp<IScreenCaptureListener>& captureListener) override;
+ status_t captureDisplay(uint64_t displayOrLayerStack,
+ const sp<IScreenCaptureListener>& captureListener) override;
+ status_t captureLayers(const LayerCaptureArgs& args,
+ const sp<IScreenCaptureListener>& captureListener) override;
status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats) override;
- status_t getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState*) override;
- status_t getDisplayInfo(const sp<IBinder>& displayToken, DisplayInfo*) override;
- status_t getDisplayConfigs(const sp<IBinder>& displayToken, Vector<DisplayConfig>*) override;
- int getActiveConfig(const sp<IBinder>& displayToken) override;
- status_t getDisplayColorModes(const sp<IBinder>& displayToken, Vector<ui::ColorMode>*) override;
+ status_t getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState*)
+ EXCLUDES(mStateLock) override;
+ status_t getStaticDisplayInfo(const sp<IBinder>& displayToken, ui::StaticDisplayInfo*)
+ EXCLUDES(mStateLock) override;
+ status_t getDynamicDisplayInfo(const sp<IBinder>& displayToken, ui::DynamicDisplayInfo*)
+ EXCLUDES(mStateLock) override;
status_t getDisplayNativePrimaries(const sp<IBinder>& displayToken,
ui::DisplayPrimaries&) override;
- ui::ColorMode getActiveColorMode(const sp<IBinder>& displayToken) override;
status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode) override;
- status_t getAutoLowLatencyModeSupport(const sp<IBinder>& displayToken,
- bool* outSupported) const override;
void setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) override;
- status_t getGameContentTypeSupport(const sp<IBinder>& displayToken,
- bool* outSupported) const override;
void setGameContentType(const sp<IBinder>& displayToken, bool on) override;
void setPowerMode(const sp<IBinder>& displayToken, int mode) override;
status_t clearAnimationFrameStats() override;
status_t getAnimationFrameStats(FrameStats* outStats) const override;
- status_t getHdrCapabilities(const sp<IBinder>& displayToken,
- HdrCapabilities* outCapabilities) const override;
+ status_t overrideHdrTypes(const sp<IBinder>& displayToken,
+ const std::vector<ui::Hdr>& hdrTypes) override;
+ status_t onPullAtom(const int32_t atomId, std::string* pulledData, bool* success) override;
status_t enableVSyncInjections(bool enable) override;
status_t injectVSync(nsecs_t when) override;
status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) override;
@@ -481,36 +660,52 @@
status_t addRegionSamplingListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle,
const sp<IRegionSamplingListener>& listener) override;
status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) override;
- status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, int32_t displayModeId,
- float primaryRefreshRateMin, float primaryRefreshRateMax,
- float appRequestRefreshRateMin,
- float appRequestRefreshRateMax) override;
- status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
- int32_t* outDefaultConfig,
- float* outPrimaryRefreshRateMin,
- float* outPrimaryRefreshRateMax,
- float* outAppRequestRefreshRateMin,
- float* outAppRequestRefreshRateMax) override;
+ status_t addFpsListener(int32_t taskId, const sp<gui::IFpsListener>& listener) override;
+ status_t removeFpsListener(const sp<gui::IFpsListener>& listener) override;
+ status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
+ ui::DisplayModeId displayModeId, bool allowGroupSwitching,
+ float primaryRefreshRateMin, float primaryRefreshRateMax,
+ float appRequestRefreshRateMin,
+ float appRequestRefreshRateMax) override;
+ status_t getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
+ ui::DisplayModeId* outDefaultMode,
+ bool* outAllowGroupSwitching,
+ float* outPrimaryRefreshRateMin,
+ float* outPrimaryRefreshRateMax,
+ float* outAppRequestRefreshRateMin,
+ float* outAppRequestRefreshRateMax) override;
status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
bool* outSupport) const override;
- status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) override;
- status_t notifyPowerHint(int32_t hintId) override;
+ status_t setDisplayBrightness(const sp<IBinder>& displayToken,
+ const gui::DisplayBrightness& brightness) override;
+ status_t addHdrLayerInfoListener(const sp<IBinder>& displayToken,
+ const sp<gui::IHdrLayerInfoListener>& listener) override;
+ status_t removeHdrLayerInfoListener(const sp<IBinder>& displayToken,
+ const sp<gui::IHdrLayerInfoListener>& listener) override;
+ status_t notifyPowerBoost(int32_t boostId) override;
status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
float lightPosY, float lightPosZ, float lightRadius) override;
status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
- int8_t compatibility) override;
+ int8_t compatibility, int8_t changeFrameRateStrategy) override;
status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) override;
- /* ------------------------------------------------------------------------
- * DeathRecipient interface
- */
+
+ status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface,
+ const FrameTimelineInfo& frameTimelineInfo) override;
+
+ status_t addTransactionTraceListener(
+ const sp<gui::ITransactionTraceListener>& listener) override;
+
+ int getGPUContextPriority() override;
+
+ status_t getExtraBufferCount(int* extraBuffers) const override;
+
+ // Implements IBinder::DeathRecipient.
void binderDied(const wp<IBinder>& who) override;
- /* ------------------------------------------------------------------------
- * RefBase interface
- */
+ // Implements RefBase.
void onFirstRef() override;
- /* ------------------------------------------------------------------------
+ /*
* HWC2::ComposerCallback / HWComposer::EventHandler interface
*/
void onVsyncReceived(int32_t sequenceId, hal::HWDisplayId hwcDisplayId, int64_t timestamp,
@@ -523,14 +718,20 @@
const hal::VsyncPeriodChangeTimeline& updatedTimeline) override;
void onSeamlessPossible(int32_t sequenceId, hal::HWDisplayId display) override;
- /* ------------------------------------------------------------------------
+ /*
* ISchedulerCallback
*/
- void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ConfigEvent) override;
- // force full composition on all displays without resetting the scheduler idle timer.
+
+ // Toggles hardware VSYNC by calling into HWC.
+ void setVsyncEnabled(bool) override;
+ // Initiates a refresh rate change to be applied on invalidate.
+ void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ModeEvent) override;
+ // Forces full composition on all displays without resetting the scheduler idle timer.
void repaintEverythingForHWC() override;
// Called when kernel idle timer has expired. Used to update the refresh rate overlay.
void kernelTimerChanged(bool expired) override;
+ // Called when the frame rate override list changed to trigger an event.
+ void triggerOnFrameRateOverridesChanged() override;
// Toggles the kernel idle timer on or off depending the policy decisions around refresh rates.
void toggleKernelIdleTimer();
// Keeps track of whether the kernel idle timer is currently enabled, so we don't have to
@@ -538,7 +739,10 @@
bool mKernelIdleTimerEnabled = false;
// Keeps track of whether the kernel timer is supported on the SF side.
bool mSupportKernelIdleTimer = false;
- /* ------------------------------------------------------------------------
+ // Show spinner with refresh rate overlay
+ bool mRefreshRateOverlaySpinner = false;
+
+ /*
* Message handling
*/
// Can only be called from the main thread or with mStateLock held
@@ -547,43 +751,33 @@
void signalLayerUpdate();
void signalRefresh();
- using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
-
- struct ActiveConfigInfo {
- HwcConfigIndexType configId;
- Scheduler::ConfigEvent event = Scheduler::ConfigEvent::None;
-
- bool operator!=(const ActiveConfigInfo& other) const {
- return configId != other.configId || event != other.event;
- }
- };
-
- // called on the main thread in response to initializeDisplays()
+ // Called on the main thread in response to initializeDisplays()
void onInitializeDisplays() REQUIRES(mStateLock);
- // Sets the desired active config bit. It obtains the lock, and sets mDesiredActiveConfig.
- void setDesiredActiveConfig(const ActiveConfigInfo& info) REQUIRES(mStateLock);
- status_t setActiveConfig(const sp<IBinder>& displayToken, int id);
- // Once HWC has returned the present fence, this sets the active config and a new refresh
+ // Sets the desired active mode bit. It obtains the lock, and sets mDesiredActiveMode.
+ void setDesiredActiveMode(const ActiveModeInfo& info) REQUIRES(mStateLock);
+ status_t setActiveMode(const sp<IBinder>& displayToken, int id);
+ // Once HWC has returned the present fence, this sets the active mode and a new refresh
// rate in SF.
- void setActiveConfigInternal() REQUIRES(mStateLock);
- // Calls to setActiveConfig on the main thread if there is a pending config
+ void setActiveModeInternal() REQUIRES(mStateLock);
+ // Calls to setActiveMode on the main thread if there is a pending mode change
// that needs to be applied.
- void performSetActiveConfig() REQUIRES(mStateLock);
- // Called when active config is no longer is progress
- void desiredActiveConfigChangeDone() REQUIRES(mStateLock);
- // called on the main thread in response to setPowerMode()
+ void performSetActiveMode() REQUIRES(mStateLock);
+ void clearDesiredActiveModeState() REQUIRES(mStateLock) EXCLUDES(mActiveModeLock);
+ // Called when active mode is no longer is progress
+ void desiredActiveModeChangeDone() REQUIRES(mStateLock);
+ // Called on the main thread in response to setPowerMode()
void setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode)
REQUIRES(mStateLock);
- // Sets the desired display configs.
- status_t setDesiredDisplayConfigSpecsInternal(
+ // Sets the desired display mode specs.
+ status_t setDesiredDisplayModeSpecsInternal(
const sp<DisplayDevice>& display,
const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy)
EXCLUDES(mStateLock);
// Handle the INVALIDATE message queue event, latching new buffers and applying
// incoming transactions
- void onMessageInvalidate(nsecs_t expectedVSyncTime);
+ void onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTime);
// Returns whether the transaction actually modified any state
bool handleMessageTransaction();
@@ -601,9 +795,11 @@
void updateInputFlinger();
void updateInputWindowInfo();
void commitInputWindowCommands() REQUIRES(mStateLock);
- void setInputWindowsFinished();
void updateCursorAsync();
- void initScheduler(DisplayId primaryDisplayId);
+
+ void initScheduler(const DisplayDeviceState&) REQUIRES(mStateLock);
+ void updatePhaseConfiguration(const Fps&) REQUIRES(mStateLock);
+ void setVsyncConfig(const VsyncModulator::VsyncConfig&, nsecs_t vsyncPeriod);
/* handlePageFlip - latch a new buffer if available and compute the dirty
* region. Returns whether a new buffer has been latched, i.e., whether it
@@ -611,19 +807,20 @@
*/
bool handlePageFlip();
- /* ------------------------------------------------------------------------
+ /*
* Transactions
*/
- void applyTransactionState(const Vector<ComposerState>& state,
+ void applyTransactionState(const FrameTimelineInfo& info, const Vector<ComposerState>& state,
const Vector<DisplayState>& displays, uint32_t flags,
const InputWindowCommands& inputWindowCommands,
- const int64_t desiredPresentTime,
+ const int64_t desiredPresentTime, bool isAutoTimestamp,
const client_cache_t& uncacheBuffer, const int64_t postTime,
- bool privileged, bool hasListenerCallbacks,
+ uint32_t permissions, bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks,
- bool isMainThread = false) REQUIRES(mStateLock);
- // Returns true if at least one transaction was flushed
- bool flushTransactionQueues();
+ int originPid, int originUid, uint64_t transactionId)
+ REQUIRES(mStateLock);
+ // flush pending transaction that was presented after desiredPresentTime.
+ void flushTransactionQueues();
// Returns true if there is at least one transaction that needs to be flushed
bool transactionFlushNeeded();
uint32_t getTransactionFlags(uint32_t flags);
@@ -636,38 +833,26 @@
// but there is no need to try and wake up immediately to do it. Rather we rely on
// onFrameAvailable or another layer update to wake us up.
void setTraversalNeeded();
- uint32_t setTransactionFlags(uint32_t flags, Scheduler::TransactionStart transactionStart);
+ uint32_t setTransactionFlags(uint32_t flags, TransactionSchedule);
void commitTransaction() REQUIRES(mStateLock);
void commitOffscreenLayers();
- bool transactionIsReadyToBeApplied(int64_t desiredPresentTime,
- const Vector<ComposerState>& states);
+ bool transactionIsReadyToBeApplied(
+ const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
+ uid_t originUid, const Vector<ComposerState>& states,
+ const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>&
+ bufferLayersReadyToPresent) const REQUIRES(mStateLock);
uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
REQUIRES(mStateLock);
-
-protected:
- virtual uint32_t setClientStateLocked(
- const ComposerState& composerState, int64_t desiredPresentTime, int64_t postTime,
- bool privileged,
- std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks)
- REQUIRES(mStateLock);
- virtual void commitTransactionLocked();
-
- // Used internally by computeLayerBounds() to gets the clip rectangle to use for the
- // root layers on a particular display in layer-coordinate space. The
- // layers (and effectively their children) will be clipped against this
- // rectangle. The base behavior is to clip to the visible region of the
- // display.
- virtual FloatRect getLayerClipBoundsForDisplay(const DisplayDevice&) const;
-
-private:
- /* ------------------------------------------------------------------------
+ bool frameIsEarly(nsecs_t expectedPresentTime, int64_t vsyncId) const;
+ /*
* Layer management
*/
status_t createLayer(const String8& name, const sp<Client>& client, uint32_t w, uint32_t h,
PixelFormat format, uint32_t flags, LayerMetadata metadata,
sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp,
- const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer = nullptr,
+ const sp<IBinder>& parentHandle, int32_t* outLayerId,
+ const sp<Layer>& parentLayer = nullptr,
uint32_t* outTransformHint = nullptr);
status_t createBufferQueueLayer(const sp<Client>& client, std::string name, uint32_t w,
@@ -688,7 +873,7 @@
sp<IBinder>* outHandle, sp<Layer>* outLayer);
status_t mirrorLayer(const sp<Client>& client, const sp<IBinder>& mirrorFromHandle,
- sp<IBinder>* outHandle);
+ sp<IBinder>* outHandle, int32_t* outLayerId);
std::string getUniqueLayerName(const char* name);
@@ -707,47 +892,35 @@
// Traverse through all the layers and compute and cache its bounds.
void computeLayerBounds();
- /* ------------------------------------------------------------------------
- * Boot animation, on/off animations and screen capture
- */
-
+ // Boot animation, on/off animations and screen capture
void startBootAnim();
- using TraverseLayersFunction = std::function<void(const LayerVector::Visitor&)>;
+ status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, ui::Size bufferSize,
+ ui::PixelFormat, bool allowProtected, bool grayscale,
+ const sp<IScreenCaptureListener>&);
+ status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction,
+ const std::shared_ptr<renderengine::ExternalTexture>&,
+ bool regionSampling, bool grayscale,
+ const sp<IScreenCaptureListener>&);
+ status_t renderScreenImplLocked(const RenderArea&, TraverseLayersFunction,
+ const std::shared_ptr<renderengine::ExternalTexture>&,
+ bool forSystem, bool regionSampling, bool grayscale,
+ ScreenCaptureResults&);
- void renderScreenImplLocked(const RenderArea& renderArea, TraverseLayersFunction traverseLayers,
- ANativeWindowBuffer* buffer, bool useIdentityTransform,
- bool regionSampling, int* outSyncFd);
- status_t captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers,
- sp<GraphicBuffer>* outBuffer, const ui::PixelFormat reqPixelFormat,
- bool useIdentityTransform, bool& outCapturedSecureLayers);
- status_t captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers,
- const sp<GraphicBuffer>& buffer, bool useIdentityTransform,
- bool regionSampling, bool& outCapturedSecureLayers);
sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) REQUIRES(mStateLock);
+ sp<DisplayDevice> getDisplayById(DisplayId displayId) const REQUIRES(mStateLock);
sp<DisplayDevice> getDisplayByLayerStack(uint64_t layerStack) REQUIRES(mStateLock);
- status_t captureScreenImplLocked(const RenderArea& renderArea,
- TraverseLayersFunction traverseLayers,
- ANativeWindowBuffer* buffer, bool useIdentityTransform,
- bool forSystem, int* outSyncFd, bool regionSampling,
- bool& outCapturedSecureLayers);
- void traverseLayersInDisplay(const sp<const DisplayDevice>& display,
- const LayerVector::Visitor& visitor);
- sp<StartPropertySetThread> mStartPropertySetThread;
+ // If the uid provided is not UNSET_UID, the traverse will skip any layers that don't have a
+ // matching ownerUid
+ void traverseLayersInLayerStack(ui::LayerStack, const int32_t uid, const LayerVector::Visitor&);
- /* ------------------------------------------------------------------------
- * Properties
- */
void readPersistentProperties();
- /* ------------------------------------------------------------------------
- * EGL
- */
size_t getMaxTextureSize() const;
size_t getMaxViewportDims() const;
- /* ------------------------------------------------------------------------
+ /*
* Display and layer stack management
*/
// called when starting, or restarting after system_server death
@@ -779,38 +952,22 @@
return getDefaultDisplayDeviceLocked();
}
- std::optional<DeviceProductInfo> getDeviceProductInfoLocked(const DisplayDevice&) const;
-
// mark a region of a layer stack dirty. this updates the dirty
// region of all screens presenting this layer stack.
void invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty);
- /* ------------------------------------------------------------------------
+ /*
* H/W composer
*/
-
- // The current hardware composer interface.
- //
- // The following thread safety rules apply when accessing mHwc, either
- // directly or via getHwComposer():
- //
- // 1. When recreating mHwc, acquire mStateLock. Recreating mHwc must only be
- // done on the main thread.
- //
- // 2. When accessing mHwc on the main thread, it's not necessary to acquire
- // mStateLock.
- //
- // 3. When accessing mHwc on a thread other than the main thread, we always
+ // The following thread safety rules apply when accessing HWComposer:
+ // 1. When reading display state from HWComposer on the main thread, it's not necessary to
+ // acquire mStateLock.
+ // 2. When accessing HWComposer on a thread other than the main thread, we always
// need to acquire mStateLock. This is because the main thread could be
- // in the process of destroying the current mHwc instance.
- //
- // The above thread safety rules only apply to SurfaceFlinger.cpp. In
- // SurfaceFlinger_hwc1.cpp we create mHwc at surface flinger init and never
- // destroy it, so it's always safe to access mHwc from any thread without
- // acquiring mStateLock.
+ // in the process of writing display state, e.g. creating or destroying a display.
HWComposer& getHwComposer() const;
- /* ------------------------------------------------------------------------
+ /*
* Compositing
*/
void invalidateHwcGeometry();
@@ -824,9 +981,11 @@
void postFrame();
- /* ------------------------------------------------------------------------
+ /*
* Display management
*/
+ void loadDisplayModes(PhysicalDisplayId displayId, DisplayModes& outModes,
+ DisplayModePtr& outActiveMode) const REQUIRES(mStateLock);
sp<DisplayDevice> setupNewDisplayDeviceInternal(
const wp<IBinder>& displayToken,
std::shared_ptr<compositionengine::Display> compositionDisplay,
@@ -844,21 +1003,25 @@
void dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected);
- /* ------------------------------------------------------------------------
- * VSync
+ /*
+ * VSYNC
*/
nsecs_t getVsyncPeriodFromHWC() const REQUIRES(mStateLock);
// Sets the refresh rate by switching active configs, if they are available for
// the desired refresh rate.
- void changeRefreshRateLocked(const RefreshRate&, Scheduler::ConfigEvent event)
- REQUIRES(mStateLock);
+ void changeRefreshRateLocked(const RefreshRate&, Scheduler::ModeEvent) REQUIRES(mStateLock);
- bool isDisplayConfigAllowed(HwcConfigIndexType configId) const REQUIRES(mStateLock);
+ bool isDisplayModeAllowed(DisplayModeId) const REQUIRES(mStateLock);
+
+ struct FenceWithFenceTime {
+ sp<Fence> fence = Fence::NO_FENCE;
+ std::shared_ptr<FenceTime> fenceTime = FenceTime::NO_FENCE;
+ };
// Gets the fence for the previous frame.
// Must be called on the main thread.
- sp<Fence> previousFrameFence();
+ FenceWithFenceTime previousFrameFence();
// Whether the previous frame has not yet been presented to the display.
// If graceTimeMs is positive, this method waits for at most the provided
@@ -874,18 +1037,19 @@
// Calculates the expected present time for this frame. For negative offsets, performs a
// correction using the predicted vsync for the next frame instead.
- nsecs_t calculateExpectedPresentTime(nsecs_t now) const;
+ nsecs_t calculateExpectedPresentTime(DisplayStatInfo) const;
/*
* Display identification
*/
- sp<IBinder> getPhysicalDisplayTokenLocked(DisplayId displayId) const REQUIRES(mStateLock) {
+ sp<IBinder> getPhysicalDisplayTokenLocked(PhysicalDisplayId displayId) const
+ REQUIRES(mStateLock) {
const auto it = mPhysicalDisplayTokens.find(displayId);
return it != mPhysicalDisplayTokens.end() ? it->second : nullptr;
}
- std::optional<DisplayId> getPhysicalDisplayIdLocked(const sp<IBinder>& displayToken) const
- REQUIRES(mStateLock) {
+ std::optional<PhysicalDisplayId> getPhysicalDisplayIdLocked(
+ const sp<IBinder>& displayToken) const REQUIRES(mStateLock) {
for (const auto& [id, token] : mPhysicalDisplayTokens) {
if (token == displayToken) {
return id;
@@ -900,7 +1064,7 @@
return displayId ? getPhysicalDisplayTokenLocked(*displayId) : nullptr;
}
- std::optional<DisplayId> getInternalDisplayIdLocked() const REQUIRES(mStateLock) {
+ std::optional<PhysicalDisplayId> getInternalDisplayIdLocked() const REQUIRES(mStateLock) {
const auto hwcDisplayId = getHwComposer().getInternalHwcDisplayId();
return hwcDisplayId ? getHwComposer().toPhysicalDisplayId(*hwcDisplayId) : std::nullopt;
}
@@ -908,33 +1072,6 @@
/*
* Debugging & dumpsys
*/
- using DumpArgs = Vector<String16>;
- using Dumper = std::function<void(const DumpArgs&, bool asProto, std::string&)>;
-
- template <typename F, std::enable_if_t<!std::is_member_function_pointer_v<F>>* = nullptr>
- static Dumper dumper(F&& dump) {
- using namespace std::placeholders;
- return std::bind(std::forward<F>(dump), _3);
- }
-
- template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr>
- Dumper dumper(F dump) {
- using namespace std::placeholders;
- return std::bind(dump, this, _3);
- }
-
- template <typename F>
- Dumper argsDumper(F dump) {
- using namespace std::placeholders;
- return std::bind(dump, this, _1, _3);
- }
-
- template <typename F>
- Dumper protoDumper(F dump) {
- using namespace std::placeholders;
- return std::bind(dump, this, _1, _2, _3);
- }
-
void dumpAllLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
void appendSfConfigString(std::string& result) const;
@@ -942,6 +1079,7 @@
void dumpStatsLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
void clearStatsLocked(const DumpArgs& args, std::string& result);
void dumpTimeStats(const DumpArgs& args, bool asProto, std::string& result) const;
+ void dumpFrameTimeline(const DumpArgs& args, std::string& result) const;
void logFrameStats();
void dumpVSync(std::string& result) const REQUIRES(mStateLock);
@@ -963,10 +1101,7 @@
LayersProto dumpProtoFromMainThread(uint32_t traceFlags = SurfaceTracing::TRACE_ALL)
EXCLUDES(mStateLock);
void dumpOffscreenLayers(std::string& result) EXCLUDES(mStateLock);
-
- bool isLayerTripleBufferingDisabled() const {
- return this->mLayerTripleBufferingDisabled;
- }
+ void dumpPlannerInfo(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
status_t doDump(int fd, const DumpArgs& args, bool asProto);
@@ -978,20 +1113,48 @@
void onFrameRateFlexibilityTokenReleased();
+ static mat4 calculateColorMatrix(float saturation);
+
void updateColorMatrixLocked();
- /* ------------------------------------------------------------------------
- * Attributes
+ // Verify that transaction is being called by an approved process:
+ // either AID_GRAPHICS or AID_SYSTEM.
+ status_t CheckTransactCodeCredentials(uint32_t code);
+
+ // Add transaction to the Transaction Queue
+ void queueTransaction(TransactionState& state) EXCLUDES(mQueueLock);
+ void waitForSynchronousTransaction(const CountDownLatch& transactionCommittedSignal);
+ void signalSynchronousTransactions();
+
+ /*
+ * Generic Layer Metadata
+ */
+ const std::unordered_map<std::string, uint32_t>& getGenericLayerMetadataKeyMap() const;
+
+ /*
+ * Misc
*/
+ std::optional<ActiveModeInfo> getDesiredActiveMode() EXCLUDES(mActiveModeLock) {
+ std::lock_guard<std::mutex> lock(mActiveModeLock);
+ if (mDesiredActiveModeChanged) return mDesiredActiveMode;
+ return std::nullopt;
+ }
+
+ std::vector<ui::ColorMode> getDisplayColorModes(PhysicalDisplayId displayId)
+ REQUIRES(mStateLock);
+
+ static int calculateExtraBufferCount(Fps maxSupportedRefreshRate,
+ std::chrono::nanoseconds presentLatency);
+
+ sp<StartPropertySetThread> mStartPropertySetThread;
surfaceflinger::Factory& mFactory;
// access must be protected by mStateLock
mutable Mutex mStateLock;
State mCurrentState{LayerVector::StateSet::Current};
std::atomic<int32_t> mTransactionFlags = 0;
- Condition mTransactionCV;
- bool mTransactionPending = false;
+ std::vector<std::shared_ptr<CountDownLatch>> mTransactionCommittedSignals;
bool mAnimTransactionPending = false;
SortedVector<sp<Layer>> mLayersPendingRemoval;
bool mForceTraversal = false;
@@ -1004,6 +1167,10 @@
// Can't be unordered_set because wp<> isn't hashable
std::set<wp<IBinder>> mGraphicBufferProducerList;
size_t mMaxGraphicBufferProducerListSize = ISurfaceComposer::MAX_LAYERS;
+ // If there are more GraphicBufferProducers tracked by SurfaceFlinger than
+ // this threshold, then begin logging.
+ size_t mGraphicBufferProducerListSizeLogThreshold =
+ static_cast<size_t>(0.95 * static_cast<double>(MAX_LAYERS));
void removeGraphicBufferProducerAsync(const wp<IBinder>&);
@@ -1026,10 +1193,13 @@
bool mInputInfoChanged = false;
bool mGeometryInvalid = false;
bool mAnimCompositionPending = false;
- std::vector<sp<Layer>> mLayersWithQueuedFrames;
+
+ // Tracks layers that have pending frames which are candidates for being
+ // latched.
+ std::unordered_set<sp<Layer>, ISurfaceComposer::SpHash<Layer>> mLayersWithQueuedFrames;
// Tracks layers that need to update a display's dirty region.
std::vector<sp<Layer>> mLayersPendingRefresh;
- std::array<sp<Fence>, 2> mPreviousPresentFences = {Fence::NO_FENCE, Fence::NO_FENCE};
+ std::array<FenceWithFenceTime, 2> mPreviousPresentFences;
// True if in the previous frame at least one layer was composed via the GPU.
bool mHadClientComposition = false;
// True if in the previous frame at least one layer was composed via HW Composer.
@@ -1041,23 +1211,17 @@
// did not change.
bool mReusedClientComposition = false;
- enum class BootStage {
- BOOTLOADER,
- BOOTANIMATION,
- FINISHED,
- };
BootStage mBootStage = BootStage::BOOTLOADER;
- struct HotplugEvent {
- hal::HWDisplayId hwcDisplayId;
- hal::Connection connection = hal::Connection::INVALID;
- };
std::vector<HotplugEvent> mPendingHotplugEvents GUARDED_BY(mStateLock);
// this may only be written from the main thread with mStateLock held
// it may be read from other threads with mStateLock held
std::map<wp<IBinder>, sp<DisplayDevice>> mDisplays GUARDED_BY(mStateLock);
- std::unordered_map<DisplayId, sp<IBinder>> mPhysicalDisplayTokens GUARDED_BY(mStateLock);
+ std::unordered_map<PhysicalDisplayId, sp<IBinder>> mPhysicalDisplayTokens
+ GUARDED_BY(mStateLock);
+
+ RandomDisplayIdGenerator<GpuVirtualDisplayId> mGpuVirtualDisplayIdGenerator;
std::unordered_map<BBinder*, wp<Layer>> mLayersByLocalBinderToken GUARDED_BY(mStateLock);
@@ -1067,18 +1231,18 @@
bool mDebugDisableTransformHint = false;
volatile nsecs_t mDebugInTransaction = 0;
bool mForceFullDamage = false;
- bool mPropagateBackpressure = true;
bool mPropagateBackpressureClientComposition = false;
- std::unique_ptr<SurfaceInterceptor> mInterceptor;
+ sp<SurfaceInterceptor> mInterceptor;
SurfaceTracing mTracing{*this};
std::mutex mTracingLock;
bool mTracingEnabled = false;
- bool mAddCompositionStateToTrace = false;
+ bool mTracePostComposition = false;
std::atomic<bool> mTracingEnabledChanged = false;
const std::shared_ptr<TimeStats> mTimeStats;
const std::unique_ptr<FrameTracer> mFrameTracer;
+ const std::unique_ptr<frametimeline::FrameTimeline> mFrameTimeline;
bool mUseHwcVirtualDisplays = false;
// If blurs should be enabled on this device.
bool mSupportsBlur = false;
@@ -1090,10 +1254,7 @@
std::atomic<uint32_t> mHwcFrameMissedCount = 0;
std::atomic<uint32_t> mGpuFrameMissedCount = 0;
- TransactionCompletedThread mTransactionCompletedThread;
-
- // Restrict layers to use two buffers in their bufferqueues.
- bool mLayerTripleBufferingDisabled = false;
+ TransactionCallbackInvoker mTransactionCallbackInvoker;
// these are thread safe
std::unique_ptr<MessageQueue> mEventQueue;
@@ -1114,35 +1275,12 @@
uint32_t mTexturePoolSize = 0;
std::vector<uint32_t> mTexturePool;
- struct TransactionState {
- TransactionState(const Vector<ComposerState>& composerStates,
- const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
- int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
- int64_t postTime, bool privileged, bool hasListenerCallbacks,
- std::vector<ListenerCallbacks> listenerCallbacks)
- : states(composerStates),
- displays(displayStates),
- flags(transactionFlags),
- desiredPresentTime(desiredPresentTime),
- buffer(uncacheBuffer),
- postTime(postTime),
- privileged(privileged),
- hasListenerCallbacks(hasListenerCallbacks),
- listenerCallbacks(listenerCallbacks) {}
-
- Vector<ComposerState> states;
- Vector<DisplayState> displays;
- uint32_t flags;
- const int64_t desiredPresentTime;
- client_cache_t buffer;
- const int64_t postTime;
- bool privileged;
- bool hasListenerCallbacks;
- std::vector<ListenerCallbacks> listenerCallbacks;
- };
- std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash> mTransactionQueues;
-
- /* ------------------------------------------------------------------------
+ mutable Mutex mQueueLock;
+ Condition mTransactionQueueCV;
+ std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash>
+ mPendingTransactionQueues GUARDED_BY(mQueueLock);
+ std::queue<TransactionState> mTransactionQueue GUARDED_BY(mQueueLock);
+ /*
* Feature prototyping
*/
@@ -1151,10 +1289,6 @@
std::atomic<size_t> mNumLayers = 0;
- // Verify that transaction is being called by an approved process:
- // either AID_GRAPHICS or AID_SYSTEM.
- status_t CheckTransactCodeCredentials(uint32_t code);
-
// to linkToDeath
sp<IBinder> mWindowManager;
// We want to avoid multiple calls to BOOT_FINISHED as they come in on
@@ -1182,7 +1316,11 @@
SurfaceFlingerBE mBE;
std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine;
- /* ------------------------------------------------------------------------
+ const std::string mHwcServiceName;
+
+ bool hasMockHwc() const { return mHwcServiceName == "mock"; }
+
+ /*
* Scheduler
*/
std::unique_ptr<Scheduler> mScheduler;
@@ -1190,10 +1328,10 @@
scheduler::ConnectionHandle mSfConnectionHandle;
// Stores phase offsets configured per refresh rate.
- std::unique_ptr<scheduler::PhaseConfiguration> mPhaseConfiguration;
+ std::unique_ptr<scheduler::VsyncConfiguration> mVsyncConfiguration;
- // Optional to defer construction until scheduler connections are created.
- std::optional<scheduler::VSyncModulator> mVSyncModulator;
+ // Optional to defer construction until PhaseConfiguration is created.
+ std::optional<scheduler::VsyncModulator> mVsyncModulator;
std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
@@ -1202,58 +1340,33 @@
nsecs_t mScheduledPresentTime = 0;
hal::Vsync mHWCVsyncPendingState = hal::Vsync::DISABLE;
- /* ------------------------------------------------------------------------
- * Generic Layer Metadata
- */
- const std::unordered_map<std::string, uint32_t>& getGenericLayerMetadataKeyMap() const;
-
- /* ------------------------------------------------------------------------
- * Misc
- */
-
- std::optional<ActiveConfigInfo> getDesiredActiveConfig() EXCLUDES(mActiveConfigLock) {
- std::lock_guard<std::mutex> lock(mActiveConfigLock);
- if (mDesiredActiveConfigChanged) return mDesiredActiveConfig;
- return std::nullopt;
- }
-
- std::mutex mActiveConfigLock;
- // This bit is set once we start setting the config. We read from this bit during the
- // process. If at the end, this bit is different than mDesiredActiveConfig, we restart
+ std::mutex mActiveModeLock;
+ // This bit is set once we start setting the mode. We read from this bit during the
+ // process. If at the end, this bit is different than mDesiredActiveMode, we restart
// the process.
- ActiveConfigInfo mUpcomingActiveConfig; // Always read and written on the main thread.
- // This bit can be set at any point in time when the system wants the new config.
- ActiveConfigInfo mDesiredActiveConfig GUARDED_BY(mActiveConfigLock);
+ ActiveModeInfo mUpcomingActiveMode; // Always read and written on the main thread.
+ // This bit can be set at any point in time when the system wants the new mode.
+ ActiveModeInfo mDesiredActiveMode GUARDED_BY(mActiveModeLock);
// below flags are set by main thread only
- TracedOrdinal<bool> mDesiredActiveConfigChanged
- GUARDED_BY(mActiveConfigLock) = {"DesiredActiveConfigChanged", false};
- bool mSetActiveConfigPending = false;
+ TracedOrdinal<bool> mDesiredActiveModeChanged
+ GUARDED_BY(mActiveModeLock) = {"DesiredActiveModeChanged", false};
+ bool mSetActiveModePending = false;
bool mLumaSampling = true;
sp<RegionSamplingThread> mRegionSamplingThread;
+ sp<FpsReporter> mFpsReporter;
ui::DisplayPrimaries mInternalDisplayPrimaries;
const float mInternalDisplayDensity;
const float mEmulatedDisplayDensity;
- sp<IInputFlinger> mInputFlinger;
- InputWindowCommands mPendingInputWindowCommands GUARDED_BY(mStateLock);
+ sp<os::IInputFlinger> mInputFlinger;
// Should only be accessed by the main thread.
InputWindowCommands mInputWindowCommands;
- struct SetInputWindowsListener : BnSetInputWindowsListener {
- explicit SetInputWindowsListener(sp<SurfaceFlinger> flinger)
- : mFlinger(std::move(flinger)) {}
+ sp<SetInputWindowsListener> mSetInputWindowsListener;
- void onSetInputWindowsFinished() override;
-
- const sp<SurfaceFlinger> mFlinger;
- };
-
- const sp<SetInputWindowsListener> mSetInputWindowsListener = new SetInputWindowsListener(this);
-
- bool mPendingSyncInputWindows GUARDED_BY(mStateLock) = false;
Hwc2::impl::PowerAdvisor mPowerAdvisor;
// This should only be accessed on the main thread.
@@ -1262,8 +1375,8 @@
void enableRefreshRateOverlay(bool enable);
std::unique_ptr<RefreshRateOverlay> mRefreshRateOverlay GUARDED_BY(mStateLock);
- // Flag used to set override allowed display configs from backdoor
- bool mDebugDisplayConfigSetByBackdoor = false;
+ // Flag used to set override desired display mode from backdoor
+ bool mDebugDisplayModeSetByBackdoor = false;
// A set of layers that have no parent so they are not drawn on screen.
// Should only be accessed by the main thread.
@@ -1271,16 +1384,43 @@
// be any issues with a raw pointer referencing an invalid object.
std::unordered_set<Layer*> mOffscreenLayers;
- // Fields tracking the current jank event: when it started and how many
- // janky frames there are.
- nsecs_t mMissedFrameJankStart = 0;
- int32_t mMissedFrameJankCount = 0;
- // Positive if jank should be uploaded in postComposition
- nsecs_t mLastJankDuration = -1;
-
int mFrameRateFlexibilityTokenCount = 0;
sp<IBinder> mDebugFrameRateFlexibilityToken;
+
+ BufferCountTracker mBufferCountTracker;
+
+ std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners
+ GUARDED_BY(mStateLock);
+ mutable Mutex mCreatedLayersLock;
+ struct LayerCreatedState {
+ LayerCreatedState(const wp<Layer>& layer, const wp<IBinder>& parent,
+ const wp<Layer> parentLayer, const wp<IBinder>& producer)
+ : layer(layer),
+ initialParent(parent),
+ initialParentLayer(parentLayer),
+ initialProducer(producer) {}
+ wp<Layer> layer;
+ // Indicates the initial parent of the created layer, only used for creating layer in
+ // SurfaceFlinger. If nullptr, it may add the created layer into the current root layers.
+ wp<IBinder> initialParent;
+ wp<Layer> initialParentLayer;
+ // Indicates the initial graphic buffer producer of the created layer, only used for
+ // creating layer in SurfaceFlinger.
+ wp<IBinder> initialProducer;
+ };
+
+ // A temporay pool that store the created layers and will be added to current state in main
+ // thread.
+ std::unordered_map<BBinder*, std::unique_ptr<LayerCreatedState>> mCreatedLayers;
+ void setLayerCreatedState(const sp<IBinder>& handle, const wp<Layer>& layer,
+ const wp<IBinder>& parent, const wp<Layer> parentLayer,
+ const wp<IBinder>& producer);
+ auto getLayerCreatedState(const sp<IBinder>& handle);
+ sp<Layer> handleLayerCreatedLocked(const sp<IBinder>& handle, bool privileged)
+ REQUIRES(mStateLock);
+
+ std::atomic<ui::Transform::RotationFlags> mDefaultDisplayTransformHint;
};
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index ddd20a5..4a75180 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -28,6 +28,7 @@
#include "ContainerLayer.h"
#include "DisplayDevice.h"
#include "EffectLayer.h"
+#include "FrameTracer/FrameTracer.h"
#include "Layer.h"
#include "MonitoredProducer.h"
#include "NativeWindowSurface.h"
@@ -37,25 +38,15 @@
#include "SurfaceInterceptor.h"
#include "DisplayHardware/ComposerHal.h"
-#include "Scheduler/DispSync.h"
-#include "Scheduler/EventControlThread.h"
#include "Scheduler/MessageQueue.h"
-#include "Scheduler/PhaseOffsets.h"
#include "Scheduler/Scheduler.h"
+#include "Scheduler/VsyncConfiguration.h"
+#include "Scheduler/VsyncController.h"
namespace android::surfaceflinger {
DefaultFactory::~DefaultFactory() = default;
-std::unique_ptr<DispSync> DefaultFactory::createDispSync(const char* name, bool hasSyncFramework) {
- return std::make_unique<android::impl::DispSync>(name, hasSyncFramework);
-}
-
-std::unique_ptr<EventControlThread> DefaultFactory::createEventControlThread(
- SetVSyncEnabled setVSyncEnabled) {
- return std::make_unique<android::impl::EventControlThread>(std::move(setVSyncEnabled));
-}
-
std::unique_ptr<HWComposer> DefaultFactory::createHWComposer(const std::string& serviceName) {
return std::make_unique<android::impl::HWComposer>(serviceName);
}
@@ -64,26 +55,22 @@
return std::make_unique<android::impl::MessageQueue>();
}
-std::unique_ptr<scheduler::PhaseConfiguration> DefaultFactory::createPhaseConfiguration(
- const scheduler::RefreshRateConfigs& refreshRateConfigs) {
+std::unique_ptr<scheduler::VsyncConfiguration> DefaultFactory::createVsyncConfiguration(
+ Fps currentRefreshRate) {
if (property_get_bool("debug.sf.use_phase_offsets_as_durations", false)) {
- return std::make_unique<scheduler::impl::PhaseDurations>(refreshRateConfigs);
+ return std::make_unique<scheduler::impl::WorkDuration>(currentRefreshRate);
} else {
- return std::make_unique<scheduler::impl::PhaseOffsets>(refreshRateConfigs);
+ return std::make_unique<scheduler::impl::PhaseOffsets>(currentRefreshRate);
}
}
std::unique_ptr<Scheduler> DefaultFactory::createScheduler(
- SetVSyncEnabled setVSyncEnabled, const scheduler::RefreshRateConfigs& configs,
- ISchedulerCallback& schedulerCallback) {
- return std::make_unique<Scheduler>(std::move(setVSyncEnabled), configs, schedulerCallback,
- property_get_bool("debug.sf.use_content_detection_v2", true),
- sysprop::use_content_detection_for_refresh_rate(false));
+ const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback) {
+ return std::make_unique<Scheduler>(configs, callback);
}
-std::unique_ptr<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor(
- SurfaceFlinger* flinger) {
- return std::make_unique<android::impl::SurfaceInterceptor>(flinger);
+sp<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor() {
+ return new android::impl::SurfaceInterceptor();
}
sp<StartPropertySetThread> DefaultFactory::createStartPropertySetThread(
@@ -144,6 +131,15 @@
return new EffectLayer(args);
}
+std::unique_ptr<FrameTracer> DefaultFactory::createFrameTracer() {
+ return std::make_unique<FrameTracer>();
+}
+
+std::unique_ptr<frametimeline::FrameTimeline> DefaultFactory::createFrameTimeline(
+ std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid) {
+ return std::make_unique<frametimeline::impl::FrameTimeline>(timeStats, surfaceFlingerPid);
+}
+
} // namespace android::surfaceflinger
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index bd40cfb..24148dd 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -26,16 +26,13 @@
public:
virtual ~DefaultFactory();
- std::unique_ptr<DispSync> createDispSync(const char* name, bool hasSyncFramework) override;
- std::unique_ptr<EventControlThread> createEventControlThread(SetVSyncEnabled) override;
std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override;
std::unique_ptr<MessageQueue> createMessageQueue() override;
- std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
- const scheduler::RefreshRateConfigs&) override;
- std::unique_ptr<Scheduler> createScheduler(SetVSyncEnabled,
- const scheduler::RefreshRateConfigs&,
+ std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
+ Fps currentRefreshRate) override;
+ std::unique_ptr<Scheduler> createScheduler(const scheduler::RefreshRateConfigs&,
ISchedulerCallback&) override;
- std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) override;
+ sp<SurfaceInterceptor> createSurfaceInterceptor() override;
sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override;
sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&) override;
sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format,
@@ -57,6 +54,9 @@
sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) override;
sp<EffectLayer> createEffectLayer(const LayerCreationArgs& args) override;
sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) override;
+ std::unique_ptr<FrameTracer> createFrameTracer() override;
+ std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
+ std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid) override;
};
} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 6f4fcc6..885297f 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -16,6 +16,8 @@
#pragma once
+#include "Fps.h"
+
#include <cutils/compiler.h>
#include <utils/StrongPointer.h>
@@ -34,21 +36,21 @@
class EffectLayer;
class ContainerLayer;
class DisplayDevice;
-class DispSync;
-class EventControlThread;
+class FrameTracer;
class GraphicBuffer;
class HWComposer;
class IGraphicBufferConsumer;
class IGraphicBufferProducer;
-class ISchedulerCallback;
class Layer;
class MessageQueue;
class Scheduler;
class StartPropertySetThread;
class SurfaceFlinger;
class SurfaceInterceptor;
+class TimeStats;
struct DisplayDeviceCreationArgs;
+struct ISchedulerCallback;
struct LayerCreationArgs;
namespace compositionengine {
@@ -56,10 +58,15 @@
} // namespace compositionengine
namespace scheduler {
-class PhaseConfiguration;
+class VsyncConfiguration;
+class VsyncController;
class RefreshRateConfigs;
} // namespace scheduler
+namespace frametimeline {
+class FrameTimeline;
+} // namespace frametimeline
+
namespace surfaceflinger {
class NativeWindowSurface;
@@ -68,18 +75,13 @@
// of each interface.
class Factory {
public:
- using SetVSyncEnabled = std::function<void(bool)>;
-
- virtual std::unique_ptr<DispSync> createDispSync(const char* name, bool hasSyncFramework) = 0;
- virtual std::unique_ptr<EventControlThread> createEventControlThread(SetVSyncEnabled) = 0;
virtual std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) = 0;
virtual std::unique_ptr<MessageQueue> createMessageQueue() = 0;
- virtual std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
- const scheduler::RefreshRateConfigs&) = 0;
- virtual std::unique_ptr<Scheduler> createScheduler(SetVSyncEnabled,
- const scheduler::RefreshRateConfigs&,
+ virtual std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
+ Fps currentRefreshRate) = 0;
+ virtual std::unique_ptr<Scheduler> createScheduler(const scheduler::RefreshRateConfigs&,
ISchedulerCallback&) = 0;
- virtual std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) = 0;
+ virtual sp<SurfaceInterceptor> createSurfaceInterceptor() = 0;
virtual sp<StartPropertySetThread> createStartPropertySetThread(
bool timestampPropertyValue) = 0;
@@ -106,6 +108,9 @@
virtual sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) = 0;
virtual sp<EffectLayer> createEffectLayer(const LayerCreationArgs& args) = 0;
virtual sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) = 0;
+ virtual std::unique_ptr<FrameTracer> createFrameTracer() = 0;
+ virtual std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
+ std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid) = 0;
protected:
~Factory() = default;
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index 9d78702..b3dca78 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -371,5 +371,18 @@
return primaries;
}
+bool update_device_product_info_on_hotplug_reconnect(bool defaultValue) {
+ return SurfaceFlingerProperties::update_device_product_info_on_hotplug_reconnect().value_or(
+ defaultValue);
+}
+
+bool enable_frame_rate_override(bool defaultValue) {
+ return SurfaceFlingerProperties::enable_frame_rate_override().value_or(defaultValue);
+}
+
+bool enable_layer_caching(bool defaultValue) {
+ return SurfaceFlingerProperties::enable_layer_caching().value_or(defaultValue);
+}
+
} // namespace sysprop
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index c63adfe..b19d216 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -95,6 +95,13 @@
int32_t display_update_imminent_timeout_ms(int32_t defaultValue);
android::ui::DisplayPrimaries getDisplayNativePrimaries();
+
+bool update_device_product_info_on_hotplug_reconnect(bool defaultValue);
+
+bool enable_frame_rate_override(bool defaultValue);
+
+bool enable_layer_caching(bool defaultValue);
+
} // namespace sysprop
} // namespace android
#endif // SURFACEFLINGERPROPERTIES_H_
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 80102bd..113f463 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -40,9 +40,22 @@
namespace impl {
-SurfaceInterceptor::SurfaceInterceptor(SurfaceFlinger* flinger)
- : mFlinger(flinger)
-{
+void SurfaceInterceptor::addTransactionTraceListener(
+ const sp<gui::ITransactionTraceListener>& listener) {
+ sp<IBinder> asBinder = IInterface::asBinder(listener);
+
+ std::scoped_lock lock(mListenersMutex);
+
+ asBinder->linkToDeath(this);
+
+ listener->onToggled(mEnabled); // notifies of current state
+
+ mTraceToggledListeners.emplace(asBinder, listener);
+}
+
+void SurfaceInterceptor::binderDied(const wp<IBinder>& who) {
+ std::scoped_lock lock(mListenersMutex);
+ mTraceToggledListeners.erase(who);
}
void SurfaceInterceptor::enable(const SortedVector<sp<Layer>>& layers,
@@ -52,8 +65,14 @@
return;
}
ATRACE_CALL();
+ {
+ std::scoped_lock lock(mListenersMutex);
+ for (const auto& [_, listener] : mTraceToggledListeners) {
+ listener->onToggled(true);
+ }
+ }
mEnabled = true;
- std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+ std::scoped_lock<std::mutex> protoGuard(mTraceMutex);
saveExistingDisplaysLocked(displays);
saveExistingSurfacesLocked(layers);
}
@@ -63,8 +82,14 @@
return;
}
ATRACE_CALL();
- std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+ {
+ std::scoped_lock lock(mListenersMutex);
+ for (const auto& [_, listener] : mTraceToggledListeners) {
+ listener->onToggled(false);
+ }
+ }
mEnabled = false;
+ std::scoped_lock<std::mutex> protoGuard(mTraceMutex);
status_t err(writeProtoFileLocked());
ALOGE_IF(err == PERMISSION_DENIED, "Could not save the proto file! Permission denied");
ALOGE_IF(err == NOT_ENOUGH_DATA, "Could not save the proto file! There are missing fields");
@@ -112,20 +137,14 @@
addTransparentRegionLocked(transaction, layerId,
layer->mCurrentState.activeTransparentRegion_legacy);
addLayerStackLocked(transaction, layerId, layer->mCurrentState.layerStack);
- addCropLocked(transaction, layerId, layer->mCurrentState.crop_legacy);
+ addCropLocked(transaction, layerId, layer->mCurrentState.crop);
addCornerRadiusLocked(transaction, layerId, layer->mCurrentState.cornerRadius);
addBackgroundBlurRadiusLocked(transaction, layerId, layer->mCurrentState.backgroundBlurRadius);
- if (layer->mCurrentState.barrierLayer_legacy != nullptr) {
- addDeferTransactionLocked(transaction, layerId,
- layer->mCurrentState.barrierLayer_legacy.promote(),
- layer->mCurrentState.frameNumber_legacy);
- }
- addOverrideScalingModeLocked(transaction, layerId, layer->getEffectiveScalingMode());
+ addBlurRegionsLocked(transaction, layerId, layer->mCurrentState.blurRegions);
addFlagsLocked(transaction, layerId, layer->mCurrentState.flags,
layer_state_t::eLayerHidden | layer_state_t::eLayerOpaque |
layer_state_t::eLayerSecure);
addReparentLocked(transaction, layerId, getLayerIdFromWeakRef(layer->mCurrentParent));
- addDetachChildrenLocked(transaction, layerId, layer->isLayerDetached());
addRelativeParentLocked(transaction, layerId,
getLayerIdFromWeakRef(layer->mCurrentState.zOrderRelativeOf),
layer->mCurrentState.z);
@@ -143,7 +162,7 @@
addDisplayLayerStackLocked(transaction, display.sequenceId, display.layerStack);
addDisplaySizeLocked(transaction, display.sequenceId, display.width, display.height);
addDisplayProjectionLocked(transaction, display.sequenceId, toRotationInt(display.orientation),
- display.viewport, display.frame);
+ display.layerStackSpaceRect, display.orientedDisplaySpaceRect);
}
status_t SurfaceInterceptor::writeProtoFileLocked() {
@@ -221,6 +240,13 @@
protoRect->set_bottom(rect.bottom);
}
+void SurfaceInterceptor::setTransactionOriginLocked(Transaction* transaction, int32_t pid,
+ int32_t uid) {
+ Origin* origin(transaction->mutable_origin());
+ origin->set_pid(pid);
+ origin->set_uid(uid);
+}
+
void SurfaceInterceptor::addPositionLocked(Transaction* transaction, int32_t layerId,
float x, float y)
{
@@ -330,26 +356,23 @@
blurRadiusChange->set_background_blur_radius(backgroundBlurRadius);
}
-void SurfaceInterceptor::addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
- const sp<const Layer>& layer, uint64_t frameNumber)
-{
+void SurfaceInterceptor::addBlurRegionsLocked(Transaction* transaction, int32_t layerId,
+ const std::vector<BlurRegion>& blurRegions) {
SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
- if (layer == nullptr) {
- ALOGE("An existing layer could not be retrieved with the handle"
- " for the deferred transaction");
- return;
+ BlurRegionsChange* blurRegionsChange(change->mutable_blur_regions());
+ for (const auto blurRegion : blurRegions) {
+ const auto blurRegionChange = blurRegionsChange->add_blur_regions();
+ blurRegionChange->set_blur_radius(blurRegion.blurRadius);
+ blurRegionChange->set_corner_radius_tl(blurRegion.cornerRadiusTL);
+ blurRegionChange->set_corner_radius_tr(blurRegion.cornerRadiusTR);
+ blurRegionChange->set_corner_radius_bl(blurRegion.cornerRadiusBL);
+ blurRegionChange->set_corner_radius_br(blurRegion.cornerRadiusBR);
+ blurRegionChange->set_alpha(blurRegion.alpha);
+ blurRegionChange->set_left(blurRegion.left);
+ blurRegionChange->set_top(blurRegion.top);
+ blurRegionChange->set_right(blurRegion.right);
+ blurRegionChange->set_bottom(blurRegion.bottom);
}
- DeferredTransactionChange* deferTransaction(change->mutable_deferred_transaction());
- deferTransaction->set_layer_id(getLayerId(layer));
- deferTransaction->set_frame_number(frameNumber);
-}
-
-void SurfaceInterceptor::addOverrideScalingModeLocked(Transaction* transaction,
- int32_t layerId, int32_t overrideScalingMode)
-{
- SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
- OverrideScalingModeChange* overrideChange(change->mutable_override_scaling_mode());
- overrideChange->set_override_scaling_mode(overrideScalingMode);
}
void SurfaceInterceptor::addReparentLocked(Transaction* transaction, int32_t layerId,
@@ -359,20 +382,6 @@
overrideChange->set_parent_id(parentId);
}
-void SurfaceInterceptor::addReparentChildrenLocked(Transaction* transaction, int32_t layerId,
- int32_t parentId) {
- SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
- ReparentChildrenChange* overrideChange(change->mutable_reparent_children());
- overrideChange->set_parent_id(parentId);
-}
-
-void SurfaceInterceptor::addDetachChildrenLocked(Transaction* transaction, int32_t layerId,
- bool detached) {
- SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
- DetachChildrenChange* overrideChange(change->mutable_detach_children());
- overrideChange->set_detach_children(detached);
-}
-
void SurfaceInterceptor::addRelativeParentLocked(Transaction* transaction, int32_t layerId,
int32_t parentId, int z) {
SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
@@ -424,8 +433,8 @@
if (state.what & layer_state_t::eLayerStackChanged) {
addLayerStackLocked(transaction, layerId, state.layerStack);
}
- if (state.what & layer_state_t::eCropChanged_legacy) {
- addCropLocked(transaction, layerId, state.crop_legacy);
+ if (state.what & layer_state_t::eCropChanged) {
+ addCropLocked(transaction, layerId, state.crop);
}
if (state.what & layer_state_t::eCornerRadiusChanged) {
addCornerRadiusLocked(transaction, layerId, state.cornerRadius);
@@ -433,40 +442,27 @@
if (state.what & layer_state_t::eBackgroundBlurRadiusChanged) {
addBackgroundBlurRadiusLocked(transaction, layerId, state.backgroundBlurRadius);
}
- if (state.what & layer_state_t::eDeferTransaction_legacy) {
- sp<Layer> otherLayer = nullptr;
- if (state.barrierHandle_legacy != nullptr) {
- otherLayer =
- static_cast<Layer::Handle*>(state.barrierHandle_legacy.get())->owner.promote();
- } else if (state.barrierGbp_legacy != nullptr) {
- auto const& gbp = state.barrierGbp_legacy;
- if (mFlinger->authenticateSurfaceTextureLocked(gbp)) {
- otherLayer = (static_cast<MonitoredProducer*>(gbp.get()))->getLayer();
- } else {
- ALOGE("Attempt to defer transaction to to an unrecognized GraphicBufferProducer");
- }
- }
- addDeferTransactionLocked(transaction, layerId, otherLayer, state.frameNumber_legacy);
- }
- if (state.what & layer_state_t::eOverrideScalingModeChanged) {
- addOverrideScalingModeLocked(transaction, layerId, state.overrideScalingMode);
+ if (state.what & layer_state_t::eBlurRegionsChanged) {
+ addBlurRegionsLocked(transaction, layerId, state.blurRegions);
}
if (state.what & layer_state_t::eReparent) {
- addReparentLocked(transaction, layerId, getLayerIdFromHandle(state.parentHandleForChild));
- }
- if (state.what & layer_state_t::eReparentChildren) {
- addReparentChildrenLocked(transaction, layerId, getLayerIdFromHandle(state.reparentHandle));
- }
- if (state.what & layer_state_t::eDetachChildren) {
- addDetachChildrenLocked(transaction, layerId, true);
+ auto parentHandle = (state.parentSurfaceControlForChild)
+ ? state.parentSurfaceControlForChild->getHandle()
+ : nullptr;
+ addReparentLocked(transaction, layerId, getLayerIdFromHandle(parentHandle));
}
if (state.what & layer_state_t::eRelativeLayerChanged) {
addRelativeParentLocked(transaction, layerId,
- getLayerIdFromHandle(state.relativeLayerHandle), state.z);
+ getLayerIdFromHandle(
+ state.relativeLayerSurfaceControl->getHandle()),
+ state.z);
}
if (state.what & layer_state_t::eShadowRadiusChanged) {
addShadowRadiusLocked(transaction, layerId, state.shadowRadius);
}
+ if (state.what & layer_state_t::eStretchChanged) {
+ ALOGW("SurfaceInterceptor not implemented for eStretchChanged");
+ }
}
void SurfaceInterceptor::addDisplayChangesLocked(Transaction* transaction,
@@ -483,18 +479,20 @@
}
if (state.what & DisplayState::eDisplayProjectionChanged) {
addDisplayProjectionLocked(transaction, sequenceId, toRotationInt(state.orientation),
- state.viewport, state.frame);
+ state.layerStackSpaceRect, state.orientedDisplaySpaceRect);
}
}
-void SurfaceInterceptor::addTransactionLocked(Increment* increment,
- const Vector<ComposerState>& stateUpdates,
- const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays,
- const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags)
-{
+void SurfaceInterceptor::addTransactionLocked(
+ Increment* increment, const Vector<ComposerState>& stateUpdates,
+ const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
+ const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags, int originPid,
+ int originUid, uint64_t transactionId) {
Transaction* transaction(increment->mutable_transaction());
transaction->set_synchronous(transactionFlags & BnSurfaceComposer::eSynchronous);
transaction->set_animation(transactionFlags & BnSurfaceComposer::eAnimation);
+ setTransactionOriginLocked(transaction, originPid, originUid);
+ transaction->set_id(transactionId);
for (const auto& compState: stateUpdates) {
addSurfaceChangesLocked(transaction, compState.state);
}
@@ -613,17 +611,18 @@
powerModeUpdate->set_mode(mode);
}
-void SurfaceInterceptor::saveTransaction(const Vector<ComposerState>& stateUpdates,
- const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays,
- const Vector<DisplayState>& changedDisplays, uint32_t flags)
-{
+void SurfaceInterceptor::saveTransaction(
+ const Vector<ComposerState>& stateUpdates,
+ const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
+ const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPid, int originUid,
+ uint64_t transactionId) {
if (!mEnabled || (stateUpdates.size() <= 0 && changedDisplays.size() <= 0)) {
return;
}
ATRACE_CALL();
std::lock_guard<std::mutex> protoGuard(mTraceMutex);
addTransactionLocked(createTraceIncrementLocked(), stateUpdates, displays, changedDisplays,
- flags);
+ flags, originPid, originUid, transactionId);
}
void SurfaceInterceptor::saveSurfaceCreation(const sp<const Layer>& layer) {
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index 896bdcc..30aca83 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -21,6 +21,8 @@
#include <mutex>
+#include <binder/IBinder.h>
+
#include <gui/LayerState.h>
#include <utils/KeyedVector.h>
@@ -48,7 +50,7 @@
constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.pb";
-class SurfaceInterceptor {
+class SurfaceInterceptor : public IBinder::DeathRecipient {
public:
virtual ~SurfaceInterceptor();
@@ -58,11 +60,16 @@
virtual void disable() = 0;
virtual bool isEnabled() = 0;
+ virtual void addTransactionTraceListener(
+ const sp<gui::ITransactionTraceListener>& listener) = 0;
+ virtual void binderDied(const wp<IBinder>& who) = 0;
+
// Intercept display and surface transactions
virtual void saveTransaction(
const Vector<ComposerState>& stateUpdates,
const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
- const Vector<DisplayState>& changedDisplays, uint32_t flags) = 0;
+ const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPid,
+ int originUid, uint64_t transactionId) = 0;
// Intercept surface data
virtual void saveSurfaceCreation(const sp<const Layer>& layer) = 0;
@@ -85,7 +92,7 @@
*/
class SurfaceInterceptor final : public android::SurfaceInterceptor {
public:
- explicit SurfaceInterceptor(SurfaceFlinger* const flinger);
+ SurfaceInterceptor() = default;
~SurfaceInterceptor() override = default;
// Both vectors are used to capture the current state of SF as the initial snapshot in the trace
@@ -94,10 +101,14 @@
void disable() override;
bool isEnabled() override;
+ void addTransactionTraceListener(const sp<gui::ITransactionTraceListener>& listener) override;
+ void binderDied(const wp<IBinder>& who) override;
+
// Intercept display and surface transactions
void saveTransaction(const Vector<ComposerState>& stateUpdates,
const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
- const Vector<DisplayState>& changedDisplays, uint32_t flags) override;
+ const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPid,
+ int originUid, uint64_t transactionId) override;
// Intercept surface data
void saveSurfaceCreation(const sp<const Layer>& layer) override;
@@ -154,17 +165,15 @@
void addCornerRadiusLocked(Transaction* transaction, int32_t layerId, float cornerRadius);
void addBackgroundBlurRadiusLocked(Transaction* transaction, int32_t layerId,
int32_t backgroundBlurRadius);
- void addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
- const sp<const Layer>& layer, uint64_t frameNumber);
- void addOverrideScalingModeLocked(Transaction* transaction, int32_t layerId,
- int32_t overrideScalingMode);
+ void addBlurRegionsLocked(Transaction* transaction, int32_t layerId,
+ const std::vector<BlurRegion>& effectRegions);
void addSurfaceChangesLocked(Transaction* transaction, const layer_state_t& state);
void addTransactionLocked(Increment* increment, const Vector<ComposerState>& stateUpdates,
- const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays,
- const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags);
+ const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
+ const Vector<DisplayState>& changedDisplays,
+ uint32_t transactionFlags, int originPid, int originUid,
+ uint64_t transactionId);
void addReparentLocked(Transaction* transaction, int32_t layerId, int32_t parentId);
- void addReparentChildrenLocked(Transaction* transaction, int32_t layerId, int32_t parentId);
- void addDetachChildrenLocked(Transaction* transaction, int32_t layerId, bool detached);
void addRelativeParentLocked(Transaction* transaction, int32_t layerId, int32_t parentId,
int z);
void addShadowRadiusLocked(Transaction* transaction, int32_t layerId, float shadowRadius);
@@ -182,12 +191,16 @@
void addDisplayChangesLocked(Transaction* transaction,
const DisplayState& state, int32_t sequenceId);
+ // Add transaction origin to trace
+ void setTransactionOriginLocked(Transaction* transaction, int32_t pid, int32_t uid);
bool mEnabled {false};
std::string mOutputFileName {DEFAULT_FILENAME};
std::mutex mTraceMutex {};
Trace mTrace {};
- SurfaceFlinger* const mFlinger;
+ std::mutex mListenersMutex;
+ std::map<wp<IBinder>, sp<gui::ITransactionTraceListener>> mTraceToggledListeners
+ GUARDED_BY(mListenersMutex);
};
} // namespace impl
diff --git a/services/surfaceflinger/SurfaceTracing.cpp b/services/surfaceflinger/SurfaceTracing.cpp
index d84ce69..b4d8a9a 100644
--- a/services/surfaceflinger/SurfaceTracing.cpp
+++ b/services/surfaceflinger/SurfaceTracing.cpp
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
#undef LOG_TAG
#define LOG_TAG "SurfaceTracing"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -32,65 +29,67 @@
namespace android {
-SurfaceTracing::SurfaceTracing(SurfaceFlinger& flinger)
- : mFlinger(flinger), mSfLock(flinger.mTracingLock) {}
+SurfaceTracing::SurfaceTracing(SurfaceFlinger& flinger) : mFlinger(flinger) {}
-void SurfaceTracing::mainLoop() {
- bool enabled = addFirstEntry();
- while (enabled) {
- LayersTraceProto entry = traceWhenNotified();
- enabled = addTraceToBuffer(entry);
- }
-}
-
-bool SurfaceTracing::addFirstEntry() {
- LayersTraceProto entry;
- {
- std::scoped_lock lock(mSfLock);
- entry = traceLayersLocked("tracing.enable");
- }
- return addTraceToBuffer(entry);
-}
-
-LayersTraceProto SurfaceTracing::traceWhenNotified() {
- std::unique_lock<std::mutex> lock(mSfLock);
- mCanStartTrace.wait(lock);
- android::base::ScopedLockAssertion assumeLock(mSfLock);
- LayersTraceProto entry = traceLayersLocked(mWhere);
- mTracingInProgress = false;
- mMissedTraceEntries = 0;
- lock.unlock();
- return entry;
-}
-
-bool SurfaceTracing::addTraceToBuffer(LayersTraceProto& entry) {
+bool SurfaceTracing::enable() {
std::scoped_lock lock(mTraceLock);
- mBuffer.emplace(std::move(entry));
- if (mWriteToFile) {
- writeProtoFileLocked();
- mWriteToFile = false;
+ if (mEnabled) {
+ return false;
}
+
+ if (flagIsSet(TRACE_SYNC)) {
+ runner = std::make_unique<SurfaceTracing::Runner>(mFlinger, mConfig);
+ } else {
+ runner = std::make_unique<SurfaceTracing::AsyncRunner>(mFlinger, mConfig,
+ mFlinger.mTracingLock);
+ }
+ mEnabled = true;
+ return true;
+}
+
+bool SurfaceTracing::disable() {
+ std::scoped_lock lock(mTraceLock);
+ if (!mEnabled) {
+ return false;
+ }
+ mEnabled = false;
+ runner->stop();
+ return true;
+}
+
+bool SurfaceTracing::isEnabled() const {
+ std::scoped_lock lock(mTraceLock);
return mEnabled;
}
+status_t SurfaceTracing::writeToFile() {
+ std::scoped_lock lock(mTraceLock);
+ if (!mEnabled) {
+ return STATUS_OK;
+ }
+ return runner->writeToFile();
+}
+
void SurfaceTracing::notify(const char* where) {
- std::scoped_lock lock(mSfLock);
- notifyLocked(where);
+ std::scoped_lock lock(mTraceLock);
+ if (mEnabled) {
+ runner->notify(where);
+ }
}
void SurfaceTracing::notifyLocked(const char* where) {
- mWhere = where;
- if (mTracingInProgress) {
- mMissedTraceEntries++;
+ std::scoped_lock lock(mTraceLock);
+ if (mEnabled) {
+ runner->notifyLocked(where);
}
- mTracingInProgress = true;
- mCanStartTrace.notify_one();
}
-void SurfaceTracing::writeToFileAsync() {
+void SurfaceTracing::dump(std::string& result) const {
std::scoped_lock lock(mTraceLock);
- mWriteToFile = true;
- mCanStartTrace.notify_one();
+ base::StringAppendF(&result, "Tracing state: %s\n", mEnabled ? "enabled" : "disabled");
+ if (mEnabled) {
+ runner->dump(result);
+ }
}
void SurfaceTracing::LayersTraceBuffer::reset(size_t newSize) {
@@ -101,12 +100,12 @@
}
void SurfaceTracing::LayersTraceBuffer::emplace(LayersTraceProto&& proto) {
- auto protoSize = proto.ByteSize();
+ size_t protoSize = static_cast<size_t>(proto.ByteSize());
while (mUsedInBytes + protoSize > mSizeInBytes) {
if (mStorage.empty()) {
return;
}
- mUsedInBytes -= mStorage.front().ByteSize();
+ mUsedInBytes -= static_cast<size_t>(mStorage.front().ByteSize());
mStorage.pop();
}
mUsedInBytes += protoSize;
@@ -115,7 +114,7 @@
}
void SurfaceTracing::LayersTraceBuffer::flush(LayersTraceFileProto* fileProto) {
- fileProto->mutable_entry()->Reserve(mStorage.size());
+ fileProto->mutable_entry()->Reserve(static_cast<int>(mStorage.size()));
while (!mStorage.empty()) {
auto entry = fileProto->add_entry();
@@ -124,85 +123,21 @@
}
}
-bool SurfaceTracing::enable() {
- std::scoped_lock lock(mTraceLock);
-
- if (mEnabled) {
- return false;
- }
-
- mBuffer.reset(mBufferSize);
- mEnabled = true;
- mThread = std::thread(&SurfaceTracing::mainLoop, this);
- return true;
+SurfaceTracing::Runner::Runner(SurfaceFlinger& flinger, SurfaceTracing::Config& config)
+ : mFlinger(flinger), mConfig(config) {
+ mBuffer.setSize(mConfig.bufferSize);
}
-status_t SurfaceTracing::writeToFile() {
- std::thread thread;
- {
- std::scoped_lock lock(mTraceLock);
- thread = std::move(mThread);
- }
- thread.join();
- return mLastErr;
+void SurfaceTracing::Runner::notify(const char* where) {
+ LayersTraceProto entry = traceLayers(where);
+ mBuffer.emplace(std::move(entry));
}
-bool SurfaceTracing::disable() {
- std::scoped_lock lock(mTraceLock);
-
- if (!mEnabled) {
- return false;
- }
-
- mEnabled = false;
- mWriteToFile = true;
- mCanStartTrace.notify_all();
- return true;
+status_t SurfaceTracing::Runner::stop() {
+ return writeToFile();
}
-bool SurfaceTracing::isEnabled() const {
- std::scoped_lock lock(mTraceLock);
- return mEnabled;
-}
-
-void SurfaceTracing::setBufferSize(size_t bufferSizeInByte) {
- std::scoped_lock lock(mTraceLock);
- mBufferSize = bufferSizeInByte;
- mBuffer.setSize(bufferSizeInByte);
-}
-
-void SurfaceTracing::setTraceFlags(uint32_t flags) {
- std::scoped_lock lock(mSfLock);
- mTraceFlags = flags;
-}
-
-LayersTraceProto SurfaceTracing::traceLayersLocked(const char* where) {
- ATRACE_CALL();
-
- LayersTraceProto entry;
- entry.set_elapsed_realtime_nanos(elapsedRealtimeNano());
- entry.set_where(where);
- LayersProto layers(mFlinger.dumpDrawingStateProto(mTraceFlags));
-
- if (flagIsSetLocked(SurfaceTracing::TRACE_EXTRA)) {
- mFlinger.dumpOffscreenLayersProto(layers);
- }
- entry.mutable_layers()->Swap(&layers);
-
- if (mTraceFlags & SurfaceTracing::TRACE_HWC) {
- std::string hwcDump;
- mFlinger.dumpHwc(hwcDump);
- entry.set_hwc_blob(hwcDump);
- }
- if (!flagIsSetLocked(SurfaceTracing::TRACE_COMPOSITION)) {
- entry.set_excludes_composition_state(true);
- }
- entry.set_missed_entries(mMissedTraceEntries);
-
- return entry;
-}
-
-void SurfaceTracing::writeProtoFileLocked() {
+status_t SurfaceTracing::Runner::writeToFile() {
ATRACE_CALL();
LayersTraceFileProto fileProto;
@@ -211,33 +146,114 @@
fileProto.set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 |
LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L);
mBuffer.flush(&fileProto);
- mBuffer.reset(mBufferSize);
+ mBuffer.reset(mConfig.bufferSize);
if (!fileProto.SerializeToString(&output)) {
ALOGE("Could not save the proto file! Permission denied");
- mLastErr = PERMISSION_DENIED;
+ return PERMISSION_DENIED;
}
// -rw-r--r--
const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
- if (!android::base::WriteStringToFile(output, kDefaultFileName, mode, getuid(), getgid(),
+ if (!android::base::WriteStringToFile(output, DEFAULT_FILE_NAME, mode, getuid(), getgid(),
true)) {
ALOGE("Could not save the proto file! There are missing fields");
- mLastErr = PERMISSION_DENIED;
+ return PERMISSION_DENIED;
}
- mLastErr = NO_ERROR;
+ return NO_ERROR;
}
-void SurfaceTracing::dump(std::string& result) const {
- std::scoped_lock lock(mTraceLock);
- base::StringAppendF(&result, "Tracing state: %s\n", mEnabled ? "enabled" : "disabled");
+LayersTraceProto SurfaceTracing::Runner::traceLayers(const char* where) {
+ ATRACE_CALL();
+
+ LayersTraceProto entry;
+ entry.set_elapsed_realtime_nanos(elapsedRealtimeNano());
+ entry.set_where(where);
+ LayersProto layers(mFlinger.dumpDrawingStateProto(mConfig.flags));
+
+ if (flagIsSet(SurfaceTracing::TRACE_EXTRA)) {
+ mFlinger.dumpOffscreenLayersProto(layers);
+ }
+ entry.mutable_layers()->Swap(&layers);
+
+ if (flagIsSet(SurfaceTracing::TRACE_HWC)) {
+ std::string hwcDump;
+ mFlinger.dumpHwc(hwcDump);
+ entry.set_hwc_blob(hwcDump);
+ }
+ if (!flagIsSet(SurfaceTracing::TRACE_COMPOSITION)) {
+ entry.set_excludes_composition_state(true);
+ }
+ entry.set_missed_entries(mMissedTraceEntries);
+
+ return entry;
+}
+
+void SurfaceTracing::Runner::dump(std::string& result) const {
base::StringAppendF(&result, " number of entries: %zu (%.2fMB / %.2fMB)\n",
mBuffer.frameCount(), float(mBuffer.used()) / float(1_MB),
float(mBuffer.size()) / float(1_MB));
}
-} // namespace android
+SurfaceTracing::AsyncRunner::AsyncRunner(SurfaceFlinger& flinger, SurfaceTracing::Config& config,
+ std::mutex& sfLock)
+ : SurfaceTracing::Runner(flinger, config), mSfLock(sfLock) {
+ mEnabled = true;
+ mThread = std::thread(&AsyncRunner::loop, this);
+}
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+void SurfaceTracing::AsyncRunner::loop() {
+ while (mEnabled) {
+ LayersTraceProto entry;
+ bool entryAdded = traceWhenNotified(&entry);
+ if (entryAdded) {
+ mBuffer.emplace(std::move(entry));
+ }
+ if (mWriteToFile) {
+ Runner::writeToFile();
+ mWriteToFile = false;
+ }
+ }
+}
+
+bool SurfaceTracing::AsyncRunner::traceWhenNotified(LayersTraceProto* outProto) {
+ std::unique_lock<std::mutex> lock(mSfLock);
+ mCanStartTrace.wait(lock);
+ if (!mAddEntry) {
+ return false;
+ }
+ *outProto = traceLayers(mWhere);
+ mAddEntry = false;
+ mMissedTraceEntries = 0;
+ return true;
+}
+
+void SurfaceTracing::AsyncRunner::notify(const char* where) {
+ std::scoped_lock lock(mSfLock);
+ notifyLocked(where);
+}
+
+void SurfaceTracing::AsyncRunner::notifyLocked(const char* where) {
+ mWhere = where;
+ if (mAddEntry) {
+ mMissedTraceEntries++;
+ }
+ mAddEntry = true;
+ mCanStartTrace.notify_one();
+}
+
+status_t SurfaceTracing::AsyncRunner::writeToFile() {
+ mWriteToFile = true;
+ mCanStartTrace.notify_one();
+ return STATUS_OK;
+}
+
+status_t SurfaceTracing::AsyncRunner::stop() {
+ mEnabled = false;
+ mCanStartTrace.notify_one();
+ mThread.join();
+ return Runner::writeToFile();
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/SurfaceTracing.h b/services/surfaceflinger/SurfaceTracing.h
index f208eb8..15a503d 100644
--- a/services/surfaceflinger/SurfaceTracing.h
+++ b/services/surfaceflinger/SurfaceTracing.h
@@ -32,25 +32,31 @@
namespace android {
class SurfaceFlinger;
-
constexpr auto operator""_MB(unsigned long long const num) {
return num * 1024 * 1024;
}
/*
- * SurfaceTracing records layer states during surface flinging.
+ * SurfaceTracing records layer states during surface flinging. Manages tracing state and
+ * configuration.
*/
class SurfaceTracing {
public:
- explicit SurfaceTracing(SurfaceFlinger& flinger);
+ SurfaceTracing(SurfaceFlinger& flinger);
bool enable();
bool disable();
status_t writeToFile();
bool isEnabled() const;
+ /*
+ * Adds a trace entry, must be called from the drawing thread or while holding the
+ * SurfaceFlinger tracing lock.
+ */
void notify(const char* where);
- void notifyLocked(const char* where) NO_THREAD_SAFETY_ANALYSIS /* REQUIRES(mSfLock) */;
+ /*
+ * Adds a trace entry, called while holding the SurfaceFlinger tracing lock.
+ */
+ void notifyLocked(const char* where) /* REQUIRES(mSfLock) */;
- void setBufferSize(size_t bufferSizeInByte);
- void writeToFileAsync();
+ void setBufferSize(size_t bufferSizeInBytes) { mConfig.bufferSize = bufferSizeInBytes; }
void dump(std::string& result) const;
enum : uint32_t {
@@ -59,18 +65,34 @@
TRACE_COMPOSITION = 1 << 2,
TRACE_EXTRA = 1 << 3,
TRACE_HWC = 1 << 4,
- TRACE_ALL = 0xffffffff
+ // Add non-geometry composition changes to the trace.
+ TRACE_BUFFERS = 1 << 5,
+ // Add entries from the drawing thread post composition.
+ TRACE_SYNC = 1 << 6,
+ TRACE_ALL = TRACE_CRITICAL | TRACE_INPUT | TRACE_COMPOSITION | TRACE_EXTRA,
};
- void setTraceFlags(uint32_t flags);
- bool flagIsSetLocked(uint32_t flags) NO_THREAD_SAFETY_ANALYSIS /* REQUIRES(mSfLock) */ {
- return (mTraceFlags & flags) == flags;
- }
+ void setTraceFlags(uint32_t flags) { mConfig.flags = flags; }
+ bool flagIsSet(uint32_t flags) { return (mConfig.flags & flags) == flags; }
private:
- static constexpr auto kDefaultBufferCapInByte = 5_MB;
- static constexpr auto kDefaultFileName = "/data/misc/wmtrace/layers_trace.pb";
+ class Runner;
+ static constexpr auto DEFAULT_BUFFER_SIZE = 5_MB;
+ static constexpr auto DEFAULT_FILE_NAME = "/data/misc/wmtrace/layers_trace.pb";
- class LayersTraceBuffer { // ring buffer
+ SurfaceFlinger& mFlinger;
+ mutable std::mutex mTraceLock;
+ bool mEnabled GUARDED_BY(mTraceLock) = false;
+ std::unique_ptr<Runner> runner GUARDED_BY(mTraceLock);
+
+ struct Config {
+ uint32_t flags = TRACE_CRITICAL | TRACE_INPUT | TRACE_SYNC;
+ size_t bufferSize = DEFAULT_BUFFER_SIZE;
+ } mConfig;
+
+ /*
+ * ring buffer.
+ */
+ class LayersTraceBuffer {
public:
size_t size() const { return mSizeInBytes; }
size_t used() const { return mUsedInBytes; }
@@ -83,35 +105,59 @@
private:
size_t mUsedInBytes = 0U;
- size_t mSizeInBytes = 0U;
+ size_t mSizeInBytes = DEFAULT_BUFFER_SIZE;
std::queue<LayersTraceProto> mStorage;
};
- void mainLoop();
- bool addFirstEntry();
- LayersTraceProto traceWhenNotified();
- LayersTraceProto traceLayersLocked(const char* where) REQUIRES(mSfLock);
+ /*
+ * Implements a synchronous way of adding trace entries. This must be called
+ * from the drawing thread.
+ */
+ class Runner {
+ public:
+ Runner(SurfaceFlinger& flinger, SurfaceTracing::Config& config);
+ virtual ~Runner() = default;
+ virtual status_t stop();
+ virtual status_t writeToFile();
+ virtual void notify(const char* where);
+ /* Cannot be called with a synchronous runner. */
+ virtual void notifyLocked(const char* /* where */) {}
+ void dump(std::string& result) const;
- // Returns true if trace is enabled.
- bool addTraceToBuffer(LayersTraceProto& entry);
- void writeProtoFileLocked() REQUIRES(mTraceLock);
+ protected:
+ bool flagIsSet(uint32_t flags) { return (mConfig.flags & flags) == flags; }
+ SurfaceFlinger& mFlinger;
+ SurfaceTracing::Config mConfig;
+ SurfaceTracing::LayersTraceBuffer mBuffer;
+ uint32_t mMissedTraceEntries = 0;
+ LayersTraceProto traceLayers(const char* where);
+ };
- SurfaceFlinger& mFlinger;
- status_t mLastErr = NO_ERROR;
- std::thread mThread;
- std::condition_variable mCanStartTrace;
+ /*
+ * Implements asynchronous way to add trace entries called from a separate thread while holding
+ * the SurfaceFlinger tracing lock. Trace entries may be missed if the tracing thread is not
+ * scheduled in time.
+ */
+ class AsyncRunner : public Runner {
+ public:
+ AsyncRunner(SurfaceFlinger& flinger, SurfaceTracing::Config& config, std::mutex& sfLock);
+ virtual ~AsyncRunner() = default;
+ status_t stop() override;
+ status_t writeToFile() override;
+ void notify(const char* where) override;
+ void notifyLocked(const char* where);
- std::mutex& mSfLock;
- uint32_t mTraceFlags GUARDED_BY(mSfLock) = TRACE_CRITICAL | TRACE_INPUT;
- const char* mWhere GUARDED_BY(mSfLock) = "";
- uint32_t mMissedTraceEntries GUARDED_BY(mSfLock) = 0;
- bool mTracingInProgress GUARDED_BY(mSfLock) = false;
-
- mutable std::mutex mTraceLock;
- LayersTraceBuffer mBuffer GUARDED_BY(mTraceLock);
- size_t mBufferSize GUARDED_BY(mTraceLock) = kDefaultBufferCapInByte;
- bool mEnabled GUARDED_BY(mTraceLock) = false;
- bool mWriteToFile GUARDED_BY(mTraceLock) = false;
+ private:
+ std::mutex& mSfLock;
+ std::condition_variable mCanStartTrace;
+ std::thread mThread;
+ const char* mWhere = "";
+ bool mWriteToFile = false;
+ bool mEnabled = false;
+ bool mAddEntry = false;
+ void loop();
+ bool traceWhenNotified(LayersTraceProto* outProto);
+ };
};
} // namespace android
diff --git a/services/surfaceflinger/TimeStats/Android.bp b/services/surfaceflinger/TimeStats/Android.bp
index d6d0cb8..bcc3e4e 100644
--- a/services/surfaceflinger/TimeStats/Android.bp
+++ b/services/surfaceflinger/TimeStats/Android.bp
@@ -7,7 +7,7 @@
default_applicable_licenses: ["frameworks_native_license"],
}
-cc_library_shared {
+cc_library {
name: "libtimestats",
srcs: [
"TimeStats.cpp",
@@ -18,20 +18,13 @@
"libcutils",
"liblog",
"libprotobuf-cpp-lite",
- "libprotoutil",
- "libstatslog",
- "libstatspull",
- "libstatssocket",
+ "libtimestats_atoms_proto",
"libtimestats_proto",
"libui",
"libutils",
],
export_include_dirs: ["."],
export_shared_lib_headers: [
- "libprotoutil",
- "libstatslog",
- "libstatspull",
- "libstatssocket",
"libtimestats_proto",
],
cppflags: [
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 37194c6..3d82afa 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -14,18 +14,14 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
+#include <unordered_map>
#undef LOG_TAG
#define LOG_TAG "TimeStats"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include "TimeStats.h"
-
#include <android-base/stringprintf.h>
-#include <android/util/ProtoOutputStream.h>
#include <log/log.h>
+#include <timestatsatomsproto/TimeStatsAtomsProtoHeader.h>
#include <utils/String8.h>
#include <utils/Timers.h>
#include <utils/Trace.h>
@@ -33,100 +29,116 @@
#include <algorithm>
#include <chrono>
+#include "TimeStats.h"
+#include "timestatsproto/TimeStatsHelper.h"
+
namespace android {
namespace impl {
-AStatsManager_PullAtomCallbackReturn TimeStats::pullAtomCallback(int32_t atom_tag,
- AStatsEventList* data,
- void* cookie) {
- impl::TimeStats* timeStats = reinterpret_cast<impl::TimeStats*>(cookie);
- AStatsManager_PullAtomCallbackReturn result = AStatsManager_PULL_SKIP;
- if (atom_tag == android::util::SURFACEFLINGER_STATS_GLOBAL_INFO) {
- result = timeStats->populateGlobalAtom(data);
- } else if (atom_tag == android::util::SURFACEFLINGER_STATS_LAYER_INFO) {
- result = timeStats->populateLayerAtom(data);
- }
-
- // Enable timestats now. The first full pull for a given build is expected to
- // have empty or very little stats, as stats are first enabled after the
- // first pull is completed for either the global or layer stats.
- timeStats->enable();
- return result;
-}
-
namespace {
-// Histograms align with the order of fields in SurfaceflingerStatsLayerInfo.
-const std::array<std::string, 6> kHistogramNames = {
- "present2present", "post2present", "acquire2present",
- "latch2present", "desired2present", "post2acquire",
-};
-std::string histogramToProtoByteString(const std::unordered_map<int32_t, int32_t>& histogram,
- size_t maxPulledHistogramBuckets) {
+FrameTimingHistogram histogramToProto(const std::unordered_map<int32_t, int32_t>& histogram,
+ size_t maxPulledHistogramBuckets) {
auto buckets = std::vector<std::pair<int32_t, int32_t>>(histogram.begin(), histogram.end());
std::sort(buckets.begin(), buckets.end(),
[](std::pair<int32_t, int32_t>& left, std::pair<int32_t, int32_t>& right) {
return left.second > right.second;
});
- util::ProtoOutputStream proto;
+ FrameTimingHistogram histogramProto;
int histogramSize = 0;
for (const auto& bucket : buckets) {
if (++histogramSize > maxPulledHistogramBuckets) {
break;
}
- proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED |
- 1 /* field id */,
- (int32_t)bucket.first);
- proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
- 2 /* field id */,
- (int64_t)bucket.second);
+ histogramProto.add_time_millis_buckets((int32_t)bucket.first);
+ histogramProto.add_frame_counts((int64_t)bucket.second);
}
+ return histogramProto;
+}
- std::string byteString;
- proto.serializeToString(&byteString);
- return byteString;
+SurfaceflingerStatsLayerInfo_SetFrameRateVote frameRateVoteToProto(
+ const TimeStats::SetFrameRateVote& setFrameRateVote) {
+ using FrameRateCompatibilityEnum =
+ SurfaceflingerStatsLayerInfo::SetFrameRateVote::FrameRateCompatibility;
+ using SeamlessnessEnum = SurfaceflingerStatsLayerInfo::SetFrameRateVote::Seamlessness;
+
+ SurfaceflingerStatsLayerInfo_SetFrameRateVote proto;
+ proto.set_frame_rate(setFrameRateVote.frameRate);
+ proto.set_frame_rate_compatibility(
+ static_cast<FrameRateCompatibilityEnum>(setFrameRateVote.frameRateCompatibility));
+ proto.set_seamlessness(static_cast<SeamlessnessEnum>(setFrameRateVote.seamlessness));
+ return proto;
}
} // namespace
-AStatsManager_PullAtomCallbackReturn TimeStats::populateGlobalAtom(AStatsEventList* data) {
+bool TimeStats::populateGlobalAtom(std::string* pulledData) {
std::lock_guard<std::mutex> lock(mMutex);
- if (mTimeStats.statsStart == 0) {
- return AStatsManager_PULL_SKIP;
+ if (mTimeStats.statsStartLegacy == 0) {
+ return false;
}
flushPowerTimeLocked();
+ SurfaceflingerStatsGlobalInfoWrapper atomList;
+ for (const auto& globalSlice : mTimeStats.stats) {
+ SurfaceflingerStatsGlobalInfo* atom = atomList.add_atom();
+ atom->set_total_frames(mTimeStats.totalFramesLegacy);
+ atom->set_missed_frames(mTimeStats.missedFramesLegacy);
+ atom->set_client_composition_frames(mTimeStats.clientCompositionFramesLegacy);
+ atom->set_display_on_millis(mTimeStats.displayOnTimeLegacy);
+ atom->set_animation_millis(mTimeStats.presentToPresentLegacy.totalTime());
+ atom->set_event_connection_count(mTimeStats.displayEventConnectionsCountLegacy);
+ *atom->mutable_frame_duration() =
+ histogramToProto(mTimeStats.frameDurationLegacy.hist, mMaxPulledHistogramBuckets);
+ *atom->mutable_render_engine_timing() =
+ histogramToProto(mTimeStats.renderEngineTimingLegacy.hist,
+ mMaxPulledHistogramBuckets);
+ atom->set_total_timeline_frames(globalSlice.second.jankPayload.totalFrames);
+ atom->set_total_janky_frames(globalSlice.second.jankPayload.totalJankyFrames);
+ atom->set_total_janky_frames_with_long_cpu(globalSlice.second.jankPayload.totalSFLongCpu);
+ atom->set_total_janky_frames_with_long_gpu(globalSlice.second.jankPayload.totalSFLongGpu);
+ atom->set_total_janky_frames_sf_unattributed(
+ globalSlice.second.jankPayload.totalSFUnattributed);
+ atom->set_total_janky_frames_app_unattributed(
+ globalSlice.second.jankPayload.totalAppUnattributed);
+ atom->set_total_janky_frames_sf_scheduling(
+ globalSlice.second.jankPayload.totalSFScheduling);
+ atom->set_total_jank_frames_sf_prediction_error(
+ globalSlice.second.jankPayload.totalSFPredictionError);
+ atom->set_total_jank_frames_app_buffer_stuffing(
+ globalSlice.second.jankPayload.totalAppBufferStuffing);
+ atom->set_display_refresh_rate_bucket(globalSlice.first.displayRefreshRateBucket);
+ *atom->mutable_sf_deadline_misses() =
+ histogramToProto(globalSlice.second.displayDeadlineDeltas.hist,
+ mMaxPulledHistogramBuckets);
+ *atom->mutable_sf_prediction_errors() =
+ histogramToProto(globalSlice.second.displayPresentDeltas.hist,
+ mMaxPulledHistogramBuckets);
+ atom->set_render_rate_bucket(globalSlice.first.renderRateBucket);
+ }
- AStatsEvent* event = mStatsDelegate->addStatsEventToPullData(data);
- mStatsDelegate->statsEventSetAtomId(event, android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
- mStatsDelegate->statsEventWriteInt64(event, mTimeStats.totalFrames);
- mStatsDelegate->statsEventWriteInt64(event, mTimeStats.missedFrames);
- mStatsDelegate->statsEventWriteInt64(event, mTimeStats.clientCompositionFrames);
- mStatsDelegate->statsEventWriteInt64(event, mTimeStats.displayOnTime);
- mStatsDelegate->statsEventWriteInt64(event, mTimeStats.presentToPresent.totalTime());
- mStatsDelegate->statsEventWriteInt32(event, mTimeStats.displayEventConnectionsCount);
- std::string frameDurationBytes =
- histogramToProtoByteString(mTimeStats.frameDuration.hist, mMaxPulledHistogramBuckets);
- mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)frameDurationBytes.c_str(),
- frameDurationBytes.size());
- std::string renderEngineTimingBytes =
- histogramToProtoByteString(mTimeStats.renderEngineTiming.hist,
- mMaxPulledHistogramBuckets);
- mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)renderEngineTimingBytes.c_str(),
- renderEngineTimingBytes.size());
- mStatsDelegate->statsEventBuild(event);
+ // Always clear data.
clearGlobalLocked();
- return AStatsManager_PULL_SUCCESS;
+ return atomList.SerializeToString(pulledData);
}
-AStatsManager_PullAtomCallbackReturn TimeStats::populateLayerAtom(AStatsEventList* data) {
+bool TimeStats::populateLayerAtom(std::string* pulledData) {
std::lock_guard<std::mutex> lock(mMutex);
- std::vector<TimeStatsHelper::TimeStatsLayer const*> dumpStats;
- for (const auto& ele : mTimeStats.stats) {
- dumpStats.push_back(&ele.second);
+ std::vector<TimeStatsHelper::TimeStatsLayer*> dumpStats;
+ uint32_t numLayers = 0;
+ for (const auto& globalSlice : mTimeStats.stats) {
+ numLayers += globalSlice.second.stats.size();
+ }
+
+ dumpStats.reserve(numLayers);
+
+ for (auto& globalSlice : mTimeStats.stats) {
+ for (auto& layerSlice : globalSlice.second.stats) {
+ dumpStats.push_back(&layerSlice.second);
+ }
}
std::sort(dumpStats.begin(), dumpStats.end(),
@@ -139,44 +151,73 @@
dumpStats.resize(mMaxPulledLayers);
}
- for (const auto& layer : dumpStats) {
- AStatsEvent* event = mStatsDelegate->addStatsEventToPullData(data);
- mStatsDelegate->statsEventSetAtomId(event, android::util::SURFACEFLINGER_STATS_LAYER_INFO);
- mStatsDelegate->statsEventWriteString8(event, layer->layerName.c_str());
- mStatsDelegate->statsEventWriteInt64(event, layer->totalFrames);
- mStatsDelegate->statsEventWriteInt64(event, layer->droppedFrames);
-
- for (const auto& name : kHistogramNames) {
- const auto& histogram = layer->deltas.find(name);
- if (histogram == layer->deltas.cend()) {
- mStatsDelegate->statsEventWriteByteArray(event, nullptr, 0);
- } else {
- std::string bytes = histogramToProtoByteString(histogram->second.hist,
- mMaxPulledHistogramBuckets);
- mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)bytes.c_str(),
- bytes.size());
- }
+ SurfaceflingerStatsLayerInfoWrapper atomList;
+ for (auto& layer : dumpStats) {
+ SurfaceflingerStatsLayerInfo* atom = atomList.add_atom();
+ atom->set_layer_name(layer->layerName);
+ atom->set_total_frames(layer->totalFrames);
+ atom->set_dropped_frames(layer->droppedFrames);
+ const auto& present2PresentHist = layer->deltas.find("present2present");
+ if (present2PresentHist != layer->deltas.cend()) {
+ *atom->mutable_present_to_present() =
+ histogramToProto(present2PresentHist->second.hist, mMaxPulledHistogramBuckets);
+ }
+ const auto& post2presentHist = layer->deltas.find("post2present");
+ if (post2presentHist != layer->deltas.cend()) {
+ *atom->mutable_post_to_present() =
+ histogramToProto(post2presentHist->second.hist, mMaxPulledHistogramBuckets);
+ }
+ const auto& acquire2presentHist = layer->deltas.find("acquire2present");
+ if (acquire2presentHist != layer->deltas.cend()) {
+ *atom->mutable_acquire_to_present() =
+ histogramToProto(acquire2presentHist->second.hist, mMaxPulledHistogramBuckets);
+ }
+ const auto& latch2presentHist = layer->deltas.find("latch2present");
+ if (latch2presentHist != layer->deltas.cend()) {
+ *atom->mutable_latch_to_present() =
+ histogramToProto(latch2presentHist->second.hist, mMaxPulledHistogramBuckets);
+ }
+ const auto& desired2presentHist = layer->deltas.find("desired2present");
+ if (desired2presentHist != layer->deltas.cend()) {
+ *atom->mutable_desired_to_present() =
+ histogramToProto(desired2presentHist->second.hist, mMaxPulledHistogramBuckets);
+ }
+ const auto& post2acquireHist = layer->deltas.find("post2acquire");
+ if (post2acquireHist != layer->deltas.cend()) {
+ *atom->mutable_post_to_acquire() =
+ histogramToProto(post2acquireHist->second.hist, mMaxPulledHistogramBuckets);
}
- mStatsDelegate->statsEventWriteInt64(event, layer->lateAcquireFrames);
- mStatsDelegate->statsEventWriteInt64(event, layer->badDesiredPresentFrames);
-
- mStatsDelegate->statsEventBuild(event);
+ atom->set_late_acquire_frames(layer->lateAcquireFrames);
+ atom->set_bad_desired_present_frames(layer->badDesiredPresentFrames);
+ atom->set_uid(layer->uid);
+ atom->set_total_timeline_frames(layer->jankPayload.totalFrames);
+ atom->set_total_janky_frames(layer->jankPayload.totalJankyFrames);
+ atom->set_total_janky_frames_with_long_cpu(layer->jankPayload.totalSFLongCpu);
+ atom->set_total_janky_frames_with_long_gpu(layer->jankPayload.totalSFLongGpu);
+ atom->set_total_janky_frames_sf_unattributed(layer->jankPayload.totalSFUnattributed);
+ atom->set_total_janky_frames_app_unattributed(layer->jankPayload.totalAppUnattributed);
+ atom->set_total_janky_frames_sf_scheduling(layer->jankPayload.totalSFScheduling);
+ atom->set_total_jank_frames_sf_prediction_error(layer->jankPayload.totalSFPredictionError);
+ atom->set_total_jank_frames_app_buffer_stuffing(layer->jankPayload.totalAppBufferStuffing);
+ atom->set_display_refresh_rate_bucket(layer->displayRefreshRateBucket);
+ atom->set_render_rate_bucket(layer->renderRateBucket);
+ *atom->mutable_set_frame_rate_vote() = frameRateVoteToProto(layer->setFrameRateVote);
+ *atom->mutable_app_deadline_misses() =
+ histogramToProto(layer->deltas["appDeadlineDeltas"].hist,
+ mMaxPulledHistogramBuckets);
}
+
+ // Always clear data.
clearLayersLocked();
- return AStatsManager_PULL_SUCCESS;
+ return atomList.SerializeToString(pulledData);
}
-TimeStats::TimeStats() : TimeStats(nullptr, std::nullopt, std::nullopt) {}
+TimeStats::TimeStats() : TimeStats(std::nullopt, std::nullopt) {}
-TimeStats::TimeStats(std::unique_ptr<StatsEventDelegate> statsDelegate,
- std::optional<size_t> maxPulledLayers,
+TimeStats::TimeStats(std::optional<size_t> maxPulledLayers,
std::optional<size_t> maxPulledHistogramBuckets) {
- if (statsDelegate != nullptr) {
- mStatsDelegate = std::move(statsDelegate);
- }
-
if (maxPulledLayers) {
mMaxPulledLayers = *maxPulledLayers;
}
@@ -186,18 +227,19 @@
}
}
-TimeStats::~TimeStats() {
- std::lock_guard<std::mutex> lock(mMutex);
- mStatsDelegate->clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
- mStatsDelegate->clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO);
-}
+bool TimeStats::onPullAtom(const int atomId, std::string* pulledData) {
+ bool success = false;
+ if (atomId == 10062) { // SURFACEFLINGER_STATS_GLOBAL_INFO
+ success = populateGlobalAtom(pulledData);
+ } else if (atomId == 10063) { // SURFACEFLINGER_STATS_LAYER_INFO
+ success = populateLayerAtom(pulledData);
+ }
-void TimeStats::onBootFinished() {
- std::lock_guard<std::mutex> lock(mMutex);
- mStatsDelegate->setStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- nullptr, TimeStats::pullAtomCallback, this);
- mStatsDelegate->setStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
- nullptr, TimeStats::pullAtomCallback, this);
+ // Enable timestats now. The first full pull for a given build is expected to
+ // have empty or very little stats, as stats are first enabled after the
+ // first pull is completed for either the global or layer stats.
+ enable();
+ return success;
}
void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, std::string& result) {
@@ -251,7 +293,7 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- mTimeStats.totalFrames++;
+ mTimeStats.totalFramesLegacy++;
}
void TimeStats::incrementMissedFrames() {
@@ -260,7 +302,7 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- mTimeStats.missedFrames++;
+ mTimeStats.missedFramesLegacy++;
}
void TimeStats::incrementClientCompositionFrames() {
@@ -269,7 +311,7 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- mTimeStats.clientCompositionFrames++;
+ mTimeStats.clientCompositionFramesLegacy++;
}
void TimeStats::incrementClientCompositionReusedFrames() {
@@ -278,7 +320,7 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- mTimeStats.clientCompositionReusedFrames++;
+ mTimeStats.clientCompositionReusedFramesLegacy++;
}
void TimeStats::incrementRefreshRateSwitches() {
@@ -287,7 +329,7 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- mTimeStats.refreshRateSwitches++;
+ mTimeStats.refreshRateSwitchesLegacy++;
}
void TimeStats::incrementCompositionStrategyChanges() {
@@ -296,7 +338,7 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- mTimeStats.compositionStrategyChanges++;
+ mTimeStats.compositionStrategyChangesLegacy++;
}
void TimeStats::recordDisplayEventConnectionCount(int32_t count) {
@@ -305,8 +347,8 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- mTimeStats.displayEventConnectionsCount =
- std::max(mTimeStats.displayEventConnectionsCount, count);
+ mTimeStats.displayEventConnectionsCountLegacy =
+ std::max(mTimeStats.displayEventConnectionsCountLegacy, count);
}
static int32_t msBetween(nsecs_t start, nsecs_t end) {
@@ -322,7 +364,7 @@
std::lock_guard<std::mutex> lock(mMutex);
if (mPowerTime.powerMode == PowerMode::ON) {
- mTimeStats.frameDuration.insert(msBetween(startTime, endTime));
+ mTimeStats.frameDurationLegacy.insert(msBetween(startTime, endTime));
}
}
@@ -385,23 +427,50 @@
return true;
}
-void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId) {
+static int32_t clampToSmallestBucket(Fps fps, size_t bucketWidth) {
+ return (fps.getIntValue() / bucketWidth) * bucketWidth;
+}
+
+void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
+ std::optional<Fps> renderRate,
+ SetFrameRateVote frameRateVote) {
ATRACE_CALL();
+ ALOGV("[%d]-flushAvailableRecordsToStatsLocked", layerId);
LayerRecord& layerRecord = mTimeStatsTracker[layerId];
TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord;
std::deque<TimeRecord>& timeRecords = layerRecord.timeRecords;
+ const int32_t refreshRateBucket =
+ clampToSmallestBucket(displayRefreshRate, REFRESH_RATE_BUCKET_WIDTH);
+ const int32_t renderRateBucket =
+ clampToSmallestBucket(renderRate ? *renderRate : displayRefreshRate,
+ RENDER_RATE_BUCKET_WIDTH);
while (!timeRecords.empty()) {
if (!recordReadyLocked(layerId, &timeRecords[0])) break;
ALOGV("[%d]-[%" PRIu64 "]-presentFenceTime[%" PRId64 "]", layerId,
timeRecords[0].frameTime.frameNumber, timeRecords[0].frameTime.presentTime);
if (prevTimeRecord.ready) {
+ uid_t uid = layerRecord.uid;
const std::string& layerName = layerRecord.layerName;
- if (!mTimeStats.stats.count(layerName)) {
- mTimeStats.stats[layerName].layerName = layerName;
+ TimeStatsHelper::TimelineStatsKey timelineKey = {refreshRateBucket, renderRateBucket};
+ if (!mTimeStats.stats.count(timelineKey)) {
+ mTimeStats.stats[timelineKey].key = timelineKey;
}
- TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[layerName];
+
+ TimeStatsHelper::TimelineStats& displayStats = mTimeStats.stats[timelineKey];
+
+ TimeStatsHelper::LayerStatsKey layerKey = {uid, layerName};
+ if (!displayStats.stats.count(layerKey)) {
+ displayStats.stats[layerKey].displayRefreshRateBucket = refreshRateBucket;
+ displayStats.stats[layerKey].renderRateBucket = renderRateBucket;
+ displayStats.stats[layerKey].uid = uid;
+ displayStats.stats[layerKey].layerName = layerName;
+ }
+ if (frameRateVote.frameRate > 0.0f) {
+ displayStats.stats[layerKey].setFrameRateVote = frameRateVote;
+ }
+ TimeStatsHelper::TimeStatsLayer& timeStatsLayer = displayStats.stats[layerKey];
timeStatsLayer.totalFrames++;
timeStatsLayer.droppedFrames += layerRecord.droppedFrames;
timeStatsLayer.lateAcquireFrames += layerRecord.lateAcquireFrames;
@@ -462,8 +531,21 @@
layerName.compare(0, kMinLenLayerName, kPopupWindowPrefix) != 0;
}
+bool TimeStats::canAddNewAggregatedStats(uid_t uid, const std::string& layerName) {
+ uint32_t layerRecords = 0;
+ for (const auto& record : mTimeStats.stats) {
+ if (record.second.stats.count({uid, layerName}) > 0) {
+ return true;
+ }
+
+ layerRecords += record.second.stats.size();
+ }
+
+ return mTimeStats.stats.size() < MAX_NUM_LAYER_STATS;
+}
+
void TimeStats::setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
- nsecs_t postTime) {
+ uid_t uid, nsecs_t postTime) {
if (!mEnabled.load()) return;
ATRACE_CALL();
@@ -471,11 +553,12 @@
postTime);
std::lock_guard<std::mutex> lock(mMutex);
- if (!mTimeStats.stats.count(layerName) && mTimeStats.stats.size() >= MAX_NUM_LAYER_STATS) {
+ if (!canAddNewAggregatedStats(uid, layerName)) {
return;
}
if (!mTimeStatsTracker.count(layerId) && mTimeStatsTracker.size() < MAX_NUM_LAYER_RECORDS &&
layerNameIsValid(layerName)) {
+ mTimeStatsTracker[layerId].uid = uid;
mTimeStatsTracker[layerId].layerName = layerName;
}
if (!mTimeStatsTracker.count(layerId)) return;
@@ -609,7 +692,9 @@
}
}
-void TimeStats::setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) {
+void TimeStats::setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
+ Fps displayRefreshRate, std::optional<Fps> renderRate,
+ SetFrameRateVote frameRateVote) {
if (!mEnabled.load()) return;
ATRACE_CALL();
@@ -628,11 +713,13 @@
layerRecord.waitData++;
}
- flushAvailableRecordsToStatsLocked(layerId);
+ flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate, frameRateVote);
}
void TimeStats::setPresentFence(int32_t layerId, uint64_t frameNumber,
- const std::shared_ptr<FenceTime>& presentFence) {
+ const std::shared_ptr<FenceTime>& presentFence,
+ Fps displayRefreshRate, std::optional<Fps> renderRate,
+ SetFrameRateVote frameRateVote) {
if (!mEnabled.load()) return;
ATRACE_CALL();
@@ -652,7 +739,101 @@
layerRecord.waitData++;
}
- flushAvailableRecordsToStatsLocked(layerId);
+ flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate, frameRateVote);
+}
+
+static const constexpr int32_t kValidJankyReason = JankType::DisplayHAL |
+ JankType::SurfaceFlingerCpuDeadlineMissed | JankType::SurfaceFlingerGpuDeadlineMissed |
+ JankType::AppDeadlineMissed | JankType::PredictionError |
+ JankType::SurfaceFlingerScheduling;
+
+template <class T>
+static void updateJankPayload(T& t, int32_t reasons) {
+ t.jankPayload.totalFrames++;
+
+ if (reasons & kValidJankyReason) {
+ t.jankPayload.totalJankyFrames++;
+ if ((reasons & JankType::SurfaceFlingerCpuDeadlineMissed) != 0) {
+ t.jankPayload.totalSFLongCpu++;
+ }
+ if ((reasons & JankType::SurfaceFlingerGpuDeadlineMissed) != 0) {
+ t.jankPayload.totalSFLongGpu++;
+ }
+ if ((reasons & JankType::DisplayHAL) != 0) {
+ t.jankPayload.totalSFUnattributed++;
+ }
+ if ((reasons & JankType::AppDeadlineMissed) != 0) {
+ t.jankPayload.totalAppUnattributed++;
+ }
+ if ((reasons & JankType::PredictionError) != 0) {
+ t.jankPayload.totalSFPredictionError++;
+ }
+ if ((reasons & JankType::SurfaceFlingerScheduling) != 0) {
+ t.jankPayload.totalSFScheduling++;
+ }
+ }
+
+ // We want to track BufferStuffing separately as it can provide info on latency issues
+ if (reasons & JankType::BufferStuffing) {
+ t.jankPayload.totalAppBufferStuffing++;
+ }
+}
+
+void TimeStats::incrementJankyFrames(const JankyFramesInfo& info) {
+ if (!mEnabled.load()) return;
+
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ // Only update layer stats if we're already tracking the layer in TimeStats.
+ // Otherwise, continue tracking the statistic but use a default layer name instead.
+ // As an implementation detail, we do this because this method is expected to be
+ // called from FrameTimeline, whose jank classification includes transaction jank
+ // that occurs without a buffer. But, in general those layer names are not suitable as
+ // aggregation keys: e.g., it's normal and expected for Window Manager to include the hash code
+ // for an animation leash. So while we can show that jank in dumpsys, aggregating based on the
+ // layer blows up the stats size, so as a workaround drop those stats. This assumes that
+ // TimeStats will flush the first present fence for a layer *before* FrameTimeline does so that
+ // the first jank record is not dropped.
+
+ static const std::string kDefaultLayerName = "none";
+
+ const int32_t refreshRateBucket =
+ clampToSmallestBucket(info.refreshRate, REFRESH_RATE_BUCKET_WIDTH);
+ const int32_t renderRateBucket =
+ clampToSmallestBucket(info.renderRate ? *info.renderRate : info.refreshRate,
+ RENDER_RATE_BUCKET_WIDTH);
+ const TimeStatsHelper::TimelineStatsKey timelineKey = {refreshRateBucket, renderRateBucket};
+
+ if (!mTimeStats.stats.count(timelineKey)) {
+ mTimeStats.stats[timelineKey].key = timelineKey;
+ }
+
+ TimeStatsHelper::TimelineStats& timelineStats = mTimeStats.stats[timelineKey];
+
+ updateJankPayload<TimeStatsHelper::TimelineStats>(timelineStats, info.reasons);
+
+ TimeStatsHelper::LayerStatsKey layerKey = {info.uid, info.layerName};
+ if (!timelineStats.stats.count(layerKey)) {
+ layerKey = {info.uid, kDefaultLayerName};
+ timelineStats.stats[layerKey].displayRefreshRateBucket = refreshRateBucket;
+ timelineStats.stats[layerKey].renderRateBucket = renderRateBucket;
+ timelineStats.stats[layerKey].uid = info.uid;
+ timelineStats.stats[layerKey].layerName = kDefaultLayerName;
+ }
+
+ TimeStatsHelper::TimeStatsLayer& timeStatsLayer = timelineStats.stats[layerKey];
+ updateJankPayload<TimeStatsHelper::TimeStatsLayer>(timeStatsLayer, info.reasons);
+
+ if (info.reasons & kValidJankyReason) {
+ // TimeStats Histograms only retain positive values, so we don't need to check if these
+ // deadlines were really missed if we know that the frame had jank, since deadlines
+ // that were met will be dropped.
+ timelineStats.displayDeadlineDeltas.insert(static_cast<int32_t>(info.displayDeadlineDelta));
+ timelineStats.displayPresentDeltas.insert(static_cast<int32_t>(info.displayPresentJitter));
+ timeStatsLayer.deltas["appDeadlineDeltas"].insert(
+ static_cast<int32_t>(info.appDeadlineDelta));
+ }
}
void TimeStats::onDestroy(int32_t layerId) {
@@ -693,7 +874,7 @@
switch (mPowerTime.powerMode) {
case PowerMode::ON:
- mTimeStats.displayOnTime += elapsedTime;
+ mTimeStats.displayOnTimeLegacy += elapsedTime;
break;
case PowerMode::OFF:
case PowerMode::DOZE:
@@ -722,10 +903,10 @@
void TimeStats::recordRefreshRate(uint32_t fps, nsecs_t duration) {
std::lock_guard<std::mutex> lock(mMutex);
- if (mTimeStats.refreshRateStats.count(fps)) {
- mTimeStats.refreshRateStats[fps] += duration;
+ if (mTimeStats.refreshRateStatsLegacy.count(fps)) {
+ mTimeStats.refreshRateStatsLegacy[fps] += duration;
} else {
- mTimeStats.refreshRateStats.insert({fps, duration});
+ mTimeStats.refreshRateStatsLegacy.insert({fps, duration});
}
}
@@ -751,7 +932,7 @@
msBetween(mGlobalRecord.prevPresentTime, curPresentTime);
ALOGV("Global present2present[%d] prev[%" PRId64 "] curr[%" PRId64 "]",
presentToPresentMs, mGlobalRecord.prevPresentTime, curPresentTime);
- mTimeStats.presentToPresent.insert(presentToPresentMs);
+ mTimeStats.presentToPresentLegacy.insert(presentToPresentMs);
}
mGlobalRecord.prevPresentTime = curPresentTime;
@@ -778,7 +959,7 @@
}
const int32_t renderEngineMs = msBetween(duration.startTime, endNs);
- mTimeStats.renderEngineTiming.insert(renderEngineMs);
+ mTimeStats.renderEngineTimingLegacy.insert(renderEngineMs);
mGlobalRecord.renderEngineDurations.pop_front();
}
@@ -821,7 +1002,7 @@
std::lock_guard<std::mutex> lock(mMutex);
mEnabled.store(true);
- mTimeStats.statsStart = static_cast<int64_t>(std::time(0));
+ mTimeStats.statsStartLegacy = static_cast<int64_t>(std::time(0));
mPowerTime.prevTime = systemTime();
ALOGD("Enabled");
}
@@ -834,7 +1015,7 @@
std::lock_guard<std::mutex> lock(mMutex);
flushPowerTimeLocked();
mEnabled.store(false);
- mTimeStats.statsEnd = static_cast<int64_t>(std::time(0));
+ mTimeStats.statsEndLegacy = static_cast<int64_t>(std::time(0));
ALOGD("Disabled");
}
@@ -847,21 +1028,24 @@
void TimeStats::clearGlobalLocked() {
ATRACE_CALL();
- mTimeStats.statsStart = (mEnabled.load() ? static_cast<int64_t>(std::time(0)) : 0);
- mTimeStats.statsEnd = 0;
- mTimeStats.totalFrames = 0;
- mTimeStats.missedFrames = 0;
- mTimeStats.clientCompositionFrames = 0;
- mTimeStats.clientCompositionReusedFrames = 0;
- mTimeStats.refreshRateSwitches = 0;
- mTimeStats.compositionStrategyChanges = 0;
- mTimeStats.displayEventConnectionsCount = 0;
- mTimeStats.displayOnTime = 0;
- mTimeStats.presentToPresent.hist.clear();
- mTimeStats.frameDuration.hist.clear();
- mTimeStats.renderEngineTiming.hist.clear();
- mTimeStats.refreshRateStats.clear();
+ mTimeStats.statsStartLegacy = (mEnabled.load() ? static_cast<int64_t>(std::time(0)) : 0);
+ mTimeStats.statsEndLegacy = 0;
+ mTimeStats.totalFramesLegacy = 0;
+ mTimeStats.missedFramesLegacy = 0;
+ mTimeStats.clientCompositionFramesLegacy = 0;
+ mTimeStats.clientCompositionReusedFramesLegacy = 0;
+ mTimeStats.refreshRateSwitchesLegacy = 0;
+ mTimeStats.compositionStrategyChangesLegacy = 0;
+ mTimeStats.displayEventConnectionsCountLegacy = 0;
+ mTimeStats.displayOnTimeLegacy = 0;
+ mTimeStats.presentToPresentLegacy.hist.clear();
+ mTimeStats.frameDurationLegacy.hist.clear();
+ mTimeStats.renderEngineTimingLegacy.hist.clear();
+ mTimeStats.refreshRateStatsLegacy.clear();
mPowerTime.prevTime = systemTime();
+ for (auto& globalRecord : mTimeStats.stats) {
+ globalRecord.second.clearGlobals();
+ }
mGlobalRecord.prevPresentTime = 0;
mGlobalRecord.presentFences.clear();
ALOGD("Cleared global stats");
@@ -871,7 +1055,10 @@
ATRACE_CALL();
mTimeStatsTracker.clear();
- mTimeStats.stats.clear();
+
+ for (auto& globalRecord : mTimeStats.stats) {
+ globalRecord.second.stats.clear();
+ }
ALOGD("Cleared layer stats");
}
@@ -883,11 +1070,11 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- if (mTimeStats.statsStart == 0) {
+ if (mTimeStats.statsStartLegacy == 0) {
return;
}
- mTimeStats.statsEnd = static_cast<int64_t>(std::time(0));
+ mTimeStats.statsEndLegacy = static_cast<int64_t>(std::time(0));
flushPowerTimeLocked();
@@ -905,6 +1092,3 @@
} // namespace impl
} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 8de5d0c..5b0f5bd 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -17,17 +17,18 @@
#pragma once
// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#include <cstdint>
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
-#include <stats_event.h>
-#include <stats_pull_atom_callback.h>
-#include <statslog.h>
+#include <../Fps.h>
+#include <gui/JankInfo.h>
#include <timestatsproto/TimeStatsHelper.h>
#include <timestatsproto/TimeStatsProtoHeader.h>
#include <ui/FenceTime.h>
@@ -46,11 +47,12 @@
class TimeStats {
public:
+ using SetFrameRateVote = TimeStatsHelper::SetFrameRateVote;
+
virtual ~TimeStats() = default;
- // Called once boot has been finished to perform additional capabilities,
- // e.g. registration to statsd.
- virtual void onBootFinished() = 0;
+ // Process a pull request from statsd.
+ virtual bool onPullAtom(const int atomId, std::string* pulledData);
virtual void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) = 0;
virtual bool isEnabled() = 0;
@@ -85,7 +87,7 @@
const std::shared_ptr<FenceTime>& readyFence) = 0;
virtual void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
- nsecs_t postTime) = 0;
+ uid_t uid, nsecs_t postTime) = 0;
virtual void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) = 0;
// Reasons why latching a particular buffer may be skipped
enum class LatchSkipReason {
@@ -105,9 +107,62 @@
const std::shared_ptr<FenceTime>& acquireFence) = 0;
// SetPresent{Time, Fence} are not expected to be called in the critical
// rendering path, as they flush prior fences if those fences have fired.
- virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) = 0;
+ virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
+ Fps displayRefreshRate, std::optional<Fps> renderRate,
+ SetFrameRateVote frameRateVote) = 0;
virtual void setPresentFence(int32_t layerId, uint64_t frameNumber,
- const std::shared_ptr<FenceTime>& presentFence) = 0;
+ const std::shared_ptr<FenceTime>& presentFence,
+ Fps displayRefreshRate, std::optional<Fps> renderRate,
+ SetFrameRateVote frameRateVote) = 0;
+
+ // Increments janky frames, blamed to the provided {refreshRate, renderRate, uid, layerName}
+ // key, with JankMetadata as supplementary reasons for the jank. Because FrameTimeline is the
+ // infrastructure responsible for computing jank in the system, this is expected to be called
+ // from FrameTimeline, rather than directly from SurfaceFlinger or individual layers. If there
+ // are no jank reasons, then total frames are incremented but jank is not, for accurate
+ // accounting of janky frames.
+ // displayDeadlineDelta, displayPresentJitter, and appDeadlineDelta are also provided in order
+ // to provide contextual information about a janky frame. These values may only be uploaded if
+ // there was an associated valid jank reason, and they must be positive. When these frame counts
+ // are incremented, these are also aggregated into a global reporting packet to help with data
+ // validation and assessing of overall device health.
+ struct JankyFramesInfo {
+ Fps refreshRate;
+ std::optional<Fps> renderRate;
+ uid_t uid = 0;
+ std::string layerName;
+ int32_t reasons = 0;
+ nsecs_t displayDeadlineDelta = 0;
+ nsecs_t displayPresentJitter = 0;
+ nsecs_t appDeadlineDelta = 0;
+
+ bool operator==(const JankyFramesInfo& o) const {
+ return Fps::EqualsInBuckets{}(refreshRate, o.refreshRate) &&
+ ((renderRate == std::nullopt && o.renderRate == std::nullopt) ||
+ (renderRate != std::nullopt && o.renderRate != std::nullopt &&
+ Fps::EqualsInBuckets{}(*renderRate, *o.renderRate))) &&
+ uid == o.uid && layerName == o.layerName && reasons == o.reasons &&
+ displayDeadlineDelta == o.displayDeadlineDelta &&
+ displayPresentJitter == o.displayPresentJitter &&
+ appDeadlineDelta == o.appDeadlineDelta;
+ }
+
+ friend std::ostream& operator<<(std::ostream& os, const JankyFramesInfo& info) {
+ os << "JankyFramesInfo {";
+ os << "\n .refreshRate = " << info.refreshRate;
+ os << "\n .renderRate = "
+ << (info.renderRate ? to_string(*info.renderRate) : "nullopt");
+ os << "\n .uid = " << info.uid;
+ os << "\n .layerName = " << info.layerName;
+ os << "\n .reasons = " << info.reasons;
+ os << "\n .displayDeadlineDelta = " << info.displayDeadlineDelta;
+ os << "\n .displayPresentJitter = " << info.displayPresentJitter;
+ os << "\n .appDeadlineDelta = " << info.appDeadlineDelta;
+ return os << "\n}";
+ }
+ };
+
+ virtual void incrementJankyFrames(const JankyFramesInfo& info) = 0;
// Clean up the layer record
virtual void onDestroy(int32_t layerId) = 0;
// If SF skips or rejects a buffer, remove the corresponding TimeRecord.
@@ -142,6 +197,7 @@
};
struct LayerRecord {
+ uid_t uid;
std::string layerName;
// This is the index in timeRecords, at which the timestamps for that
// specific frame are still not fully received. This is not waiting for
@@ -172,58 +228,11 @@
public:
TimeStats();
-
- // Delegate to the statsd service and associated APIs.
- // Production code may use this class directly, whereas unit test may define
- // a subclass for ease of testing.
- class StatsEventDelegate {
- public:
- virtual ~StatsEventDelegate() = default;
- virtual AStatsEvent* addStatsEventToPullData(AStatsEventList* data) {
- return AStatsEventList_addStatsEvent(data);
- }
- virtual void setStatsPullAtomCallback(int32_t atom_tag,
- AStatsManager_PullAtomMetadata* metadata,
- AStatsManager_PullAtomCallback callback,
- void* cookie) {
- return AStatsManager_setPullAtomCallback(atom_tag, metadata, callback, cookie);
- }
-
- virtual void clearStatsPullAtomCallback(int32_t atom_tag) {
- return AStatsManager_clearPullAtomCallback(atom_tag);
- }
-
- virtual void statsEventSetAtomId(AStatsEvent* event, uint32_t atom_id) {
- return AStatsEvent_setAtomId(event, atom_id);
- }
-
- virtual void statsEventWriteInt32(AStatsEvent* event, int32_t field) {
- return AStatsEvent_writeInt32(event, field);
- }
-
- virtual void statsEventWriteInt64(AStatsEvent* event, int64_t field) {
- return AStatsEvent_writeInt64(event, field);
- }
-
- virtual void statsEventWriteString8(AStatsEvent* event, const char* field) {
- return AStatsEvent_writeString(event, field);
- }
-
- virtual void statsEventWriteByteArray(AStatsEvent* event, const uint8_t* buf,
- size_t numBytes) {
- return AStatsEvent_writeByteArray(event, buf, numBytes);
- }
-
- virtual void statsEventBuild(AStatsEvent* event) { return AStatsEvent_build(event); }
- };
// For testing only for injecting custom dependencies.
- TimeStats(std::unique_ptr<StatsEventDelegate> statsDelegate,
- std::optional<size_t> maxPulledLayers,
+ TimeStats(std::optional<size_t> maxPulledLayers,
std::optional<size_t> maxPulledHistogramBuckets);
- ~TimeStats() override;
-
- void onBootFinished() override;
+ bool onPullAtom(const int atomId, std::string* pulledData) override;
void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) override;
bool isEnabled() override;
std::string miniDump() override;
@@ -241,7 +250,7 @@
void recordRenderEngineDuration(nsecs_t startTime,
const std::shared_ptr<FenceTime>& readyFence) override;
- void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
+ void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName, uid_t uid,
nsecs_t postTime) override;
void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) override;
void incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) override;
@@ -250,9 +259,14 @@
void setAcquireTime(int32_t layerId, uint64_t frameNumber, nsecs_t acquireTime) override;
void setAcquireFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& acquireFence) override;
- void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) override;
+ void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
+ Fps displayRefreshRate, std::optional<Fps> renderRate,
+ SetFrameRateVote frameRateVote) override;
void setPresentFence(int32_t layerId, uint64_t frameNumber,
- const std::shared_ptr<FenceTime>& presentFence) override;
+ const std::shared_ptr<FenceTime>& presentFence, Fps displayRefreshRate,
+ std::optional<Fps> renderRate, SetFrameRateVote frameRateVote) override;
+
+ void incrementJankyFrames(const JankyFramesInfo& info) override;
// Clean up the layer record
void onDestroy(int32_t layerId) override;
// If SF skips or rejects a buffer, remove the corresponding TimeRecord.
@@ -267,15 +281,15 @@
static const size_t MAX_NUM_TIME_RECORDS = 64;
private:
- static AStatsManager_PullAtomCallbackReturn pullAtomCallback(int32_t atom_tag,
- AStatsEventList* data,
- void* cookie);
- AStatsManager_PullAtomCallbackReturn populateGlobalAtom(AStatsEventList* data);
- AStatsManager_PullAtomCallbackReturn populateLayerAtom(AStatsEventList* data);
+ bool populateGlobalAtom(std::string* pulledData);
+ bool populateLayerAtom(std::string* pulledData);
bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord);
- void flushAvailableRecordsToStatsLocked(int32_t layerId);
+ void flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
+ std::optional<Fps> renderRate,
+ SetFrameRateVote frameRateVote);
void flushPowerTimeLocked();
void flushAvailableGlobalRecordsToStatsLocked();
+ bool canAddNewAggregatedStats(uid_t uid, const std::string& layerName);
void enable();
void disable();
@@ -293,9 +307,12 @@
GlobalRecord mGlobalRecord;
static const size_t MAX_NUM_LAYER_RECORDS = 200;
+
+ static const size_t REFRESH_RATE_BUCKET_WIDTH = 30;
+ static const size_t RENDER_RATE_BUCKET_WIDTH = REFRESH_RATE_BUCKET_WIDTH;
static const size_t MAX_NUM_LAYER_STATS = 200;
- std::unique_ptr<StatsEventDelegate> mStatsDelegate = std::make_unique<StatsEventDelegate>();
- size_t mMaxPulledLayers = 8;
+ static const size_t MAX_NUM_PULLED_LAYERS = MAX_NUM_LAYER_STATS;
+ size_t mMaxPulledLayers = MAX_NUM_PULLED_LAYERS;
size_t mMaxPulledHistogramBuckets = 6;
};
diff --git a/services/surfaceflinger/TimeStats/timestatsatomsproto/Android.bp b/services/surfaceflinger/TimeStats/timestatsatomsproto/Android.bp
new file mode 100644
index 0000000..0cf086f
--- /dev/null
+++ b/services/surfaceflinger/TimeStats/timestatsatomsproto/Android.bp
@@ -0,0 +1,36 @@
+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"],
+}
+
+cc_library {
+ name: "libtimestats_atoms_proto",
+ export_include_dirs: ["include"],
+
+ srcs: [
+ "timestats_atoms.proto",
+ ],
+
+ proto: {
+ type: "lite",
+ export_proto_headers: true,
+ },
+
+ cppflags: [
+ "-Werror",
+ "-Wno-c++98-compat-pedantic",
+ "-Wno-disabled-macro-expansion",
+ "-Wno-float-conversion",
+ "-Wno-float-equal",
+ "-Wno-format",
+ "-Wno-old-style-cast",
+ "-Wno-padded",
+ "-Wno-sign-conversion",
+ "-Wno-undef",
+ "-Wno-unused-parameter",
+ ],
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/TimeStats/timestatsatomsproto/include/timestatsatomsproto/TimeStatsAtomsProtoHeader.h b/services/surfaceflinger/TimeStats/timestatsatomsproto/include/timestatsatomsproto/TimeStatsAtomsProtoHeader.h
new file mode 100644
index 0000000..d305cb4
--- /dev/null
+++ b/services/surfaceflinger/TimeStats/timestatsatomsproto/include/timestatsatomsproto/TimeStatsAtomsProtoHeader.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2021 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.
+ */
+
+// pragma is used here to disable the warnings emitted from the protobuf
+// headers. By adding #pragma before including layer.pb.h, it suppresses
+// protobuf warnings, but allows the rest of the files to continuing using
+// the current flags.
+// This file should be included instead of directly including timestats_atoms.b.h
+#pragma GCC system_header
+#include <timestats_atoms.pb.h>
diff --git a/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto b/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto
new file mode 100644
index 0000000..133a541
--- /dev/null
+++ b/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto
@@ -0,0 +1,289 @@
+/*
+ * Copyright 2021 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.
+ */
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+package android.surfaceflinger;
+
+// This is a copy of surfaceflinger's atoms from frameworks/proto_logging/stats/atoms.proto.
+// Pulled atoms for surfaceflinger must be routed through system server since surfaceflinger is
+// in the bootstrap namespace. This copy is used to pass the atoms as protos to system server using
+// proto lite to serialize/deserialize the atoms.
+
+// These wrappers are so that we can pass a List<Atom> as a single byte string.
+// They are not in atoms.proto
+message SurfaceflingerStatsGlobalInfoWrapper {
+ repeated SurfaceflingerStatsGlobalInfo atom = 1;
+}
+
+message SurfaceflingerStatsLayerInfoWrapper {
+ repeated SurfaceflingerStatsLayerInfo atom = 1;
+}
+
+/**
+ * Global display pipeline metrics reported by SurfaceFlinger.
+ * Metrics exist beginning in Android 11.
+ * Pulled from:
+ * frameworks/native/services/surfaceflinger/TimeStats/TimeStats.cpp
+ */
+message SurfaceflingerStatsGlobalInfo {
+ // Aggregated refresh rate buckets that layers were presenting at. Buckets
+ // are defined in SurfaceFlinger and are tracked per device.
+ // Introduced in Android 12.
+ // This is intended to be used as a dimenstion in collecting per-refresh rate
+ // jank statistics.
+ optional int32 display_refresh_rate_bucket = 18;
+ // Aggregated render rate buckets that layers were overridden to run at.
+ // Buckets are defined in SurfaceFlinger and are tracked per device.
+ // Introduced in Android 12.
+ // This is intended to be used as a dimension in collecting per-render rate
+ // jank statistics.
+ optional int32 render_rate_bucket = 21;
+ // Total number of frames presented during the tracing period
+ // Note: This stat is not sliced by dimension. It will be duplicated for metrics
+ // using render_rate_bucket as a dimension.
+ optional int64 total_frames = 1;
+ // Total number of frames missed
+ // Note: This stat is not sliced by dimension. It will be duplicated for metrics
+ // using render_rate_bucket as a dimension.
+ optional int64 missed_frames = 2;
+ // Total number of frames that fell back to client composition
+ // Note: This stat is not sliced by dimension. It will be duplicated for metrics
+ // using render_rate_bucket as a dimension.
+ optional int64 client_composition_frames = 3;
+ // Total time the display was turned on
+ // Note: This stat is not sliced by dimension. It will be duplicated for metrics
+ // using render_rate_bucket as a dimension.
+ optional int64 display_on_millis = 4;
+ // Total time that was spent performing animations.
+ // This is derived from the present-to-present layer histogram.
+ // Note: This stat is not sliced by dimension. It will be duplicated for metrics
+ // using render_rate_bucket as a dimension.
+ optional int64 animation_millis = 5;
+ // Total number of event connections tracked by SurfaceFlinger at the time
+ // of this pull. If this number grows prohibitively large, then this can
+ // cause jank due to resource contention.
+ // Note: This stat is not sliced by dimension. It will be duplicated for metrics
+ // using render_rate_bucket as a dimension.
+ optional int32 event_connection_count = 6;
+ // Set of timings measured from when SurfaceFlinger began compositing a
+ // frame, until the frame was requested to be presented to the display. This
+ // measures SurfaceFlinger's total CPU walltime on the critical path per
+ // frame.
+ // Note: This stat is not sliced by dimension. It will be duplicated for metrics
+ // using render_rate_bucket as a dimension.
+ optional FrameTimingHistogram frame_duration = 7;
+ // Set of timings measured from when SurfaceFlinger first began using the
+ // GPU to composite a frame, until the GPU has finished compositing that
+ // frame. This measures the total additional time SurfaceFlinger needed to
+ // perform due to falling back into GPU composition.
+ // Note: This stat is not sliced by dimension. It will be duplicated for metrics
+ // using render_rate_bucket as a dimension.
+ optional FrameTimingHistogram render_engine_timing = 8;
+ // Number of frames where SF saw a frame, based on its frame timeline.
+ // Frame timelines may include transactions without updating buffer contents.
+ // Introduced in Android 12.
+ optional int32 total_timeline_frames = 9;
+ // Number of frames where SF saw a janky frame.
+ // Introduced in Android 12.
+ optional int32 total_janky_frames = 10;
+ // Number of janky frames where SF spent a long time on the CPU.
+ // Introduced in Android 12.
+ optional int32 total_janky_frames_with_long_cpu = 11;
+ // Number of janky frames where SF spent a long time on the GPU.
+ // Introduced in Android 12.
+ optional int32 total_janky_frames_with_long_gpu = 12;
+ // Number of janky frames where SF missed the frame deadline, but there
+ // was not an attributed reason (e.g., maybe HWC missed?)
+ // Introduced in Android 12.
+ optional int32 total_janky_frames_sf_unattributed = 13;
+ // Number of janky frames where the app missed the frame deadline, but
+ // there was not an attributed reason
+ // Introduced in Android 12.
+ optional int32 total_janky_frames_app_unattributed = 14;
+ // Number of janky frames that were caused because of scheduling errors in
+ // SF that resulted in early present (e.g., SF sending a buffer to the
+ // composition engine earlier than expected, resulting in a present that is
+ // one vsync early)
+ // Introduced in Android 12.
+ optional int32 total_janky_frames_sf_scheduling = 15;
+ // Number of frames that were classified as jank because of possible drift in
+ // vsync predictions.
+ // Introduced in Android 12.
+ optional int32 total_jank_frames_sf_prediction_error = 16;
+ // Number of janky frames where the app was in a buffer stuffed state (more
+ // than one buffer ready to be presented at the same vsync). Usually caused
+ // when the first frame is unusually long, the following frames enter into a
+ // stuffed state.
+ // Introduced in Android 12.
+ optional int32 total_jank_frames_app_buffer_stuffing = 17;
+ // Buckets of timings in ms by which SurfaceFlinger's deadline was missed
+ // while latching and presenting frames.
+ // Introduced in Android 12.
+ optional FrameTimingHistogram sf_deadline_misses = 19;
+ // Buckets of timings in ms by which the Vsync prediction drifted, when
+ // compared to the actual hardware vsync.
+ // Introduced in Android 12.
+ optional FrameTimingHistogram sf_prediction_errors = 20;
+
+ // Next ID: 22
+}
+
+/**
+ * Per-layer display pipeline metrics reported by SurfaceFlinger.
+ * Metrics exist beginning in Android 11.
+ * The number of layers uploaded may be restricted due to size limitations.
+ * Pulled from:
+ * frameworks/native/services/surfaceflinger/TimeStats/TimeStats.cpp
+ */
+message SurfaceflingerStatsLayerInfo {
+ // UID of the application who submitted this layer for presentation
+ // This is intended to be used as a dimension for surfacing rendering
+ // statistics to applications.
+ // Introduced in Android 12.
+ optional int32 uid = 12;
+ // Refresh rate bucket that the layer was presenting at. Buckets are
+ // defined in SurfaceFlinger and are tracked per device.
+ // Introduced in Android 12.
+ // This is intended to be used as a dimension in collecting per-refresh rate
+ // jank statistics
+ optional int32 display_refresh_rate_bucket = 22;
+ // Render rate bucket that the layer was submitting frames at. Buckets are
+ // defined in SurfaceFlinger and are tracked per device.
+ // Introduced in Android 12.
+ // This is intended to be used as a dimension in collecting per-render rate
+ // jank statistics.
+ optional int32 render_rate_bucket = 23;
+ // The layer for this set of metrics
+ // In many scenarios the package name is included in the layer name, e.g.,
+ // layers created by Window Manager. But this is not a guarantee - in the
+ // general case layer names are arbitrary debug names.
+ optional string layer_name = 1;
+ // Total number of frames presented
+ optional int64 total_frames = 2;
+ // Total number of dropped frames while latching a buffer for this layer.
+ optional int64 dropped_frames = 3;
+ // Set of timings measured between successive presentation timestamps.
+ optional FrameTimingHistogram present_to_present = 4;
+ // Set of timings measured from when an app queued a buffer for
+ // presentation, until the buffer was actually presented to the
+ // display.
+ optional FrameTimingHistogram post_to_present = 5;
+ // Set of timings measured from when a buffer is ready to be presented,
+ // until the buffer was actually presented to the display.
+ optional FrameTimingHistogram acquire_to_present = 6;
+ // Set of timings measured from when a buffer was latched by
+ // SurfaceFlinger, until the buffer was presented to the display
+ optional FrameTimingHistogram latch_to_present = 7;
+ // Set of timings measured from the desired presentation to the actual
+ // presentation time
+ optional FrameTimingHistogram desired_to_present = 8;
+ // Set of timings measured from when an app queued a buffer for
+ // presentation, until the buffer was ready to be presented.
+ optional FrameTimingHistogram post_to_acquire = 9;
+ // Frames missed latch because the acquire fence didn't fire
+ optional int64 late_acquire_frames = 10;
+ // Frames latched early because the desired present time was bad
+ optional int64 bad_desired_present_frames = 11;
+ // Number of frames where SF saw a frame, based on its frame timeline.
+ // Frame timelines may include transactions without updating buffer contents.
+ // Introduced in Android 12.
+ optional int32 total_timeline_frames = 13;
+ // Number of frames where SF saw a janky frame.
+ // Introduced in Android 12.
+ optional int32 total_janky_frames = 14;
+ // Number of janky frames where SF spent a long time on the CPU.
+ // Introduced in Android 12.
+ optional int32 total_janky_frames_with_long_cpu = 15;
+ // Number of janky frames where SF spent a long time on the GPU.
+ // Introduced in Android 12.
+ optional int32 total_janky_frames_with_long_gpu = 16;
+ // Number of janky frames where SF missed the frame deadline, but there
+ // was not an attributed reason (e.g., maybe HWC missed?)
+ // Introduced in Android 12.
+ optional int32 total_janky_frames_sf_unattributed = 17;
+ // Number of janky frames where the app missed the frame deadline, but
+ // there was not an attributed reason
+ // Introduced in Android 12.
+ optional int32 total_janky_frames_app_unattributed = 18;
+ // Number of janky frames that were caused because of scheduling errors in
+ // SF that resulted in early present (e.g., SF sending a buffer to the
+ // composition engine earlier than expected, resulting in a present that is
+ // one vsync early)
+ // Introduced in Android 12.
+ optional int32 total_janky_frames_sf_scheduling = 19;
+ // Number of frames that were classified as jank because of possible drift in
+ // vsync predictions.
+ // Introduced in Android 12.
+ optional int32 total_jank_frames_sf_prediction_error = 20;
+ // Number of janky frames where the app was in a buffer stuffed state (more
+ // than one buffer ready to be presented at the same vsync). Usually caused
+ // when the first frame is unusually long, the following frames enter into a
+ // stuffed state.
+ // Introduced in Android 12.
+ optional int32 total_jank_frames_app_buffer_stuffing = 21;
+
+ /**
+ * Encapsulates the FrameRateVote information sent by the application while
+ * calling setFrameRate.
+ * Metrics exist beginning in Android 12.
+ */
+ message SetFrameRateVote {
+ // The desired frame rate the application wishes to run on.
+ optional float frame_rate = 1;
+
+ enum FrameRateCompatibility {
+ FRAME_RATE_UNDEFINED = 0;
+ FRAME_RATE_DEFAULT = 1;
+ FRAME_RATE_EXACT_OR_MULTIPLE = 2;
+ }
+
+ // Specifies how to interpret the frame rate associated with the layer.
+ // Defined in Layer.h
+ optional FrameRateCompatibility frame_rate_compatibility = 2;
+
+ enum Seamlessness {
+ SEAMLESS_UNDEFINED = 0;
+ SEAMLESS_SHOULD_BE_SEAMLESS = 1;
+ SEAMLESS_NOT_REQUIRED = 2;
+ }
+ // Indicates whether seamless refresh rate switch is required or not.
+ optional Seamlessness seamlessness = 3;
+ }
+
+ // The last frame rate vote set by the application.
+ // Introduced in Android 12.
+ optional SetFrameRateVote set_frame_rate_vote = 24;
+ // Buckets of timings in ms by which the app deadline was missed while
+ // submitting work for a frame.
+ // Introduced in Android 12.
+ optional FrameTimingHistogram app_deadline_misses = 25;
+
+ // Next ID: 26
+}
+
+/**
+ * Histogram of frame counts bucketed by time in milliseconds.
+ * Because of size limitations, we hard-cap the number of buckets, with
+ * buckets for corresponding to larger milliseconds being less precise.
+ */
+message FrameTimingHistogram {
+ // Timings in milliseconds that describes a set of histogram buckets
+ repeated int32 time_millis_buckets = 1;
+ // Number of frames that match to each time_millis, i.e. the bucket
+ // contents
+ // It's required that len(time_millis) == len(frame_count)
+ repeated int64 frame_counts = 2;
+}
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/Android.bp b/services/surfaceflinger/TimeStats/timestatsproto/Android.bp
index 4743098..972edaa 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/Android.bp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/Android.bp
@@ -7,7 +7,7 @@
default_applicable_licenses: ["frameworks_native_license"],
}
-cc_library_shared {
+cc_library {
name: "libtimestats_proto",
export_include_dirs: ["include"],
@@ -39,3 +39,28 @@
"-Wno-unused-parameter",
],
}
+
+// ==== java host library for timestats proto ===========================
+// Note timestats is deprecated and is only used for legacy tests
+java_library_host {
+ name: "host-timestats-proto",
+ srcs: [
+ "timestats.proto",
+ ],
+ proto: {
+ type: "full",
+ },
+}
+
+// ==== java device library for timestats proto ===========================
+// Note timestats is deprecated and is only used for legacy tests
+java_library {
+ name: "timestats-proto",
+ srcs: [
+ "timestats.proto",
+ ],
+ proto: {
+ type: "lite",
+ },
+ sdk_version: "current",
+}
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index 894ee6d..a7e7db2 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -41,7 +41,7 @@
if (delta < 0) return;
// std::lower_bound won't work on out of range values
if (delta > histogramConfig[HISTOGRAM_SIZE - 1]) {
- hist[histogramConfig[HISTOGRAM_SIZE - 1]] += delta / histogramConfig[HISTOGRAM_SIZE - 1];
+ hist[histogramConfig[HISTOGRAM_SIZE - 1]]++;
return;
}
auto iter = std::lower_bound(histogramConfig.begin(), histogramConfig.end(), delta);
@@ -77,14 +77,66 @@
return result;
}
+std::string TimeStatsHelper::JankPayload::toString() const {
+ std::string result;
+ StringAppendF(&result, "totalTimelineFrames = %d\n", totalFrames);
+ StringAppendF(&result, "jankyFrames = %d\n", totalJankyFrames);
+ StringAppendF(&result, "sfLongCpuJankyFrames = %d\n", totalSFLongCpu);
+ StringAppendF(&result, "sfLongGpuJankyFrames = %d\n", totalSFLongGpu);
+ StringAppendF(&result, "sfUnattributedJankyFrames = %d\n", totalSFUnattributed);
+ StringAppendF(&result, "appUnattributedJankyFrames = %d\n", totalAppUnattributed);
+ StringAppendF(&result, "sfSchedulingJankyFrames = %d\n", totalSFScheduling);
+ StringAppendF(&result, "sfPredictionErrorJankyFrames = %d\n", totalSFPredictionError);
+ StringAppendF(&result, "appBufferStuffingJankyFrames = %d\n", totalAppBufferStuffing);
+ return result;
+}
+
+std::string TimeStatsHelper::SetFrameRateVote::toString(FrameRateCompatibility compatibility) {
+ switch (compatibility) {
+ case FrameRateCompatibility::Undefined:
+ return "Undefined";
+ case FrameRateCompatibility::Default:
+ return "Default";
+ case FrameRateCompatibility::ExactOrMultiple:
+ return "ExactOrMultiple";
+ }
+}
+
+std::string TimeStatsHelper::SetFrameRateVote::toString(Seamlessness seamlessness) {
+ switch (seamlessness) {
+ case Seamlessness::Undefined:
+ return "Undefined";
+ case Seamlessness::ShouldBeSeamless:
+ return "ShouldBeSeamless";
+ case Seamlessness::NotRequired:
+ return "NotRequired";
+ }
+}
+
+std::string TimeStatsHelper::SetFrameRateVote::toString() const {
+ std::string result;
+ StringAppendF(&result, "frameRate = %.2f\n", frameRate);
+ StringAppendF(&result, "frameRateCompatibility = %s\n",
+ toString(frameRateCompatibility).c_str());
+ StringAppendF(&result, "seamlessness = %s\n", toString(seamlessness).c_str());
+ return result;
+}
+
std::string TimeStatsHelper::TimeStatsLayer::toString() const {
std::string result = "\n";
+ StringAppendF(&result, "displayRefreshRate = %d fps\n", displayRefreshRateBucket);
+ StringAppendF(&result, "renderRate = %d fps\n", renderRateBucket);
+ StringAppendF(&result, "uid = %d\n", uid);
StringAppendF(&result, "layerName = %s\n", layerName.c_str());
StringAppendF(&result, "packageName = %s\n", packageName.c_str());
StringAppendF(&result, "totalFrames = %d\n", totalFrames);
StringAppendF(&result, "droppedFrames = %d\n", droppedFrames);
StringAppendF(&result, "lateAcquireFrames = %d\n", lateAcquireFrames);
StringAppendF(&result, "badDesiredPresentFrames = %d\n", badDesiredPresentFrames);
+ result.append("Jank payload for this layer:\n");
+ result.append(jankPayload.toString());
+ result.append("SetFrateRate vote for this layer:\n");
+ result.append(setFrameRateVote.toString());
const auto iter = deltas.find("present2present");
if (iter != deltas.end()) {
const float averageTime = iter->second.averageTime();
@@ -101,33 +153,49 @@
std::string TimeStatsHelper::TimeStatsGlobal::toString(std::optional<uint32_t> maxLayers) const {
std::string result = "SurfaceFlinger TimeStats:\n";
- StringAppendF(&result, "statsStart = %" PRId64 "\n", statsStart);
- StringAppendF(&result, "statsEnd = %" PRId64 "\n", statsEnd);
- StringAppendF(&result, "totalFrames = %d\n", totalFrames);
- StringAppendF(&result, "missedFrames = %d\n", missedFrames);
- StringAppendF(&result, "clientCompositionFrames = %d\n", clientCompositionFrames);
- StringAppendF(&result, "clientCompositionReusedFrames = %d\n", clientCompositionReusedFrames);
- StringAppendF(&result, "refreshRateSwitches = %d\n", refreshRateSwitches);
- StringAppendF(&result, "compositionStrategyChanges = %d\n", compositionStrategyChanges);
- StringAppendF(&result, "displayOnTime = %" PRId64 " ms\n", displayOnTime);
+ result.append("Legacy stats are as follows:\n");
+ StringAppendF(&result, "statsStart = %" PRId64 "\n", statsStartLegacy);
+ StringAppendF(&result, "statsEnd = %" PRId64 "\n", statsEndLegacy);
+ StringAppendF(&result, "totalFrames = %d\n", totalFramesLegacy);
+ StringAppendF(&result, "missedFrames = %d\n", missedFramesLegacy);
+ StringAppendF(&result, "clientCompositionFrames = %d\n", clientCompositionFramesLegacy);
+ StringAppendF(&result, "clientCompositionReusedFrames = %d\n",
+ clientCompositionReusedFramesLegacy);
+ StringAppendF(&result, "refreshRateSwitches = %d\n", refreshRateSwitchesLegacy);
+ StringAppendF(&result, "compositionStrategyChanges = %d\n", compositionStrategyChangesLegacy);
+ StringAppendF(&result, "displayOnTime = %" PRId64 " ms\n", displayOnTimeLegacy);
StringAppendF(&result, "displayConfigStats is as below:\n");
- for (const auto& [fps, duration] : refreshRateStats) {
- StringAppendF(&result, "%dfps=%ldms ", fps, ns2ms(duration));
+ for (const auto& [fps, duration] : refreshRateStatsLegacy) {
+ StringAppendF(&result, "%dfps = %ldms\n", fps, ns2ms(duration));
}
result.back() = '\n';
- StringAppendF(&result, "totalP2PTime = %" PRId64 " ms\n", presentToPresent.totalTime());
+ StringAppendF(&result, "totalP2PTime = %" PRId64 " ms\n", presentToPresentLegacy.totalTime());
StringAppendF(&result, "presentToPresent histogram is as below:\n");
- result.append(presentToPresent.toString());
- const float averageFrameDuration = frameDuration.averageTime();
+ result.append(presentToPresentLegacy.toString());
+ const float averageFrameDuration = frameDurationLegacy.averageTime();
StringAppendF(&result, "averageFrameDuration = %.3f ms\n",
std::isnan(averageFrameDuration) ? 0.0f : averageFrameDuration);
StringAppendF(&result, "frameDuration histogram is as below:\n");
- result.append(frameDuration.toString());
- const float averageRenderEngineTiming = renderEngineTiming.averageTime();
+ result.append(frameDurationLegacy.toString());
+ const float averageRenderEngineTiming = renderEngineTimingLegacy.averageTime();
StringAppendF(&result, "averageRenderEngineTiming = %.3f ms\n",
std::isnan(averageRenderEngineTiming) ? 0.0f : averageRenderEngineTiming);
StringAppendF(&result, "renderEngineTiming histogram is as below:\n");
- result.append(renderEngineTiming.toString());
+ result.append(renderEngineTimingLegacy.toString());
+
+ result.append("\nGlobal aggregated jank payload (Timeline stats):");
+ for (const auto& ele : stats) {
+ result.append("\n");
+ StringAppendF(&result, "displayRefreshRate = %d fps\n",
+ ele.second.key.displayRefreshRateBucket);
+ StringAppendF(&result, "renderRate = %d fps\n", ele.second.key.renderRateBucket);
+ result.append(ele.second.jankPayload.toString());
+ StringAppendF(&result, "sfDeadlineMisses histogram is as below:\n");
+ result.append(ele.second.displayDeadlineDeltas.toString());
+ StringAppendF(&result, "sfPredictionErrors histogram is as below:\n");
+ result.append(ele.second.displayPresentDeltas.toString());
+ }
+
const auto dumpStats = generateDumpStats(maxLayers);
for (const auto& ele : dumpStats) {
result.append(ele->toString());
@@ -157,30 +225,30 @@
SFTimeStatsGlobalProto TimeStatsHelper::TimeStatsGlobal::toProto(
std::optional<uint32_t> maxLayers) const {
SFTimeStatsGlobalProto globalProto;
- globalProto.set_stats_start(statsStart);
- globalProto.set_stats_end(statsEnd);
- globalProto.set_total_frames(totalFrames);
- globalProto.set_missed_frames(missedFrames);
- globalProto.set_client_composition_frames(clientCompositionFrames);
- globalProto.set_display_on_time(displayOnTime);
- for (const auto& ele : refreshRateStats) {
+ globalProto.set_stats_start(statsStartLegacy);
+ globalProto.set_stats_end(statsEndLegacy);
+ globalProto.set_total_frames(totalFramesLegacy);
+ globalProto.set_missed_frames(missedFramesLegacy);
+ globalProto.set_client_composition_frames(clientCompositionFramesLegacy);
+ globalProto.set_display_on_time(displayOnTimeLegacy);
+ for (const auto& ele : refreshRateStatsLegacy) {
SFTimeStatsDisplayConfigBucketProto* configBucketProto =
globalProto.add_display_config_stats();
SFTimeStatsDisplayConfigProto* configProto = configBucketProto->mutable_config();
configProto->set_fps(ele.first);
configBucketProto->set_duration_millis(ns2ms(ele.second));
}
- for (const auto& histEle : presentToPresent.hist) {
+ for (const auto& histEle : presentToPresentLegacy.hist) {
SFTimeStatsHistogramBucketProto* histProto = globalProto.add_present_to_present();
histProto->set_time_millis(histEle.first);
histProto->set_frame_count(histEle.second);
}
- for (const auto& histEle : frameDuration.hist) {
+ for (const auto& histEle : frameDurationLegacy.hist) {
SFTimeStatsHistogramBucketProto* histProto = globalProto.add_frame_duration();
histProto->set_time_millis(histEle.first);
histProto->set_frame_count(histEle.second);
}
- for (const auto& histEle : renderEngineTiming.hist) {
+ for (const auto& histEle : renderEngineTimingLegacy.hist) {
SFTimeStatsHistogramBucketProto* histProto = globalProto.add_render_engine_timing();
histProto->set_time_millis(histEle.first);
histProto->set_frame_count(histEle.second);
@@ -196,8 +264,18 @@
std::vector<TimeStatsHelper::TimeStatsLayer const*>
TimeStatsHelper::TimeStatsGlobal::generateDumpStats(std::optional<uint32_t> maxLayers) const {
std::vector<TimeStatsLayer const*> dumpStats;
+
+ int numLayers = 0;
for (const auto& ele : stats) {
- dumpStats.push_back(&ele.second);
+ numLayers += ele.second.stats.size();
+ }
+
+ dumpStats.reserve(numLayers);
+
+ for (const auto& ele : stats) {
+ for (const auto& layerEle : ele.second.stats) {
+ dumpStats.push_back(&layerEle.second);
+ }
}
std::sort(dumpStats.begin(), dumpStats.end(),
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 0c75f96..2b37ffe 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -40,37 +40,146 @@
std::string toString() const;
};
+ struct JankPayload {
+ // note that transactions are counted for these frames.
+ int32_t totalFrames = 0;
+ int32_t totalJankyFrames = 0;
+ int32_t totalSFLongCpu = 0;
+ int32_t totalSFLongGpu = 0;
+ int32_t totalSFUnattributed = 0;
+ int32_t totalAppUnattributed = 0;
+ int32_t totalSFScheduling = 0;
+ int32_t totalSFPredictionError = 0;
+ int32_t totalAppBufferStuffing = 0;
+
+ std::string toString() const;
+ };
+
+ struct SetFrameRateVote {
+ float frameRate = 0;
+
+ // Needs to be in sync with atoms.proto
+ enum class FrameRateCompatibility {
+ Undefined = 0,
+ Default = 1,
+ ExactOrMultiple = 2,
+ } frameRateCompatibility = FrameRateCompatibility::Undefined;
+
+ // Needs to be in sync with atoms.proto
+ enum class Seamlessness {
+ Undefined = 0,
+ ShouldBeSeamless = 1,
+ NotRequired = 2,
+ } seamlessness = Seamlessness::Undefined;
+
+ static std::string toString(FrameRateCompatibility);
+ static std::string toString(Seamlessness);
+ std::string toString() const;
+ };
+
class TimeStatsLayer {
public:
+ uid_t uid;
std::string layerName;
std::string packageName;
+ int32_t displayRefreshRateBucket = 0;
+ int32_t renderRateBucket = 0;
int32_t totalFrames = 0;
int32_t droppedFrames = 0;
int32_t lateAcquireFrames = 0;
int32_t badDesiredPresentFrames = 0;
+ JankPayload jankPayload;
+ SetFrameRateVote setFrameRateVote;
std::unordered_map<std::string, Histogram> deltas;
std::string toString() const;
SFTimeStatsLayerProto toProto() const;
};
+ // Lifted from SkiaGLRenderEngine's LinearEffect class.
+ // Which in turn was inspired by art/runtime/class_linker.cc
+ // Also this is what boost:hash_combine does so this is a pretty good hash.
+ static size_t HashCombine(size_t seed, size_t val) {
+ return seed ^ (val + 0x9e3779b9 + (seed << 6) + (seed >> 2));
+ }
+
+ struct TimelineStatsKey {
+ int32_t displayRefreshRateBucket = 0;
+ int32_t renderRateBucket = 0;
+
+ struct Hasher {
+ size_t operator()(const TimelineStatsKey& key) const {
+ size_t result = std::hash<int32_t>{}(key.displayRefreshRateBucket);
+ return HashCombine(result, std::hash<int32_t>{}(key.renderRateBucket));
+ }
+ };
+
+ bool operator==(const TimelineStatsKey& o) const {
+ return displayRefreshRateBucket == o.displayRefreshRateBucket &&
+ renderRateBucket == o.renderRateBucket;
+ }
+ };
+
+ struct LayerStatsKey {
+ uid_t uid = 0;
+ std::string layerName;
+
+ struct Hasher {
+ size_t operator()(const LayerStatsKey& key) const {
+ size_t result = std::hash<uid_t>{}(key.uid);
+ return HashCombine(result, std::hash<std::string>{}(key.layerName));
+ }
+ };
+
+ bool operator==(const LayerStatsKey& o) const {
+ return uid == o.uid && layerName == o.layerName;
+ }
+ };
+
+ struct LayerStatsHasher {
+ size_t operator()(const std::pair<uid_t, std::string>& p) const {
+ // Normally this isn't a very good hash function due to symmetry reasons,
+ // but these are distinct types so this should be good enough
+ return std::hash<uid_t>{}(p.first) ^ std::hash<std::string>{}(p.second);
+ }
+ };
+
+ struct TimelineStats {
+ TimelineStatsKey key;
+ JankPayload jankPayload;
+ Histogram displayDeadlineDeltas;
+ Histogram displayPresentDeltas;
+ std::unordered_map<LayerStatsKey, TimeStatsLayer, LayerStatsKey::Hasher> stats;
+
+ void clearGlobals() {
+ jankPayload = {};
+ displayDeadlineDeltas = {};
+ displayPresentDeltas = {};
+ }
+ };
+
class TimeStatsGlobal {
public:
- int64_t statsStart = 0;
- int64_t statsEnd = 0;
- int32_t totalFrames = 0;
- int32_t missedFrames = 0;
- int32_t clientCompositionFrames = 0;
- int32_t clientCompositionReusedFrames = 0;
- int32_t refreshRateSwitches = 0;
- int32_t compositionStrategyChanges = 0;
- int32_t displayEventConnectionsCount = 0;
- int64_t displayOnTime = 0;
- Histogram presentToPresent;
- Histogram frameDuration;
- Histogram renderEngineTiming;
- std::unordered_map<std::string, TimeStatsLayer> stats;
- std::unordered_map<uint32_t, nsecs_t> refreshRateStats;
+ // Note: these are all legacy statistics, we're keeping these around because a variety of
+ // systems and form-factors find these useful when comparing with older releases. However,
+ // the current recommendation is that the new timeline-based metrics are used, and the old
+ // ones are deprecated.
+ int64_t statsStartLegacy = 0;
+ int64_t statsEndLegacy = 0;
+ int32_t totalFramesLegacy = 0;
+ int32_t missedFramesLegacy = 0;
+ int32_t clientCompositionFramesLegacy = 0;
+ int32_t clientCompositionReusedFramesLegacy = 0;
+ int32_t refreshRateSwitchesLegacy = 0;
+ int32_t compositionStrategyChangesLegacy = 0;
+ int32_t displayEventConnectionsCountLegacy = 0;
+ int64_t displayOnTimeLegacy = 0;
+ Histogram presentToPresentLegacy;
+ Histogram frameDurationLegacy;
+ Histogram renderEngineTimingLegacy;
+ std::unordered_map<uint32_t, nsecs_t> refreshRateStatsLegacy;
+
+ std::unordered_map<TimelineStatsKey, TimelineStats, TimelineStatsKey::Hasher> stats;
std::string toString(std::optional<uint32_t> maxLayers) const;
SFTimeStatsGlobalProto toProto(std::optional<uint32_t> maxLayers) const;
diff --git a/services/surfaceflinger/TraceUtils.h b/services/surfaceflinger/TraceUtils.h
new file mode 100644
index 0000000..90a34a5
--- /dev/null
+++ b/services/surfaceflinger/TraceUtils.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2021 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.
+ */
+
+// TODO(b/183120308): This file is a copy of f/b/libs/hwui/utils/TraceUtils.h
+// It should be migrated to a common place where both SF and hwui could use it.
+
+#pragma once
+
+#include <cutils/trace.h>
+#include <utils/Trace.h>
+
+#define ATRACE_FORMAT(fmt, ...) \
+ TraceUtils::TraceEnder __traceEnder = \
+ (TraceUtils::atraceFormatBegin(fmt, ##__VA_ARGS__), TraceUtils::TraceEnder())
+
+#define ATRACE_FORMAT_BEGIN(fmt, ...) TraceUtils::atraceFormatBegin(fmt, ##__VA_ARGS__)
+
+namespace android {
+
+class TraceUtils {
+public:
+ class TraceEnder {
+ public:
+ ~TraceEnder() { ATRACE_END(); }
+ };
+
+ static void atraceFormatBegin(const char* fmt, ...) {
+ if (CC_LIKELY(!ATRACE_ENABLED())) return;
+
+ const int BUFFER_SIZE = 256;
+ va_list ap;
+ char buf[BUFFER_SIZE];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, BUFFER_SIZE, fmt, ap);
+ va_end(ap);
+
+ ATRACE_BEGIN(buf);
+ }
+
+}; // class TraceUtils
+
+} /* namespace android */
diff --git a/services/surfaceflinger/TracedOrdinal.h b/services/surfaceflinger/TracedOrdinal.h
index 4e7f67d..eee4bec 100644
--- a/services/surfaceflinger/TracedOrdinal.h
+++ b/services/surfaceflinger/TracedOrdinal.h
@@ -21,12 +21,32 @@
#include <cmath>
#include <string>
+namespace std {
+template <class Rep, class Period>
+bool signbit(std::chrono::duration<Rep, Period> v) {
+ return signbit(std::chrono::duration_cast<std::chrono::nanoseconds>(v).count());
+}
+} // namespace std
+
namespace android {
+namespace {
+template <typename T>
+int64_t to_int64(T v) {
+ return int64_t(v);
+}
+
+template <class Rep, class Period>
+int64_t to_int64(std::chrono::duration<Rep, Period> v) {
+ return int64_t(v.count());
+}
+} // namespace
+
template <typename T>
class TracedOrdinal {
public:
- static_assert(std::is_same<bool, T>() || (std::is_signed<T>() && std::is_integral<T>()),
+ static_assert(std::is_same<bool, T>() || (std::is_signed<T>() && std::is_integral<T>()) ||
+ std::is_same<std::chrono::nanoseconds, T>(),
"Type is not supported. Please test it with systrace before adding "
"it to the list.");
@@ -37,7 +57,9 @@
trace();
}
- operator T() const { return mData; }
+ T get() const { return mData; }
+
+ operator T() const { return get(); }
TracedOrdinal& operator=(T other) {
mData = other;
@@ -57,12 +79,12 @@
}
if (!std::signbit(mData)) {
- ATRACE_INT64(mName.c_str(), int64_t(mData));
+ ATRACE_INT64(mName.c_str(), to_int64(mData));
if (mHasGoneNegative) {
ATRACE_INT64(mNameNegative.c_str(), 0);
}
} else {
- ATRACE_INT64(mNameNegative.c_str(), -int64_t(mData));
+ ATRACE_INT64(mNameNegative.c_str(), -to_int64(mData));
ATRACE_INT64(mName.c_str(), 0);
}
}
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
new file mode 100644
index 0000000..4f4c02b
--- /dev/null
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2018 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "TransactionCallbackInvoker"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "TransactionCallbackInvoker.h"
+
+#include <cinttypes>
+
+#include <binder/IInterface.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+// Returns 0 if they are equal
+// <0 if the first id that doesn't match is lower in c2 or all ids match but c2 is shorter
+// >0 if the first id that doesn't match is greater in c2 or all ids match but c2 is longer
+//
+// See CallbackIdsHash for a explanation of why this works
+static int compareCallbackIds(const std::vector<CallbackId>& c1,
+ const std::vector<CallbackId>& c2) {
+ if (c1.empty()) {
+ return !c2.empty();
+ }
+ return c1.front().id - c2.front().id;
+}
+
+static bool containsOnCommitCallbacks(const std::vector<CallbackId>& callbacks) {
+ return !callbacks.empty() && callbacks.front().type == CallbackId::Type::ON_COMMIT;
+}
+
+TransactionCallbackInvoker::~TransactionCallbackInvoker() {
+ {
+ std::lock_guard lock(mMutex);
+ for (const auto& [listener, transactionStats] : mCompletedTransactions) {
+ listener->unlinkToDeath(mDeathRecipient);
+ }
+ }
+}
+
+status_t TransactionCallbackInvoker::startRegistration(const ListenerCallbacks& listenerCallbacks) {
+ std::lock_guard lock(mMutex);
+
+ auto [itr, inserted] = mRegisteringTransactions.insert(listenerCallbacks);
+ auto& [listener, callbackIds] = listenerCallbacks;
+
+ if (inserted) {
+ if (mCompletedTransactions.count(listener) == 0) {
+ status_t err = listener->linkToDeath(mDeathRecipient);
+ if (err != NO_ERROR) {
+ ALOGE("cannot add callback because linkToDeath failed, err: %d", err);
+ return err;
+ }
+ }
+ auto& transactionStatsDeque = mCompletedTransactions[listener];
+ transactionStatsDeque.emplace_back(callbackIds);
+ }
+
+ return NO_ERROR;
+}
+
+status_t TransactionCallbackInvoker::endRegistration(const ListenerCallbacks& listenerCallbacks) {
+ std::lock_guard lock(mMutex);
+
+ auto itr = mRegisteringTransactions.find(listenerCallbacks);
+ if (itr == mRegisteringTransactions.end()) {
+ ALOGE("cannot end a registration that does not exist");
+ return BAD_VALUE;
+ }
+
+ mRegisteringTransactions.erase(itr);
+
+ return NO_ERROR;
+}
+
+bool TransactionCallbackInvoker::isRegisteringTransaction(
+ const sp<IBinder>& transactionListener, const std::vector<CallbackId>& callbackIds) {
+ ListenerCallbacks listenerCallbacks(transactionListener, callbackIds);
+
+ auto itr = mRegisteringTransactions.find(listenerCallbacks);
+ return itr != mRegisteringTransactions.end();
+}
+
+status_t TransactionCallbackInvoker::registerPendingCallbackHandle(
+ const sp<CallbackHandle>& handle) {
+ std::lock_guard lock(mMutex);
+
+ // If we can't find the transaction stats something has gone wrong. The client should call
+ // startRegistration before trying to register a pending callback handle.
+ TransactionStats* transactionStats;
+ status_t err = findTransactionStats(handle->listener, handle->callbackIds, &transactionStats);
+ if (err != NO_ERROR) {
+ ALOGE("cannot find transaction stats");
+ return err;
+ }
+
+ mPendingTransactions[handle->listener][handle->callbackIds]++;
+ return NO_ERROR;
+}
+
+status_t TransactionCallbackInvoker::finalizeCallbackHandle(const sp<CallbackHandle>& handle,
+ const std::vector<JankData>& jankData) {
+ auto listener = mPendingTransactions.find(handle->listener);
+ if (listener != mPendingTransactions.end()) {
+ auto& pendingCallbacks = listener->second;
+ auto pendingCallback = pendingCallbacks.find(handle->callbackIds);
+
+ if (pendingCallback != pendingCallbacks.end()) {
+ auto& pendingCount = pendingCallback->second;
+
+ // Decrease the pending count for this listener
+ if (--pendingCount == 0) {
+ pendingCallbacks.erase(pendingCallback);
+ }
+ } else {
+ ALOGW("there are more latched callbacks than there were registered callbacks");
+ }
+ if (listener->second.size() == 0) {
+ mPendingTransactions.erase(listener);
+ }
+ } else {
+ ALOGW("cannot find listener in mPendingTransactions");
+ }
+
+ status_t err = addCallbackHandle(handle, jankData);
+ if (err != NO_ERROR) {
+ ALOGE("could not add callback handle");
+ return err;
+ }
+ return NO_ERROR;
+}
+
+status_t TransactionCallbackInvoker::finalizeOnCommitCallbackHandles(
+ const std::deque<sp<CallbackHandle>>& handles,
+ std::deque<sp<CallbackHandle>>& outRemainingHandles) {
+ if (handles.empty()) {
+ return NO_ERROR;
+ }
+ std::lock_guard lock(mMutex);
+ const std::vector<JankData>& jankData = std::vector<JankData>();
+ for (const auto& handle : handles) {
+ if (!containsOnCommitCallbacks(handle->callbackIds)) {
+ outRemainingHandles.push_back(handle);
+ continue;
+ }
+ status_t err = finalizeCallbackHandle(handle, jankData);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ }
+
+ return NO_ERROR;
+}
+
+status_t TransactionCallbackInvoker::finalizePendingCallbackHandles(
+ const std::deque<sp<CallbackHandle>>& handles, const std::vector<JankData>& jankData) {
+ if (handles.empty()) {
+ return NO_ERROR;
+ }
+ std::lock_guard lock(mMutex);
+ for (const auto& handle : handles) {
+ status_t err = finalizeCallbackHandle(handle, jankData);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ }
+
+ return NO_ERROR;
+}
+
+status_t TransactionCallbackInvoker::registerUnpresentedCallbackHandle(
+ const sp<CallbackHandle>& handle) {
+ std::lock_guard lock(mMutex);
+
+ return addCallbackHandle(handle, std::vector<JankData>());
+}
+
+status_t TransactionCallbackInvoker::findTransactionStats(
+ const sp<IBinder>& listener, const std::vector<CallbackId>& callbackIds,
+ TransactionStats** outTransactionStats) {
+ auto& transactionStatsDeque = mCompletedTransactions[listener];
+
+ // Search back to front because the most recent transactions are at the back of the deque
+ auto itr = transactionStatsDeque.rbegin();
+ for (; itr != transactionStatsDeque.rend(); itr++) {
+ if (compareCallbackIds(itr->callbackIds, callbackIds) == 0) {
+ *outTransactionStats = &(*itr);
+ return NO_ERROR;
+ }
+ }
+
+ ALOGE("could not find transaction stats");
+ return BAD_VALUE;
+}
+
+status_t TransactionCallbackInvoker::addCallbackHandle(const sp<CallbackHandle>& handle,
+ const std::vector<JankData>& jankData) {
+ // If we can't find the transaction stats something has gone wrong. The client should call
+ // startRegistration before trying to add a callback handle.
+ TransactionStats* transactionStats;
+ status_t err = findTransactionStats(handle->listener, handle->callbackIds, &transactionStats);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ transactionStats->latchTime = handle->latchTime;
+ // If the layer has already been destroyed, don't add the SurfaceControl to the callback.
+ // The client side keeps a sp<> to the SurfaceControl so if the SurfaceControl has been
+ // destroyed the client side is dead and there won't be anyone to send the callback to.
+ sp<IBinder> surfaceControl = handle->surfaceControl.promote();
+ if (surfaceControl) {
+ FrameEventHistoryStats eventStats(handle->frameNumber,
+ handle->gpuCompositionDoneFence->getSnapshot().fence,
+ handle->compositorTiming, handle->refreshStartTime,
+ handle->dequeueReadyTime);
+ transactionStats->surfaceStats.emplace_back(surfaceControl, handle->acquireTime,
+ handle->previousReleaseFence,
+ handle->transformHint, eventStats, jankData,
+ handle->previousBufferId);
+ }
+ return NO_ERROR;
+}
+
+void TransactionCallbackInvoker::addPresentFence(const sp<Fence>& presentFence) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mPresentFence = presentFence;
+}
+
+void TransactionCallbackInvoker::sendCallbacks() {
+ std::lock_guard lock(mMutex);
+
+ // For each listener
+ auto completedTransactionsItr = mCompletedTransactions.begin();
+ while (completedTransactionsItr != mCompletedTransactions.end()) {
+ auto& [listener, transactionStatsDeque] = *completedTransactionsItr;
+ ListenerStats listenerStats;
+ listenerStats.listener = listener;
+
+ // For each transaction
+ auto transactionStatsItr = transactionStatsDeque.begin();
+ while (transactionStatsItr != transactionStatsDeque.end()) {
+ auto& transactionStats = *transactionStatsItr;
+
+ // If this transaction is still registering, it is not safe to send a callback
+ // because there could be surface controls that haven't been added to
+ // transaction stats or mPendingTransactions.
+ if (isRegisteringTransaction(listener, transactionStats.callbackIds)) {
+ break;
+ }
+
+ // If we are still waiting on the callback handles for this transaction, stop
+ // here because all transaction callbacks for the same listener must come in order
+ auto pendingTransactions = mPendingTransactions.find(listener);
+ if (pendingTransactions != mPendingTransactions.end() &&
+ pendingTransactions->second.count(transactionStats.callbackIds) != 0) {
+ break;
+ }
+
+ // If the transaction has been latched
+ if (transactionStats.latchTime >= 0 &&
+ !containsOnCommitCallbacks(transactionStats.callbackIds)) {
+ if (!mPresentFence) {
+ break;
+ }
+ transactionStats.presentFence = mPresentFence;
+ }
+
+ // Remove the transaction from completed to the callback
+ listenerStats.transactionStats.push_back(std::move(transactionStats));
+ transactionStatsItr = transactionStatsDeque.erase(transactionStatsItr);
+ }
+ // If the listener has completed transactions
+ if (!listenerStats.transactionStats.empty()) {
+ // If the listener is still alive
+ if (listener->isBinderAlive()) {
+ // Send callback. The listener stored in listenerStats
+ // comes from the cross-process setTransactionState call to
+ // SF. This MUST be an ITransactionCompletedListener. We
+ // keep it as an IBinder due to consistency reasons: if we
+ // interface_cast at the IPC boundary when reading a Parcel,
+ // we get pointers that compare unequal in the SF process.
+ interface_cast<ITransactionCompletedListener>(listenerStats.listener)
+ ->onTransactionCompleted(listenerStats);
+ if (transactionStatsDeque.empty()) {
+ listener->unlinkToDeath(mDeathRecipient);
+ completedTransactionsItr =
+ mCompletedTransactions.erase(completedTransactionsItr);
+ } else {
+ completedTransactionsItr++;
+ }
+ } else {
+ completedTransactionsItr =
+ mCompletedTransactions.erase(completedTransactionsItr);
+ }
+ } else {
+ completedTransactionsItr++;
+ }
+ }
+
+ if (mPresentFence) {
+ mPresentFence.clear();
+ }
+}
+
+// -----------------------------------------------------------------------
+
+CallbackHandle::CallbackHandle(const sp<IBinder>& transactionListener,
+ const std::vector<CallbackId>& ids, const sp<IBinder>& sc)
+ : listener(transactionListener), callbackIds(ids), surfaceControl(sc) {}
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/TransactionCompletedThread.h b/services/surfaceflinger/TransactionCallbackInvoker.h
similarity index 80%
rename from services/surfaceflinger/TransactionCompletedThread.h
rename to services/surfaceflinger/TransactionCallbackInvoker.h
index f50147a..184b151 100644
--- a/services/surfaceflinger/TransactionCompletedThread.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -50,13 +50,12 @@
nsecs_t refreshStartTime = 0;
nsecs_t dequeueReadyTime = 0;
uint64_t frameNumber = 0;
+ uint64_t previousBufferId = 0;
};
-class TransactionCompletedThread {
+class TransactionCallbackInvoker {
public:
- ~TransactionCompletedThread();
-
- void run();
+ ~TransactionCallbackInvoker();
// Adds listener and callbackIds in case there are no SurfaceControls that are supposed
// to be included in the callback. This functions should be call before attempting to register
@@ -66,14 +65,17 @@
// It is safe to send a callback if the Transaction doesn't have any Pending callback handles.
status_t endRegistration(const ListenerCallbacks& listenerCallbacks);
- // Informs the TransactionCompletedThread that there is a Transaction with a CallbackHandle
+ // Informs the TransactionCallbackInvoker that there is a Transaction with a CallbackHandle
// that needs to be latched and presented this frame. This function should be called once the
- // layer has received the CallbackHandle so the TransactionCompletedThread knows not to send
+ // layer has received the CallbackHandle so the TransactionCallbackInvoker knows not to send
// a callback for that Listener/Transaction pair until that CallbackHandle has been latched and
// presented.
status_t registerPendingCallbackHandle(const sp<CallbackHandle>& handle);
- // Notifies the TransactionCompletedThread that a pending CallbackHandle has been presented.
- status_t finalizePendingCallbackHandles(const std::deque<sp<CallbackHandle>>& handles);
+ // Notifies the TransactionCallbackInvoker that a pending CallbackHandle has been presented.
+ status_t finalizePendingCallbackHandles(const std::deque<sp<CallbackHandle>>& handles,
+ const std::vector<JankData>& jankData);
+ status_t finalizeOnCommitCallbackHandles(const std::deque<sp<CallbackHandle>>& handles,
+ std::deque<sp<CallbackHandle>>& outRemainingHandles);
// Adds the Transaction CallbackHandle from a layer that does not need to be relatched and
// presented this frame.
@@ -84,7 +86,6 @@
void sendCallbacks();
private:
- void threadMain();
bool isRegisteringTransaction(const sp<IBinder>& transactionListener,
const std::vector<CallbackId>& callbackIds) REQUIRES(mMutex);
@@ -93,9 +94,13 @@
const std::vector<CallbackId>& callbackIds,
TransactionStats** outTransactionStats) REQUIRES(mMutex);
- status_t addCallbackHandle(const sp<CallbackHandle>& handle) REQUIRES(mMutex);
+ status_t addCallbackHandle(const sp<CallbackHandle>& handle,
+ const std::vector<JankData>& jankData) REQUIRES(mMutex);
- class ThreadDeathRecipient : public IBinder::DeathRecipient {
+ status_t finalizeCallbackHandle(const sp<CallbackHandle>& handle,
+ const std::vector<JankData>& jankData) REQUIRES(mMutex);
+
+ class CallbackDeathRecipient : public IBinder::DeathRecipient {
public:
// This function is a no-op. isBinderAlive needs a linked DeathRecipient to work.
// Death recipients needs a binderDied function.
@@ -104,12 +109,8 @@
// sendObituary is only called if linkToDeath was called with a DeathRecipient.)
void binderDied(const wp<IBinder>& /*who*/) override {}
};
- sp<ThreadDeathRecipient> mDeathRecipient;
-
- // Protects the creation and destruction of mThread
- std::mutex mThreadMutex;
-
- std::thread mThread GUARDED_BY(mThreadMutex);
+ sp<CallbackDeathRecipient> mDeathRecipient =
+ new CallbackDeathRecipient();
std::mutex mMutex;
std::condition_variable_any mConditionVariable;
@@ -126,9 +127,6 @@
std::unordered_map<sp<IBinder>, std::deque<TransactionStats>, IListenerHash>
mCompletedTransactions GUARDED_BY(mMutex);
- bool mRunning GUARDED_BY(mMutex) = false;
- bool mKeepRunning GUARDED_BY(mMutex) = true;
-
sp<Fence> mPresentFence GUARDED_BY(mMutex);
};
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
deleted file mode 100644
index ca24493..0000000
--- a/services/surfaceflinger/TransactionCompletedThread.cpp
+++ /dev/null
@@ -1,371 +0,0 @@
-/*
- * Copyright 2018 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.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-//#define LOG_NDEBUG 0
-#undef LOG_TAG
-#define LOG_TAG "TransactionCompletedThread"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "TransactionCompletedThread.h"
-
-#include <cinttypes>
-
-#include <binder/IInterface.h>
-#include <utils/RefBase.h>
-
-namespace android {
-
-// Returns 0 if they are equal
-// <0 if the first id that doesn't match is lower in c2 or all ids match but c2 is shorter
-// >0 if the first id that doesn't match is greater in c2 or all ids match but c2 is longer
-//
-// See CallbackIdsHash for a explaniation of why this works
-static int compareCallbackIds(const std::vector<CallbackId>& c1,
- const std::vector<CallbackId>& c2) {
- if (c1.empty()) {
- return !c2.empty();
- }
- return c1.front() - c2.front();
-}
-
-TransactionCompletedThread::~TransactionCompletedThread() {
- std::lock_guard lockThread(mThreadMutex);
-
- {
- std::lock_guard lock(mMutex);
- mKeepRunning = false;
- mConditionVariable.notify_all();
- }
-
- if (mThread.joinable()) {
- mThread.join();
- }
-
- {
- std::lock_guard lock(mMutex);
- for (const auto& [listener, transactionStats] : mCompletedTransactions) {
- listener->unlinkToDeath(mDeathRecipient);
- }
- }
-}
-
-void TransactionCompletedThread::run() {
- std::lock_guard lock(mMutex);
- if (mRunning || !mKeepRunning) {
- return;
- }
- mDeathRecipient = new ThreadDeathRecipient();
- mRunning = true;
-
- std::lock_guard lockThread(mThreadMutex);
- mThread = std::thread(&TransactionCompletedThread::threadMain, this);
-}
-
-status_t TransactionCompletedThread::startRegistration(const ListenerCallbacks& listenerCallbacks) {
- // begin running if not already running
- run();
- std::lock_guard lock(mMutex);
- if (!mRunning) {
- ALOGE("cannot add callback because the callback thread isn't running");
- return BAD_VALUE;
- }
-
- auto [itr, inserted] = mRegisteringTransactions.insert(listenerCallbacks);
- auto& [listener, callbackIds] = listenerCallbacks;
-
- if (inserted) {
- if (mCompletedTransactions.count(listener) == 0) {
- status_t err = listener->linkToDeath(mDeathRecipient);
- if (err != NO_ERROR) {
- ALOGE("cannot add callback because linkToDeath failed, err: %d", err);
- return err;
- }
- }
- auto& transactionStatsDeque = mCompletedTransactions[listener];
- transactionStatsDeque.emplace_back(callbackIds);
- }
-
- return NO_ERROR;
-}
-
-status_t TransactionCompletedThread::endRegistration(const ListenerCallbacks& listenerCallbacks) {
- std::lock_guard lock(mMutex);
- if (!mRunning) {
- ALOGE("cannot add callback because the callback thread isn't running");
- return BAD_VALUE;
- }
-
- auto itr = mRegisteringTransactions.find(listenerCallbacks);
- if (itr == mRegisteringTransactions.end()) {
- ALOGE("cannot end a registration that does not exist");
- return BAD_VALUE;
- }
-
- mRegisteringTransactions.erase(itr);
-
- return NO_ERROR;
-}
-
-bool TransactionCompletedThread::isRegisteringTransaction(
- const sp<IBinder>& transactionListener, const std::vector<CallbackId>& callbackIds) {
- ListenerCallbacks listenerCallbacks(transactionListener, callbackIds);
-
- auto itr = mRegisteringTransactions.find(listenerCallbacks);
- return itr != mRegisteringTransactions.end();
-}
-
-status_t TransactionCompletedThread::registerPendingCallbackHandle(
- const sp<CallbackHandle>& handle) {
- std::lock_guard lock(mMutex);
- if (!mRunning) {
- ALOGE("cannot register callback handle because the callback thread isn't running");
- return BAD_VALUE;
- }
-
- // If we can't find the transaction stats something has gone wrong. The client should call
- // startRegistration before trying to register a pending callback handle.
- TransactionStats* transactionStats;
- status_t err = findTransactionStats(handle->listener, handle->callbackIds, &transactionStats);
- if (err != NO_ERROR) {
- ALOGE("cannot find transaction stats");
- return err;
- }
-
- mPendingTransactions[handle->listener][handle->callbackIds]++;
- return NO_ERROR;
-}
-
-status_t TransactionCompletedThread::finalizePendingCallbackHandles(
- const std::deque<sp<CallbackHandle>>& handles) {
- if (handles.empty()) {
- return NO_ERROR;
- }
- std::lock_guard lock(mMutex);
- if (!mRunning) {
- ALOGE("cannot add presented callback handle because the callback thread isn't running");
- return BAD_VALUE;
- }
-
- for (const auto& handle : handles) {
- auto listener = mPendingTransactions.find(handle->listener);
- if (listener != mPendingTransactions.end()) {
- auto& pendingCallbacks = listener->second;
- auto pendingCallback = pendingCallbacks.find(handle->callbackIds);
-
- if (pendingCallback != pendingCallbacks.end()) {
- auto& pendingCount = pendingCallback->second;
-
- // Decrease the pending count for this listener
- if (--pendingCount == 0) {
- pendingCallbacks.erase(pendingCallback);
- }
- } else {
- ALOGW("there are more latched callbacks than there were registered callbacks");
- }
- if (listener->second.size() == 0) {
- mPendingTransactions.erase(listener);
- }
- } else {
- ALOGW("cannot find listener in mPendingTransactions");
- }
-
- status_t err = addCallbackHandle(handle);
- if (err != NO_ERROR) {
- ALOGE("could not add callback handle");
- return err;
- }
- }
-
- return NO_ERROR;
-}
-
-status_t TransactionCompletedThread::registerUnpresentedCallbackHandle(
- const sp<CallbackHandle>& handle) {
- std::lock_guard lock(mMutex);
- if (!mRunning) {
- ALOGE("cannot add unpresented callback handle because the callback thread isn't running");
- return BAD_VALUE;
- }
-
- return addCallbackHandle(handle);
-}
-
-status_t TransactionCompletedThread::findTransactionStats(
- const sp<IBinder>& listener, const std::vector<CallbackId>& callbackIds,
- TransactionStats** outTransactionStats) {
- auto& transactionStatsDeque = mCompletedTransactions[listener];
-
- // Search back to front because the most recent transactions are at the back of the deque
- auto itr = transactionStatsDeque.rbegin();
- for (; itr != transactionStatsDeque.rend(); itr++) {
- if (compareCallbackIds(itr->callbackIds, callbackIds) == 0) {
- *outTransactionStats = &(*itr);
- return NO_ERROR;
- }
- }
-
- ALOGE("could not find transaction stats");
- return BAD_VALUE;
-}
-
-status_t TransactionCompletedThread::addCallbackHandle(const sp<CallbackHandle>& handle) {
- // If we can't find the transaction stats something has gone wrong. The client should call
- // startRegistration before trying to add a callback handle.
- TransactionStats* transactionStats;
- status_t err = findTransactionStats(handle->listener, handle->callbackIds, &transactionStats);
- if (err != NO_ERROR) {
- return err;
- }
-
- transactionStats->latchTime = handle->latchTime;
- // If the layer has already been destroyed, don't add the SurfaceControl to the callback.
- // The client side keeps a sp<> to the SurfaceControl so if the SurfaceControl has been
- // destroyed the client side is dead and there won't be anyone to send the callback to.
- sp<IBinder> surfaceControl = handle->surfaceControl.promote();
- if (surfaceControl) {
- FrameEventHistoryStats eventStats(handle->frameNumber,
- handle->gpuCompositionDoneFence->getSnapshot().fence,
- handle->compositorTiming, handle->refreshStartTime,
- handle->dequeueReadyTime);
- transactionStats->surfaceStats.emplace_back(surfaceControl, handle->acquireTime,
- handle->previousReleaseFence,
- handle->transformHint, eventStats);
- }
- return NO_ERROR;
-}
-
-void TransactionCompletedThread::addPresentFence(const sp<Fence>& presentFence) {
- std::lock_guard<std::mutex> lock(mMutex);
- mPresentFence = presentFence;
-}
-
-void TransactionCompletedThread::sendCallbacks() {
- std::lock_guard lock(mMutex);
- if (mRunning) {
- mConditionVariable.notify_all();
- }
-}
-
-void TransactionCompletedThread::threadMain() {
- std::lock_guard lock(mMutex);
-
- while (mKeepRunning) {
- mConditionVariable.wait(mMutex);
- std::vector<ListenerStats> completedListenerStats;
-
- // For each listener
- auto completedTransactionsItr = mCompletedTransactions.begin();
- while (completedTransactionsItr != mCompletedTransactions.end()) {
- auto& [listener, transactionStatsDeque] = *completedTransactionsItr;
- ListenerStats listenerStats;
- listenerStats.listener = listener;
-
- // For each transaction
- auto transactionStatsItr = transactionStatsDeque.begin();
- while (transactionStatsItr != transactionStatsDeque.end()) {
- auto& transactionStats = *transactionStatsItr;
-
- // If this transaction is still registering, it is not safe to send a callback
- // because there could be surface controls that haven't been added to
- // transaction stats or mPendingTransactions.
- if (isRegisteringTransaction(listener, transactionStats.callbackIds)) {
- break;
- }
-
- // If we are still waiting on the callback handles for this transaction, stop
- // here because all transaction callbacks for the same listener must come in order
- auto pendingTransactions = mPendingTransactions.find(listener);
- if (pendingTransactions != mPendingTransactions.end() &&
- pendingTransactions->second.count(transactionStats.callbackIds) != 0) {
- break;
- }
-
- // If the transaction has been latched
- if (transactionStats.latchTime >= 0) {
- if (!mPresentFence) {
- break;
- }
- transactionStats.presentFence = mPresentFence;
- }
-
- // Remove the transaction from completed to the callback
- listenerStats.transactionStats.push_back(std::move(transactionStats));
- transactionStatsItr = transactionStatsDeque.erase(transactionStatsItr);
- }
- // If the listener has completed transactions
- if (!listenerStats.transactionStats.empty()) {
- // If the listener is still alive
- if (listener->isBinderAlive()) {
- // Send callback. The listener stored in listenerStats
- // comes from the cross-process setTransactionState call to
- // SF. This MUST be an ITransactionCompletedListener. We
- // keep it as an IBinder due to consistency reasons: if we
- // interface_cast at the IPC boundary when reading a Parcel,
- // we get pointers that compare unequal in the SF process.
- interface_cast<ITransactionCompletedListener>(listenerStats.listener)
- ->onTransactionCompleted(listenerStats);
- if (transactionStatsDeque.empty()) {
- listener->unlinkToDeath(mDeathRecipient);
- completedTransactionsItr =
- mCompletedTransactions.erase(completedTransactionsItr);
- } else {
- completedTransactionsItr++;
- }
- } else {
- completedTransactionsItr =
- mCompletedTransactions.erase(completedTransactionsItr);
- }
- } else {
- completedTransactionsItr++;
- }
-
- completedListenerStats.push_back(std::move(listenerStats));
- }
-
- if (mPresentFence) {
- mPresentFence.clear();
- }
-
- // If everyone else has dropped their reference to a layer and its listener is dead,
- // we are about to cause the layer to be deleted. If this happens at the wrong time and
- // we are holding mMutex, we will cause a deadlock.
- //
- // The deadlock happens because this thread is holding on to mMutex and when we delete
- // the layer, it grabs SF's mStateLock. A different SF binder thread grabs mStateLock,
- // then call's TransactionCompletedThread::run() which tries to grab mMutex.
- //
- // To avoid this deadlock, we need to unlock mMutex when dropping our last reference to
- // to the layer.
- mMutex.unlock();
- completedListenerStats.clear();
- mMutex.lock();
- }
-}
-
-// -----------------------------------------------------------------------
-
-CallbackHandle::CallbackHandle(const sp<IBinder>& transactionListener,
- const std::vector<CallbackId>& ids, const sp<IBinder>& sc)
- : listener(transactionListener), callbackIds(ids), surfaceControl(sc) {}
-
-} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/layerproto/Android.bp b/services/surfaceflinger/layerproto/Android.bp
index 7f8da5a..c8a2b5e 100644
--- a/services/surfaceflinger/layerproto/Android.bp
+++ b/services/surfaceflinger/layerproto/Android.bp
@@ -7,7 +7,7 @@
default_applicable_licenses: ["frameworks_native_license"],
}
-cc_library_shared {
+cc_library {
name: "liblayers_proto",
export_include_dirs: ["include"],
@@ -42,7 +42,6 @@
"-Wno-old-style-cast",
"-Wno-undef",
],
-
}
java_library_static {
diff --git a/services/surfaceflinger/layerproto/LayerProtoParser.cpp b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
index 8fce0c9..aef670d 100644
--- a/services/surfaceflinger/layerproto/LayerProtoParser.cpp
+++ b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
@@ -115,6 +115,7 @@
}
layer.cornerRadiusCrop = generateFloatRect(layerProto.corner_radius_crop());
layer.shadowRadius = layerProto.shadow_radius();
+ layer.ownerUid = layerProto.owner_uid();
return layer;
}
@@ -276,7 +277,7 @@
std::string LayerProtoParser::Layer::to_string() const {
std::string result;
- StringAppendF(&result, "+ %s (%s)\n", type.c_str(), name.c_str());
+ StringAppendF(&result, "+ %s (%s) uid=%d\n", type.c_str(), name.c_str(), ownerUid);
result.append(transparentRegion.to_string("TransparentRegion").c_str());
result.append(visibleRegion.to_string("VisibleRegion").c_str());
result.append(damageRegion.to_string("SurfaceDamageRegion").c_str());
diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
index 52b9165..c48354f 100644
--- a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
+++ b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
@@ -114,6 +114,7 @@
LayerMetadata metadata;
LayerProtoParser::FloatRect cornerRadiusCrop;
float shadowRadius;
+ uid_t ownerUid;
std::string to_string() const;
};
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index 7f1f542..9f25674 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -123,6 +123,11 @@
bool is_relative_of = 51;
// Layer's background blur radius in pixels.
int32 background_blur_radius = 52;
+
+ uint32 owner_uid = 53;
+
+ // Regions of a layer, where blur should be applied.
+ repeated BlurRegion blur_regions = 54;
}
message PositionProto {
@@ -191,20 +196,34 @@
uint32 surface_inset = 5;
bool visible = 6;
- bool can_receive_keys = 7;
- bool has_focus = 8;
+ bool can_receive_keys = 7 [deprecated=true];
+ bool focusable = 8;
bool has_wallpaper = 9;
float global_scale_factor = 10;
- float window_x_scale = 11;
- float window_y_scale = 12;
+ float window_x_scale = 11 [deprecated=true];
+ float window_y_scale = 12 [deprecated=true];
uint32 crop_layer_id = 13;
bool replace_touchable_region_with_crop = 14;
RectProto touchable_region_crop = 15;
+ TransformProto transform = 16;
}
message ColorTransformProto {
// This will be a 4x4 matrix of float values
repeated float val = 1;
}
+
+message BlurRegion {
+ uint32 blur_radius = 1;
+ uint32 corner_radius_tl = 2;
+ uint32 corner_radius_tr = 3;
+ uint32 corner_radius_bl = 4;
+ float corner_radius_br = 5;
+ float alpha = 6;
+ int32 left = 7;
+ int32 top = 8;
+ int32 right = 9;
+ int32 bottom = 10;
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/layerproto/layerstrace.proto b/services/surfaceflinger/layerproto/layerstrace.proto
index acf621e..990f3cf 100644
--- a/services/surfaceflinger/layerproto/layerstrace.proto
+++ b/services/surfaceflinger/layerproto/layerstrace.proto
@@ -42,7 +42,7 @@
/* one window manager trace entry. */
message LayersTraceProto {
/* required: elapsed realtime in nanos since boot of when this entry was logged */
- optional fixed64 elapsed_realtime_nanos = 1;
+ optional sfixed64 elapsed_realtime_nanos = 1;
/* where the trace originated */
optional string where = 2;
@@ -56,5 +56,5 @@
optional bool excludes_composition_state = 5;
/* Number of missed entries since the last entry was recorded. */
- optional int32 missed_entries = 6;
+ optional uint32 missed_entries = 6;
}
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index 2b8424c..9686523 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -89,13 +89,47 @@
// binder threads to 4.
ProcessState::self()->setThreadPoolMaxThreadCount(4);
+ // The binder threadpool we start will inherit sched policy and priority
+ // of (this) creating thread. We want the binder thread pool to have
+ // SCHED_FIFO policy and priority 1 (lowest RT priority)
+ // Once the pool is created we reset this thread's priority back to
+ // original.
+ int newPriority = 0;
+ int origPolicy = sched_getscheduler(0);
+ struct sched_param origSchedParam;
+
+ int errorInPriorityModification = sched_getparam(0, &origSchedParam);
+ if (errorInPriorityModification == 0) {
+ int policy = SCHED_FIFO;
+ newPriority = sched_get_priority_min(policy);
+
+ struct sched_param param;
+ param.sched_priority = newPriority;
+
+ errorInPriorityModification = sched_setscheduler(0, policy, ¶m);
+ }
+
// start the thread pool
sp<ProcessState> ps(ProcessState::self());
ps->startThreadPool();
+ // Reset current thread's policy and priority
+ if (errorInPriorityModification == 0) {
+ errorInPriorityModification = sched_setscheduler(0, origPolicy, &origSchedParam);
+ } else {
+ ALOGE("Failed to set SurfaceFlinger binder threadpool priority to SCHED_FIFO");
+ }
+
// instantiate surfaceflinger
sp<SurfaceFlinger> flinger = surfaceflinger::createSurfaceFlinger();
+ // Set the minimum policy of surfaceflinger node to be SCHED_FIFO.
+ // So any thread with policy/priority lower than {SCHED_FIFO, 1}, will run
+ // at least with SCHED_FIFO policy and priority 1.
+ if (errorInPriorityModification == 0) {
+ flinger->setMinSchedulerPolicy(SCHED_FIFO, newPriority);
+ }
+
setpriority(PRIO_PROCESS, 0, PRIORITY_URGENT_DISPLAY);
set_sched_policy(0, SP_FOREGROUND);
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 7666f7f..ee5542d 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -435,3 +435,31 @@
access: Readonly
prop_name: "ro.surface_flinger.display_update_imminent_timeout_ms"
}
+
+
+# Updates the DeviceProductInfo when a hoplug reconnect event is processed
+prop {
+ api_name: "update_device_product_info_on_hotplug_reconnect"
+ type: Boolean
+ scope: Public
+ access: Readonly
+ prop_name: "ro.surface_flinger.update_device_product_info_on_hotplug_reconnect"
+}
+
+# Enables the frame rate override feature
+prop {
+ api_name: "enable_frame_rate_override"
+ type: Boolean
+ scope: Public
+ access: Readonly
+ prop_name: "ro.surface_flinger.enable_frame_rate_override"
+}
+
+# Enables Layer Caching
+prop {
+ api_name: "enable_layer_caching"
+ type: Boolean
+ scope: Public
+ access: Readonly
+ prop_name: "ro.surface_flinger.enable_layer_caching"
+}
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index ba60a7d..47e14f6 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -41,6 +41,14 @@
prop_name: "ro.surface_flinger.display_update_imminent_timeout_ms"
}
prop {
+ api_name: "enable_frame_rate_override"
+ prop_name: "ro.surface_flinger.enable_frame_rate_override"
+ }
+ prop {
+ api_name: "enable_layer_caching"
+ prop_name: "ro.surface_flinger.enable_layer_caching"
+ }
+ prop {
api_name: "enable_protected_contents"
prop_name: "ro.surface_flinger.protected_contents"
}
@@ -124,6 +132,10 @@
prop_name: "ro.surface_flinger.supports_background_blur"
}
prop {
+ api_name: "update_device_product_info_on_hotplug_reconnect"
+ prop_name: "ro.surface_flinger.update_device_product_info_on_hotplug_reconnect"
+ }
+ prop {
api_name: "use_color_management"
prop_name: "ro.surface_flinger.use_color_management"
}
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 15744a1..b96725f 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -35,13 +35,17 @@
"InvalidHandles_test.cpp",
"LayerCallback_test.cpp",
"LayerRenderTypeTransaction_test.cpp",
+ "LayerState_test.cpp",
"LayerTransaction_test.cpp",
"LayerTypeAndRenderTypeTransaction_test.cpp",
"LayerTypeTransaction_test.cpp",
"LayerUpdate_test.cpp",
"MirrorLayer_test.cpp",
"MultiDisplayLayerBounds_test.cpp",
+ "RefreshRateOverlay_test.cpp",
"RelativeZ_test.cpp",
+ "ReleaseBufferCallback_test.cpp",
+ "ScreenCapture_test.cpp",
"SetFrameRate_test.cpp",
"SetGeometry_test.cpp",
"Stress_test.cpp",
@@ -51,18 +55,19 @@
data: ["SurfaceFlinger_test.filter"],
static_libs: [
"libtrace_proto",
+ "liblayers_proto",
+ "android.hardware.graphics.composer@2.1",
],
shared_libs: [
"android.hardware.graphics.common-V2-ndk_platform",
"android.hardware.graphics.common@1.2",
- "android.hardware.graphics.composer@2.1",
"libandroid",
+ "libbase",
"libbinder",
"libcutils",
"libEGL",
"libGLESv2",
"libgui",
- "liblayers_proto",
"liblog",
"libnativewindow",
"libprotobuf-cpp-full",
diff --git a/services/surfaceflinger/tests/BufferGenerator.cpp b/services/surfaceflinger/tests/BufferGenerator.cpp
index 4868c12..47a150d 100644
--- a/services/surfaceflinger/tests/BufferGenerator.cpp
+++ b/services/surfaceflinger/tests/BufferGenerator.cpp
@@ -70,7 +70,7 @@
consumer->setDefaultBufferSize(width, height);
consumer->setDefaultBufferFormat(format);
- mBufferItemConsumer = new BufferItemConsumer(consumer, 0);
+ mBufferItemConsumer = new BufferItemConsumer(consumer, GraphicBuffer::USAGE_HW_TEXTURE);
mListener = new BufferListener(consumer, callback);
mBufferItemConsumer->setFrameAvailableListener(mListener);
@@ -296,12 +296,12 @@
BufferGenerator::BufferGenerator()
: mSurfaceManager(new SurfaceManager), mEglManager(new EglManager), mProgram(new Program) {
- const float width = 1000.0;
- const float height = 1000.0;
+ mBufferSize.set(1000.0, 1000.0);
auto setBufferWithContext =
std::bind(setBuffer, std::placeholders::_1, std::placeholders::_2, this);
- mSurfaceManager->initialize(width, height, HAL_PIXEL_FORMAT_RGBA_8888, setBufferWithContext);
+ mSurfaceManager->initialize(mBufferSize.width, mBufferSize.height, HAL_PIXEL_FORMAT_RGBA_8888,
+ setBufferWithContext);
if (!mEglManager->initialize(mSurfaceManager->getSurface())) return;
@@ -309,7 +309,9 @@
if (!mProgram->initialize(VERTEX_SHADER, FRAGMENT_SHADER)) return;
mProgram->use();
- mProgram->bindVec4(0, vec4{width, height, 1.0f / width, 1.0f / height});
+ mProgram->bindVec4(0,
+ vec4{mBufferSize.width, mBufferSize.height, 1.0f / mBufferSize.width,
+ 1.0f / mBufferSize.height});
mProgram->bindVec3(2, &SPHERICAL_HARMONICS[0], 4);
glEnableVertexAttribArray(0);
@@ -372,6 +374,10 @@
return NO_ERROR;
}
+ui::Size BufferGenerator::getSize() {
+ return mBufferSize;
+}
+
// static
void BufferGenerator::setBuffer(const sp<GraphicBuffer>& buffer, int32_t fence,
void* bufferGenerator) {
diff --git a/services/surfaceflinger/tests/BufferGenerator.h b/services/surfaceflinger/tests/BufferGenerator.h
index a3ffe86..f7d548b 100644
--- a/services/surfaceflinger/tests/BufferGenerator.h
+++ b/services/surfaceflinger/tests/BufferGenerator.h
@@ -37,6 +37,7 @@
/* Static callback that sets the fence on a particular instance */
static void setBuffer(const sp<GraphicBuffer>& buffer, int32_t fence, void* fenceGenerator);
+ ui::Size getSize();
private:
bool mInitialized = false;
@@ -53,6 +54,7 @@
using Epoch = std::chrono::time_point<std::chrono::steady_clock>;
Epoch mEpoch = std::chrono::steady_clock::now();
+ ui::Size mBufferSize;
};
} // namespace android
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index c136708..fa3f0e7 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -1,3 +1,23 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
#include <gtest/gtest.h>
#include <gui/ISurfaceComposer.h>
#include <gui/LayerDebugInfo.h>
@@ -5,10 +25,11 @@
#include <gui/SurfaceComposerClient.h>
#include <private/android_filesystem_config.h>
#include <private/gui/ComposerService.h>
-#include <ui/DisplayConfig.h>
+#include <ui/DisplayMode.h>
+#include <ui/DynamicDisplayInfo.h>
#include <utils/String8.h>
-
#include <functional>
+#include "utils/ScreenshotUtils.h"
namespace android {
@@ -18,7 +39,6 @@
namespace {
const String8 DISPLAY_NAME("Credentials Display Test");
const String8 SURFACE_NAME("Test Surface Name");
-const float FRAME_SCALE = 1.0f;
} // namespace
/**
@@ -62,14 +82,13 @@
mDisplay = SurfaceComposerClient::getInternalDisplayToken();
ASSERT_FALSE(mDisplay == nullptr);
- DisplayConfig config;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(mDisplay, &config));
+ ui::DisplayMode mode;
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(mDisplay, &mode));
// Background surface
- mBGSurfaceControl =
- mComposerClient->createSurface(SURFACE_NAME, config.resolution.getWidth(),
- config.resolution.getHeight(),
- PIXEL_FORMAT_RGBA_8888, 0);
+ mBGSurfaceControl = mComposerClient->createSurface(SURFACE_NAME, mode.resolution.getWidth(),
+ mode.resolution.getHeight(),
+ PIXEL_FORMAT_RGBA_8888, 0);
ASSERT_TRUE(mBGSurfaceControl != nullptr);
ASSERT_TRUE(mBGSurfaceControl->isValid());
@@ -79,26 +98,6 @@
t.setLayer(mBGSurfaceControl, INT_MAX - 3).show(mBGSurfaceControl).apply());
}
- void setupVirtualDisplay() {
- mVirtualDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, true);
- const ssize_t displayWidth = 100;
- const ssize_t displayHeight = 100;
-
- // Background surface
- mVirtualSurfaceControl =
- mComposerClient->createSurface(SURFACE_NAME, displayWidth, displayHeight,
- PIXEL_FORMAT_RGBA_8888, 0);
- ASSERT_TRUE(mVirtualSurfaceControl != nullptr);
- ASSERT_TRUE(mVirtualSurfaceControl->isValid());
-
- Transaction t;
- t.setDisplayLayerStack(mVirtualDisplay, 0);
- ASSERT_EQ(NO_ERROR,
- t.setLayer(mVirtualSurfaceControl, INT_MAX - 3)
- .show(mVirtualSurfaceControl)
- .apply());
- }
-
/**
* Sets UID to imitate Graphic's process.
*/
@@ -146,6 +145,10 @@
// Check as a non-supported user.
setBinUID();
ASSERT_EQ(unprivilegedValue, condition());
+
+ // Check as shell since shell has some additional permissions
+ seteuid(AID_SHELL);
+ ASSERT_EQ(unprivilegedValue, condition());
}
};
@@ -182,23 +185,19 @@
const auto display = SurfaceComposerClient::getInternalDisplayToken();
ASSERT_TRUE(display != nullptr);
- DisplayConfig config;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+ ui::DisplayMode mode;
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
- Vector<DisplayConfig> configs;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
-
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveConfig(display));
-
- ASSERT_NE(static_cast<ui::ColorMode>(BAD_VALUE),
- SurfaceComposerClient::getActiveColorMode(display));
+ Vector<ui::DisplayMode> modes;
+ ui::DynamicDisplayInfo info;
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info));
}
-TEST_F(CredentialsTest, GetDisplayColorModesTest) {
+TEST_F(CredentialsTest, GetDynamicDisplayInfoTest) {
const auto display = SurfaceComposerClient::getInternalDisplayToken();
std::function<status_t()> condition = [=]() {
- Vector<ui::ColorMode> outColorModes;
- return SurfaceComposerClient::getDisplayColorModes(display, &outColorModes);
+ ui::DynamicDisplayInfo info;
+ return SurfaceComposerClient::getDynamicDisplayInfo(display, &info);
};
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, NO_ERROR));
}
@@ -214,22 +213,23 @@
TEST_F(CredentialsTest, SetDesiredDisplayConfigsTest) {
const auto display = SurfaceComposerClient::getInternalDisplayToken();
- int32_t defaultConfig;
+ ui::DisplayModeId defaultMode;
+ bool allowGroupSwitching;
float primaryFpsMin;
float primaryFpsMax;
float appRequestFpsMin;
float appRequestFpsMax;
status_t res =
- SurfaceComposerClient::getDesiredDisplayConfigSpecs(display, &defaultConfig,
- &primaryFpsMin, &primaryFpsMax,
- &appRequestFpsMin,
- &appRequestFpsMax);
+ SurfaceComposerClient::getDesiredDisplayModeSpecs(display, &defaultMode,
+ &allowGroupSwitching, &primaryFpsMin,
+ &primaryFpsMax, &appRequestFpsMin,
+ &appRequestFpsMax);
ASSERT_EQ(res, NO_ERROR);
std::function<status_t()> condition = [=]() {
- return SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, defaultConfig,
- primaryFpsMin, primaryFpsMax,
- appRequestFpsMin,
- appRequestFpsMax);
+ return SurfaceComposerClient::setDesiredDisplayModeSpecs(display, defaultMode,
+ allowGroupSwitching, primaryFpsMin,
+ primaryFpsMax, appRequestFpsMin,
+ appRequestFpsMax);
};
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
}
@@ -243,11 +243,31 @@
}
TEST_F(CredentialsTest, CreateDisplayTest) {
+ // Only graphics and system processes can create a secure display.
std::function<bool()> condition = [=]() {
sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, true);
return testDisplay.get() != nullptr;
};
- ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, false));
+
+ // Check with root.
+ seteuid(AID_ROOT);
+ ASSERT_FALSE(condition());
+
+ // Check as a Graphics user.
+ setGraphicsUID();
+ ASSERT_TRUE(condition());
+
+ // Check as a system user.
+ setSystemUID();
+ ASSERT_TRUE(condition());
+
+ // Check as a non-supported user.
+ setBinUID();
+ ASSERT_FALSE(condition());
+
+ // Check as shell since shell has some additional permissions
+ seteuid(AID_SHELL);
+ ASSERT_FALSE(condition());
condition = [=]() {
sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false);
@@ -260,9 +280,10 @@
const auto display = SurfaceComposerClient::getInternalDisplayToken();
std::function<status_t()> condition = [=]() {
sp<GraphicBuffer> outBuffer;
- return ScreenshotClient::capture(display, ui::Dataspace::V0_SRGB,
- ui::PixelFormat::RGBA_8888, Rect(), 0 /*reqWidth*/,
- 0 /*reqHeight*/, false, ui::ROTATION_0, &outBuffer);
+ DisplayCaptureArgs captureArgs;
+ captureArgs.displayToken = display;
+ ScreenCaptureResults captureResults;
+ return ScreenCapture::captureDisplay(captureArgs, captureResults);
};
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
}
@@ -271,10 +292,12 @@
setupBackgroundSurface();
sp<GraphicBuffer> outBuffer;
std::function<status_t()> condition = [=]() {
- sp<GraphicBuffer> outBuffer;
- return ScreenshotClient::captureLayers(mBGSurfaceControl->getHandle(),
- ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888,
- Rect(0, 0, 1, 1), FRAME_SCALE, &outBuffer);
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = mBGSurfaceControl->getHandle();
+ captureArgs.sourceCrop = {0, 0, 1, 1};
+
+ ScreenCaptureResults captureResults;
+ return ScreenCapture::captureLayers(captureArgs, captureResults);
};
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
}
@@ -282,25 +305,6 @@
/**
* The following tests are for methods accessible directly through SurfaceFlinger.
*/
-
-/**
- * An app can pass a buffer queue to the media server and ask the media server to decode a DRM video
- * to that buffer queue. The media server is the buffer producer in this case. Because the app may create
- * its own buffer queue and act as the buffer consumer, the media server wants to be careful to avoid
- * sending decoded video frames to the app. This is where authenticateSurfaceTexture call comes in, to check
- * the consumer of a buffer queue is SurfaceFlinger.
- */
-TEST_F(CredentialsTest, AuthenticateSurfaceTextureTest) {
- setupBackgroundSurface();
- sp<IGraphicBufferProducer> producer =
- mBGSurfaceControl->getSurface()->getIGraphicBufferProducer();
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-
- std::function<bool()> condition = [=]() { return sf->authenticateSurfaceTexture(producer); };
- // Anyone should be able to check if the consumer of the buffer queue is SF.
- ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, true));
-}
-
TEST_F(CredentialsTest, GetLayerDebugInfo) {
setupBackgroundSurface();
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
@@ -329,8 +333,9 @@
status_t error = SurfaceComposerClient::isWideColorDisplay(display, &result);
ASSERT_EQ(NO_ERROR, error);
bool hasWideColorMode = false;
- Vector<ColorMode> colorModes;
- SurfaceComposerClient::getDisplayColorModes(display, &colorModes);
+ ui::DynamicDisplayInfo info;
+ SurfaceComposerClient::getDynamicDisplayInfo(display, &info);
+ const auto& colorModes = info.supportedColorModes;
for (ColorMode colorMode : colorModes) {
switch (colorMode) {
case ColorMode::DISPLAY_P3:
@@ -358,7 +363,9 @@
TEST_F(CredentialsTest, GetActiveColorModeBasicCorrectness) {
const auto display = SurfaceComposerClient::getInternalDisplayToken();
ASSERT_FALSE(display == nullptr);
- ColorMode colorMode = SurfaceComposerClient::getActiveColorMode(display);
+ ui::DynamicDisplayInfo info;
+ SurfaceComposerClient::getDynamicDisplayInfo(display, &info);
+ ColorMode colorMode = info.activeColorMode;
ASSERT_NE(static_cast<ColorMode>(BAD_VALUE), colorMode);
}
diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
index debfe83..2dc96b8 100644
--- a/services/surfaceflinger/tests/DisplayConfigs_test.cpp
+++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
@@ -16,14 +16,21 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
-#include <thread>
-#include "LayerTransactionTest.h"
+#include <gtest/gtest.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/SurfaceComposerClient.h>
+#include <private/gui/ComposerService.h>
+#include <ui/DisplayMode.h>
+#include <ui/DynamicDisplayInfo.h>
+#include <utils/Errors.h>
+#include <utils/Vector.h>
+
+#include "utils/TransactionUtils.h"
+
namespace android {
-using android::hardware::graphics::common::V1_1::BufferUsage;
-
::testing::Environment* const binderEnv =
::testing::AddGlobalTestEnvironment(new BinderEnvironment());
@@ -31,64 +38,115 @@
* Test class for setting display configs and passing around refresh rate ranges.
*/
class RefreshRateRangeTest : public ::testing::Test {
+private:
+ ui::DisplayModeId initialDefaultMode;
+ bool initialAllowGroupSwitching;
+ float initialPrimaryMin;
+ float initialPrimaryMax;
+ float initialAppRequestMin;
+ float initialAppRequestMax;
+
protected:
- void SetUp() override { mDisplayToken = SurfaceComposerClient::getInternalDisplayToken(); }
+ void SetUp() override {
+ mDisplayToken = SurfaceComposerClient::getInternalDisplayToken();
+ status_t res =
+ SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken,
+ &initialDefaultMode,
+ &initialAllowGroupSwitching,
+ &initialPrimaryMin,
+ &initialPrimaryMax,
+ &initialAppRequestMin,
+ &initialAppRequestMax);
+ ASSERT_EQ(res, NO_ERROR);
+ }
+
+ void TearDown() override {
+ status_t res =
+ SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, initialDefaultMode,
+ initialAllowGroupSwitching,
+ initialPrimaryMin,
+ initialPrimaryMax,
+ initialAppRequestMin,
+ initialAppRequestMax);
+ ASSERT_EQ(res, NO_ERROR);
+ }
+
+ void testSetAllowGroupSwitching(bool allowGroupSwitching);
sp<IBinder> mDisplayToken;
};
TEST_F(RefreshRateRangeTest, setAllConfigs) {
- int32_t initialDefaultConfig;
- float initialPrimaryMin;
- float initialPrimaryMax;
- float initialAppRequestMin;
- float initialAppRequestMax;
- status_t res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken,
- &initialDefaultConfig,
- &initialPrimaryMin,
- &initialPrimaryMax,
- &initialAppRequestMin,
- &initialAppRequestMax);
+ ui::DynamicDisplayInfo info;
+ status_t res = SurfaceComposerClient::getDynamicDisplayInfo(mDisplayToken, &info);
+ const auto& modes = info.supportedDisplayModes;
ASSERT_EQ(res, NO_ERROR);
+ ASSERT_GT(modes.size(), 0);
- Vector<DisplayConfig> configs;
- res = SurfaceComposerClient::getDisplayConfigs(mDisplayToken, &configs);
- ASSERT_EQ(res, NO_ERROR);
-
- for (size_t i = 0; i < configs.size(); i++) {
- res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, i,
- configs[i].refreshRate,
- configs[i].refreshRate,
- configs[i].refreshRate,
- configs[i].refreshRate);
+ for (size_t i = 0; i < modes.size(); i++) {
+ res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, modes[i].id, false,
+ modes[i].refreshRate,
+ modes[i].refreshRate,
+ modes[i].refreshRate,
+ modes[i].refreshRate);
ASSERT_EQ(res, NO_ERROR);
- int defaultConfig;
+ ui::DisplayModeId defaultConfig;
+ bool allowGroupSwitching;
float primaryRefreshRateMin;
float primaryRefreshRateMax;
float appRequestRefreshRateMin;
float appRequestRefreshRateMax;
- res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, &defaultConfig,
- &primaryRefreshRateMin,
- &primaryRefreshRateMax,
- &appRequestRefreshRateMin,
- &appRequestRefreshRateMax);
+ res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &defaultConfig,
+ &allowGroupSwitching,
+ &primaryRefreshRateMin,
+ &primaryRefreshRateMax,
+ &appRequestRefreshRateMin,
+ &appRequestRefreshRateMax);
ASSERT_EQ(res, NO_ERROR);
ASSERT_EQ(defaultConfig, i);
- ASSERT_EQ(primaryRefreshRateMin, configs[i].refreshRate);
- ASSERT_EQ(primaryRefreshRateMax, configs[i].refreshRate);
- ASSERT_EQ(appRequestRefreshRateMin, configs[i].refreshRate);
- ASSERT_EQ(appRequestRefreshRateMax, configs[i].refreshRate);
+ ASSERT_EQ(allowGroupSwitching, false);
+ ASSERT_EQ(primaryRefreshRateMin, modes[i].refreshRate);
+ ASSERT_EQ(primaryRefreshRateMax, modes[i].refreshRate);
+ ASSERT_EQ(appRequestRefreshRateMin, modes[i].refreshRate);
+ ASSERT_EQ(appRequestRefreshRateMax, modes[i].refreshRate);
}
+}
- res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, initialDefaultConfig,
- initialPrimaryMin, initialPrimaryMax,
- initialAppRequestMin,
- initialAppRequestMax);
+void RefreshRateRangeTest::testSetAllowGroupSwitching(bool allowGroupSwitching) {
+ status_t res =
+ SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, 0, allowGroupSwitching,
+ 0.f, 90.f, 0.f, 90.f);
ASSERT_EQ(res, NO_ERROR);
+ ui::DisplayModeId defaultConfig;
+ bool newAllowGroupSwitching;
+ float primaryRefreshRateMin;
+ float primaryRefreshRateMax;
+ float appRequestRefreshRateMin;
+ float appRequestRefreshRateMax;
+
+ res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &defaultConfig,
+ &newAllowGroupSwitching,
+ &primaryRefreshRateMin,
+ &primaryRefreshRateMax,
+ &appRequestRefreshRateMin,
+ &appRequestRefreshRateMax);
+ ASSERT_EQ(res, NO_ERROR);
+ ASSERT_EQ(defaultConfig, 0);
+ ASSERT_EQ(newAllowGroupSwitching, allowGroupSwitching);
+ ASSERT_EQ(primaryRefreshRateMin, 0.f);
+ ASSERT_EQ(primaryRefreshRateMax, 90.f);
+ ASSERT_EQ(appRequestRefreshRateMin, 0.f);
+ ASSERT_EQ(appRequestRefreshRateMax, 90.f);
+}
+
+TEST_F(RefreshRateRangeTest, setAllowGroupSwitching) {
+ testSetAllowGroupSwitching(true);
+ testSetAllowGroupSwitching(false);
+ testSetAllowGroupSwitching(true);
}
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file
+#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/EffectLayer_test.cpp b/services/surfaceflinger/tests/EffectLayer_test.cpp
index 3dca391..af00ec7 100644
--- a/services/surfaceflinger/tests/EffectLayer_test.cpp
+++ b/services/surfaceflinger/tests/EffectLayer_test.cpp
@@ -51,11 +51,11 @@
sp<SurfaceControl> effectLayer =
mClient->createSurface(String8("Effect Layer"), 0 /* width */, 0 /* height */,
PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceEffect,
- mParentLayer.get());
+ mParentLayer->getHandle());
EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
asTransaction([&](Transaction& t) {
- t.setCrop_legacy(effectLayer, Rect(0, 0, 400, 400));
+ t.setCrop(effectLayer, Rect(0, 0, 400, 400));
t.show(effectLayer);
});
@@ -72,11 +72,11 @@
PIXEL_FORMAT_RGBA_8888,
ISurfaceComposerClient::eFXSurfaceEffect |
ISurfaceComposerClient::eNoColorFill,
- mParentLayer.get());
+ mParentLayer->getHandle());
EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
asTransaction([&](Transaction& t) {
- t.setCrop_legacy(effectLayer, Rect(0, 0, 400, 400));
+ t.setCrop(effectLayer, Rect(0, 0, 400, 400));
t.show(effectLayer);
});
@@ -93,11 +93,11 @@
PIXEL_FORMAT_RGBA_8888,
ISurfaceComposerClient::eFXSurfaceEffect |
ISurfaceComposerClient::eNoColorFill,
- mParentLayer.get());
+ mParentLayer->getHandle());
EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
asTransaction([&](Transaction& t) {
- t.setCrop_legacy(effectLayer, Rect(0, 0, 400, 400));
+ t.setCrop(effectLayer, Rect(0, 0, 400, 400));
t.setColor(effectLayer,
half3{Color::GREEN.r / 255.0f, Color::GREEN.g / 255.0f,
Color::GREEN.b / 255.0f});
@@ -111,6 +111,71 @@
}
}
+TEST_F(EffectLayerTest, BlurEffectLayerIsVisible) {
+ if (!deviceSupportsBlurs()) GTEST_SKIP();
+ if (!deviceUsesSkiaRenderEngine()) GTEST_SKIP();
+
+ const auto canvasSize = 256;
+
+ sp<SurfaceControl> leftLayer = createColorLayer("Left", Color::BLUE);
+ sp<SurfaceControl> rightLayer = createColorLayer("Right", Color::GREEN);
+ sp<SurfaceControl> blurLayer;
+ const auto leftRect = Rect(0, 0, canvasSize / 2, canvasSize);
+ const auto rightRect = Rect(canvasSize / 2, 0, canvasSize, canvasSize);
+ const auto blurRect = Rect(0, 0, canvasSize, canvasSize);
+
+ asTransaction([&](Transaction& t) {
+ t.setLayer(leftLayer, mLayerZBase + 1);
+ t.reparent(leftLayer, mParentLayer);
+ t.setCrop(leftLayer, leftRect);
+ t.setLayer(rightLayer, mLayerZBase + 2);
+ t.reparent(rightLayer, mParentLayer);
+ t.setCrop(rightLayer, rightRect);
+ t.show(leftLayer);
+ t.show(rightLayer);
+ });
+
+ {
+ auto shot = screenshot();
+ shot->expectColor(leftRect, Color::BLUE);
+ shot->expectColor(rightRect, Color::GREEN);
+ }
+
+ ASSERT_NO_FATAL_FAILURE(blurLayer = createColorLayer("BackgroundBlur", Color::TRANSPARENT));
+
+ const auto blurRadius = canvasSize / 2;
+ asTransaction([&](Transaction& t) {
+ t.setLayer(blurLayer, mLayerZBase + 3);
+ t.reparent(blurLayer, mParentLayer);
+ t.setBackgroundBlurRadius(blurLayer, blurRadius);
+ t.setCrop(blurLayer, blurRect);
+ t.setAlpha(blurLayer, 0.0f);
+ t.show(blurLayer);
+ });
+
+ {
+ auto shot = screenshot();
+
+ const auto stepSize = 1;
+ const auto blurAreaOffset = blurRadius * 0.7f;
+ const auto blurAreaStartX = canvasSize / 2 - blurRadius + blurAreaOffset;
+ const auto blurAreaEndX = canvasSize / 2 + blurRadius - blurAreaOffset;
+ Color previousColor;
+ Color currentColor;
+ for (int y = 0; y < canvasSize; y++) {
+ shot->checkPixel(0, y, /* r = */ 0, /* g = */ 0, /* b = */ 255);
+ previousColor = shot->getPixelColor(0, y);
+ for (int x = blurAreaStartX; x < blurAreaEndX; x += stepSize) {
+ currentColor = shot->getPixelColor(x, y);
+ ASSERT_GT(currentColor.g, previousColor.g);
+ ASSERT_LT(currentColor.b, previousColor.b);
+ ASSERT_EQ(0, currentColor.r);
+ }
+ shot->checkPixel(canvasSize - 1, y, 0, 255, 0);
+ }
+ }
+}
+
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp
index 4023c66..9fa3d4c 100644
--- a/services/surfaceflinger/tests/IPC_test.cpp
+++ b/services/surfaceflinger/tests/IPC_test.cpp
@@ -23,7 +23,7 @@
#include <gui/LayerState.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
-#include <ui/DisplayConfig.h>
+#include <ui/DisplayMode.h>
#include <utils/String8.h>
#include <limits>
@@ -161,7 +161,6 @@
Color::RED);
transaction->setLayerStack(mSurfaceControl, 0)
.setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
- .setFrame(mSurfaceControl, Rect(0, 0, width, height))
.setBuffer(mSurfaceControl, gb)
.setAcquireFence(mSurfaceControl, fence)
.show(mSurfaceControl)
@@ -227,10 +226,10 @@
ASSERT_EQ(NO_ERROR, mClient->initCheck());
mPrimaryDisplay = mClient->getInternalDisplayToken();
- DisplayConfig config;
- mClient->getActiveDisplayConfig(mPrimaryDisplay, &config);
- mDisplayWidth = config.resolution.getWidth();
- mDisplayHeight = config.resolution.getHeight();
+ ui::DisplayMode mode;
+ mClient->getActiveDisplayMode(mPrimaryDisplay, &mode);
+ mDisplayWidth = mode.resolution.getWidth();
+ mDisplayHeight = mode.resolution.getHeight();
Transaction setupTransaction;
setupTransaction.setDisplayLayerStack(mPrimaryDisplay, 0);
diff --git a/services/surfaceflinger/tests/InvalidHandles_test.cpp b/services/surfaceflinger/tests/InvalidHandles_test.cpp
index 42d1f5a..9cf7c09 100644
--- a/services/surfaceflinger/tests/InvalidHandles_test.cpp
+++ b/services/surfaceflinger/tests/InvalidHandles_test.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
#include <binder/Binder.h>
#include <gtest/gtest.h>
@@ -22,6 +26,7 @@
#include <gui/SurfaceComposerClient.h>
#include <private/gui/ComposerService.h>
#include <ui/Rect.h>
+#include "utils/ScreenshotUtils.h"
namespace android {
namespace {
@@ -47,21 +52,27 @@
}
};
-TEST_F(InvalidHandleTest, createSurfaceInvalidHandle) {
- auto notSc = makeNotSurfaceControl();
- ASSERT_EQ(nullptr,
- mScc->createSurface(String8("lolcats"), 19, 47, PIXEL_FORMAT_RGBA_8888, 0,
- notSc.get())
- .get());
+TEST_F(InvalidHandleTest, createSurfaceInvalidParentHandle) {
+ // The createSurface is scheduled now, we could still get a created surface from createSurface.
+ // Should verify if it actually added into current state by checking the screenshot.
+ auto notSc = mScc->createSurface(String8("lolcats"), 19, 47, PIXEL_FORMAT_RGBA_8888, 0,
+ mNotSc->getHandle());
+ LayerCaptureArgs args;
+ args.layerHandle = notSc->getHandle();
+ ScreenCaptureResults captureResults;
+ ASSERT_EQ(NAME_NOT_FOUND, ScreenCapture::captureLayers(args, captureResults));
}
TEST_F(InvalidHandleTest, captureLayersInvalidHandle) {
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- sp<GraphicBuffer> outBuffer;
+ LayerCaptureArgs args;
+ args.layerHandle = mNotSc->getHandle();
- ASSERT_EQ(NAME_NOT_FOUND,
- sf->captureLayers(mNotSc->getHandle(), &outBuffer, Rect::EMPTY_RECT, 1.0f));
+ ScreenCaptureResults captureResults;
+ ASSERT_EQ(NAME_NOT_FOUND, ScreenCapture::captureLayers(args, captureResults));
}
} // namespace
} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp
index 6d28e62..011ff70 100644
--- a/services/surfaceflinger/tests/LayerCallback_test.cpp
+++ b/services/surfaceflinger/tests/LayerCallback_test.cpp
@@ -14,13 +14,15 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
+#include <sys/epoll.h>
+
+#include <gui/DisplayEventReceiver.h>
#include "LayerTransactionTest.h"
#include "utils/CallbackUtils.h"
+using namespace std::chrono_literals;
+
namespace android {
using android::hardware::graphics::common::V1_1::BufferUsage;
@@ -30,6 +32,24 @@
class LayerCallbackTest : public LayerTransactionTest {
public:
+ void SetUp() override {
+ LayerTransactionTest::SetUp();
+
+ EXPECT_EQ(NO_ERROR, mDisplayEventReceiver.initCheck());
+
+ mEpollFd = epoll_create1(EPOLL_CLOEXEC);
+ EXPECT_GT(mEpollFd, 1);
+
+ epoll_event event;
+ event.events = EPOLLIN;
+ EXPECT_EQ(0, epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mDisplayEventReceiver.getFd(), &event));
+ }
+
+ void TearDown() override {
+ close(mEpollFd);
+ LayerTransactionTest::TearDown();
+ }
+
virtual sp<SurfaceControl> createBufferStateLayer() {
return createLayer(mClient, "test", 0, 0, ISurfaceComposerClient::eFXSurfaceBufferState);
}
@@ -82,6 +102,35 @@
ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState());
}
}
+
+ DisplayEventReceiver mDisplayEventReceiver;
+ int mEpollFd;
+
+ struct Vsync {
+ int64_t vsyncId = FrameTimelineInfo::INVALID_VSYNC_ID;
+ nsecs_t expectedPresentTime = std::numeric_limits<nsecs_t>::max();
+ };
+
+ Vsync waitForNextVsync() {
+ mDisplayEventReceiver.requestNextVsync();
+ epoll_event epollEvent;
+ Vsync vsync;
+ EXPECT_EQ(1, epoll_wait(mEpollFd, &epollEvent, 1, 1000))
+ << "Timeout waiting for vsync event";
+ DisplayEventReceiver::Event event;
+ while (mDisplayEventReceiver.getEvents(&event, 1) > 0) {
+ if (event.header.type != DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
+ continue;
+ }
+
+ vsync = {event.vsync.vsyncId, event.vsync.expectedVSyncTimestamp};
+ }
+
+ EXPECT_GE(vsync.vsyncId, 1);
+ EXPECT_GT(event.vsync.expectedVSyncTimestamp, systemTime());
+
+ return vsync;
+ }
};
TEST_F(LayerCallbackTest, BufferColor) {
@@ -115,7 +164,10 @@
return;
}
- transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+ ui::Size bufferSize = getBufferSize();
+ TransactionUtils::setFrame(transaction, layer, Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(0, 0, 32, 32));
+ transaction.apply();
ExpectedResult expected;
expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer,
@@ -135,7 +187,10 @@
return;
}
- transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+ ui::Size bufferSize = getBufferSize();
+ TransactionUtils::setFrame(transaction, layer, Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(0, 0, 32, 32));
+ transaction.apply();
ExpectedResult expected;
expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
@@ -154,7 +209,10 @@
return;
}
- transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+ ui::Size bufferSize = getBufferSize();
+ TransactionUtils::setFrame(transaction, layer, Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(0, 0, 32, 32));
+ transaction.apply();
ExpectedResult expected;
expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
@@ -189,7 +247,10 @@
return;
}
- transaction.setFrame(layer, Rect(-100, -100, 100, 100)).apply();
+ ui::Size bufferSize = getBufferSize();
+ TransactionUtils::setFrame(transaction, layer, Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(-100, -100, 100, 100));
+ transaction.apply();
ExpectedResult expected;
expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
@@ -214,8 +275,15 @@
return;
}
- transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
- transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+ ui::Size bufferSize = getBufferSize();
+
+ TransactionUtils::setFrame(transaction1, layer1,
+ Rect(0, 0, bufferSize.width, bufferSize.height), Rect(0, 0, 32, 32));
+ TransactionUtils::setFrame(transaction2, layer2,
+ Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(32, 32, 64, 64));
+
+ transaction2.merge(std::move(transaction1)).apply();
ExpectedResult expected;
expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
@@ -241,8 +309,15 @@
return;
}
- transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
- transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+ ui::Size bufferSize = getBufferSize();
+
+ TransactionUtils::setFrame(transaction1, layer1,
+ Rect(0, 0, bufferSize.width, bufferSize.height), Rect(0, 0, 32, 32));
+ TransactionUtils::setFrame(transaction2, layer2,
+ Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(32, 32, 64, 64));
+
+ transaction2.merge(std::move(transaction1)).apply();
ExpectedResult expected;
expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
@@ -269,8 +344,15 @@
return;
}
- transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
- transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+ ui::Size bufferSize = getBufferSize();
+
+ TransactionUtils::setFrame(transaction1, layer1,
+ Rect(0, 0, bufferSize.width, bufferSize.height), Rect(0, 0, 32, 32));
+ TransactionUtils::setFrame(transaction2, layer2,
+ Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(32, 32, 64, 64));
+
+ transaction2.merge(std::move(transaction1)).apply();
ExpectedResult expected;
expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer1);
@@ -356,8 +438,15 @@
return;
}
- transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
- transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+ ui::Size bufferSize = getBufferSize();
+
+ TransactionUtils::setFrame(transaction1, layer1,
+ Rect(0, 0, bufferSize.width, bufferSize.height), Rect(0, 0, 32, 32));
+ TransactionUtils::setFrame(transaction2, layer2,
+ Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(32, 32, 64, 64));
+
+ transaction2.merge(std::move(transaction1)).apply();
ExpectedResult expected;
expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
@@ -442,7 +531,11 @@
}
}
- transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+ ui::Size bufferSize = getBufferSize();
+ TransactionUtils::setFrame(transaction, layer,
+ Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(0, 0, 32, 32));
+ transaction.apply();
ExpectedResult expected;
expected.addSurface((i == 0) ? ExpectedResult::Transaction::PRESENTED
@@ -474,8 +567,16 @@
return;
}
- transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
- transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+ ui::Size bufferSize = getBufferSize();
+
+ TransactionUtils::setFrame(transaction1, layer1,
+ Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(0, 0, 32, 32));
+ TransactionUtils::setFrame(transaction2, layer2,
+ Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(32, 32, 64, 64));
+
+ transaction2.merge(std::move(transaction1)).apply();
ExpectedResult expected;
expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
@@ -515,8 +616,16 @@
return;
}
- transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
- transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+ ui::Size bufferSize = getBufferSize();
+
+ TransactionUtils::setFrame(transaction1, layer1,
+ Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(0, 0, 32, 32));
+ TransactionUtils::setFrame(transaction2, layer2,
+ Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(32, 32, 64, 64));
+
+ transaction2.merge(std::move(transaction1)).apply();
ExpectedResult expected;
expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
@@ -557,8 +666,15 @@
return;
}
- transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
- transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+ ui::Size bufferSize = getBufferSize();
+
+ TransactionUtils::setFrame(transaction1, layer1,
+ Rect(0, 0, bufferSize.width, bufferSize.height), Rect(0, 0, 32, 32));
+ TransactionUtils::setFrame(transaction2, layer2,
+ Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(32, 32, 64, 64));
+
+ transaction2.merge(std::move(transaction1)).apply();
ExpectedResult expected;
expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
@@ -612,8 +728,15 @@
return;
}
- transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
- transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+ ui::Size bufferSize = getBufferSize();
+
+ TransactionUtils::setFrame(transaction1, layer1,
+ Rect(0, 0, bufferSize.width, bufferSize.height), Rect(0, 0, 32, 32));
+ TransactionUtils::setFrame(transaction2, layer2,
+ Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(32, 32, 64, 64));
+
+ transaction2.merge(std::move(transaction1)).apply();
ExpectedResult expected;
expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
@@ -633,7 +756,10 @@
return;
}
- transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+ TransactionUtils::setFrame(transaction2, layer2,
+ Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(32, 32, 64, 64));
+ transaction2.merge(std::move(transaction1)).apply();
expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer2,
ExpectedResult::Buffer::NOT_ACQUIRED);
@@ -713,7 +839,10 @@
return;
}
- transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+ ui::Size bufferSize = getBufferSize();
+ TransactionUtils::setFrame(transaction, layer, Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(0, 0, 32, 32));
+ transaction.apply();
ExpectedResult expectedResult;
expectedResult.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
@@ -732,7 +861,10 @@
return;
}
- transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+ TransactionUtils::setFrame(transaction, layer,
+ Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(0, 0, 32, 32));
+ transaction.apply();
}
EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
}
@@ -750,7 +882,7 @@
}
// Try to present 100ms in the future
- nsecs_t time = systemTime() + (100 * 1e6);
+ nsecs_t time = systemTime() + std::chrono::nanoseconds(100ms).count();
transaction.setDesiredPresentTime(time);
transaction.apply();
@@ -774,7 +906,7 @@
}
// Try to present 100ms in the future
- nsecs_t time = systemTime() + (100 * 1e6);
+ nsecs_t time = systemTime() + std::chrono::nanoseconds(100ms).count();
transaction.setDesiredPresentTime(time);
transaction.apply();
@@ -791,7 +923,7 @@
}
// Try to present 33ms after the first frame
- time += (33.3 * 1e6);
+ time += std::chrono::nanoseconds(33ms).count();
transaction.setDesiredPresentTime(time);
transaction.apply();
@@ -819,7 +951,7 @@
}
// Try to present 100ms in the future
- nsecs_t time = systemTime() + (100 * 1e6);
+ nsecs_t time = systemTime() + std::chrono::nanoseconds(100ms).count();
transaction.setDesiredPresentTime(time);
transaction.apply();
@@ -836,7 +968,7 @@
}
// Try to present 33ms before the previous frame
- time -= (33.3 * 1e6);
+ time -= std::chrono::nanoseconds(33ms).count();
transaction.setDesiredPresentTime(time);
transaction.apply();
@@ -863,7 +995,7 @@
}
// Try to present 100ms in the past
- nsecs_t time = systemTime() - (100 * 1e6);
+ nsecs_t time = systemTime() - std::chrono::nanoseconds(100ms).count();
transaction.setDesiredPresentTime(time);
transaction.apply();
@@ -873,7 +1005,27 @@
expected.addExpectedPresentTime(systemTime());
EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
}
-} // namespace android
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+TEST_F(LayerCallbackTest, ExpectedPresentTime) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction, &callback, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ const Vsync vsync = waitForNextVsync();
+ transaction.setFrameTimelineInfo({vsync.vsyncId, 0});
+ transaction.apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+ expected.addExpectedPresentTimeForVsyncId(vsync.expectedPresentTime);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
index 83e5060..7581cd3 100644
--- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -43,6 +43,9 @@
protected:
LayerRenderPathTestHarness mHarness;
+
+ static constexpr int64_t kUsageFlags = BufferUsage::CPU_READ_OFTEN |
+ BufferUsage::CPU_WRITE_OFTEN | BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE;
};
INSTANTIATE_TEST_CASE_P(LayerRenderTypeTransactionTests, LayerRenderTypeTransactionTest,
@@ -132,66 +135,6 @@
}
}
-TEST_P(LayerRenderTypeTransactionTest, SetPositionWithResize_BufferQueue) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
- // setPosition is applied immediately by default, with or without resize
- // pending
- Transaction().setPosition(layer, 5, 10).setSize(layer, 64, 64).apply();
- {
- SCOPED_TRACE("resize pending");
- auto shot = getScreenCapture();
- const Rect rect(5, 10, 37, 42);
- shot->expectColor(rect, Color::RED);
- shot->expectBorder(rect, Color::BLACK);
- }
-
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
- {
- SCOPED_TRACE("resize applied");
- getScreenCapture()->expectColor(Rect(5, 10, 69, 74), Color::RED);
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetSizeBasic_BufferQueue) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
- Transaction().setSize(layer, 64, 64).apply();
- {
- SCOPED_TRACE("resize pending");
- auto shot = getScreenCapture();
- const Rect rect(0, 0, 32, 32);
- shot->expectColor(rect, Color::RED);
- shot->expectBorder(rect, Color::BLACK);
- }
-
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
- {
- SCOPED_TRACE("resize applied");
- auto shot = getScreenCapture();
- const Rect rect(0, 0, 64, 64);
- shot->expectColor(rect, Color::RED);
- shot->expectBorder(rect, Color::BLACK);
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetSizeWithScaleToWindow_BufferQueue) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
- // setSize is immediate with SCALE_TO_WINDOW, unlike setPosition
- Transaction()
- .setSize(layer, 64, 64)
- .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
- .apply();
- getScreenCapture()->expectColor(Rect(0, 0, 64, 64), Color::RED);
-}
-
TEST_P(LayerRenderTypeTransactionTest, CreateLayer_BufferState) {
uint32_t transformHint = ui::Transform::ROT_INVALID;
sp<SurfaceControl> layer;
@@ -211,17 +154,10 @@
switch (layerType) {
case ISurfaceComposerClient::eFXSurfaceBufferQueue:
- Transaction()
- .setPosition(layerG, 16, 16)
- .setRelativeLayer(layerG, layerR->getHandle(), 1)
- .apply();
+ Transaction().setPosition(layerG, 16, 16).setRelativeLayer(layerG, layerR, 1).apply();
break;
case ISurfaceComposerClient::eFXSurfaceBufferState:
- Transaction()
- .setFrame(layerR, Rect(0, 0, 32, 32))
- .setFrame(layerG, Rect(16, 16, 48, 48))
- .setRelativeLayer(layerG, layerR->getHandle(), 1)
- .apply();
+ Transaction().setPosition(layerG, 16, 16).setRelativeLayer(layerG, layerR, 1).apply();
break;
default:
ASSERT_FALSE(true) << "Unsupported layer type";
@@ -233,7 +169,7 @@
shot->expectColor(Rect(16, 16, 48, 48), Color::GREEN);
}
- Transaction().setRelativeLayer(layerG, layerR->getHandle(), -1).apply();
+ Transaction().setRelativeLayer(layerG, layerR, -1).apply();
{
SCOPED_TRACE("layerG below");
auto shot = getScreenCapture();
@@ -266,17 +202,16 @@
case ISurfaceComposerClient::eFXSurfaceBufferQueue:
Transaction()
.setPosition(layerG, 8, 8)
- .setRelativeLayer(layerG, layerR->getHandle(), 3)
+ .setRelativeLayer(layerG, layerR, 3)
.setPosition(layerB, 16, 16)
.setLayer(layerB, mLayerZBase + 2)
.apply();
break;
case ISurfaceComposerClient::eFXSurfaceBufferState:
Transaction()
- .setFrame(layerR, Rect(0, 0, 32, 32))
- .setFrame(layerG, Rect(8, 8, 40, 40))
- .setRelativeLayer(layerG, layerR->getHandle(), 3)
- .setFrame(layerB, Rect(16, 16, 48, 48))
+ .setPosition(layerG, 8, 8)
+ .setRelativeLayer(layerG, layerR, 3)
+ .setPosition(layerB, 16, 16)
.setLayer(layerB, mLayerZBase + 2)
.apply();
break;
@@ -303,7 +238,7 @@
}
// layerR = 4, layerG = layerR - 3, layerB = 2
- Transaction().setRelativeLayer(layerG, layerR->getHandle(), -3).apply();
+ Transaction().setRelativeLayer(layerG, layerR, -3).apply();
{
SCOPED_TRACE("layerB < (layerG < layerR)");
auto shot = getScreenCapture();
@@ -393,10 +328,7 @@
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
sp<GraphicBuffer> buffer =
- new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
+ new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
ASSERT_NO_FATAL_FAILURE(
TransactionUtils::fillGraphicBufferColor(buffer, top, Color::TRANSPARENT));
@@ -404,7 +336,6 @@
Transaction()
.setTransparentRegionHint(layer, Region(top))
.setBuffer(layer, buffer)
- .setFrame(layer, Rect(0, 0, 32, 32))
.apply();
{
SCOPED_TRACE("top transparent");
@@ -421,10 +352,7 @@
shot->expectColor(bottom, Color::BLACK);
}
- buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
+ buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
ASSERT_NO_FATAL_FAILURE(TransactionUtils::fillGraphicBufferColor(buffer, top, Color::RED));
ASSERT_NO_FATAL_FAILURE(
@@ -466,7 +394,7 @@
// check that transparent region hint is bound by the layer size
Transaction()
.setTransparentRegionHint(layerTransparent, Region(mDisplayRect))
- .setFrame(layerR, Rect(16, 16, 48, 48))
+ .setPosition(layerR, 16, 16)
.setLayer(layerR, mLayerZBase + 1)
.apply();
ASSERT_NO_FATAL_FAILURE(
@@ -496,8 +424,7 @@
Transaction()
.setAlpha(layer1, 0.25f)
.setAlpha(layer2, 0.75f)
- .setFrame(layer1, Rect(0, 0, 32, 32))
- .setFrame(layer2, Rect(16, 0, 48, 32))
+ .setPosition(layer2, 16, 0)
.setLayer(layer2, mLayerZBase + 1)
.apply();
break;
@@ -534,7 +461,7 @@
ISurfaceComposerClient::eFXSurfaceEffect));
Transaction()
- .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+ .setCrop(colorLayer, Rect(0, 0, 32, 32))
.setLayer(colorLayer, mLayerZBase + 1)
.apply();
@@ -573,7 +500,7 @@
case ISurfaceComposerClient::eFXSurfaceEffect:
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 0, 0, layerType));
Transaction()
- .setCrop_legacy(layer, Rect(0, 0, width, height))
+ .setCrop(layer, Rect(0, 0, width, height))
.setColor(layer, half3(1.0f, 0, 0))
.apply();
expectedColor = fillColor;
@@ -584,7 +511,7 @@
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, fillColor, width, height));
expectedColor = fillColor;
}
- Transaction().setCrop_legacy(layer, Rect(0, 0, width, height)).apply();
+ Transaction().setCrop(layer, Rect(0, 0, width, height)).apply();
break;
case ISurfaceComposerClient::eFXSurfaceBufferState:
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height, layerType));
@@ -592,7 +519,7 @@
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, fillColor, width, height));
expectedColor = fillColor;
}
- Transaction().setFrame(layer, Rect(0, 0, width, height)).apply();
+ Transaction().setCrop(layer, Rect(0, 0, width, height)).apply();
break;
default:
GTEST_FAIL() << "Unknown layer type in setBackgroundColorHelper";
@@ -633,44 +560,6 @@
}
TEST_P(LayerRenderTypeTransactionTest,
- SetBackgroundColor_BufferQueue_BufferFill_NoPriorColor_Basic) {
- bool priorColor = false;
- bool bufferFill = true;
- float alpha = 1.0f;
- Color finalColor = Color::RED;
- ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
- priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest,
- SetBackgroundColor_BufferQueue_NoBufferFill_NoPriorColor_Basic) {
- bool priorColor = false;
- bool bufferFill = false;
- float alpha = 1.0f;
- Color finalColor = Color::GREEN;
- ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
- priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetBackgroundColor_BufferQueue_BufferFill_PriorColor_Basic) {
- bool priorColor = true;
- bool bufferFill = true;
- float alpha = 1.0f;
- Color finalColor = Color::RED;
- ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
- priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest,
- SetBackgroundColor_BufferQueue_NoBufferFill_PriorColor_Basic) {
- bool priorColor = true;
- bool bufferFill = false;
- float alpha = 1.0f;
- Color finalColor = Color::GREEN;
- ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
- priorColor, bufferFill, alpha, finalColor));
-}
-TEST_P(LayerRenderTypeTransactionTest,
SetBackgroundColor_BufferQueue_NoPriorColor_ZeroAlpha_NoEffect) {
bool priorColor = false;
bool bufferFill = false;
@@ -746,7 +635,7 @@
createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
ISurfaceComposerClient::eFXSurfaceEffect));
Transaction()
- .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+ .setCrop(colorLayer, Rect(0, 0, 32, 32))
.setColor(colorLayer, half3(2.0f, 0.0f, 0.0f))
.apply();
@@ -760,7 +649,7 @@
createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
ISurfaceComposerClient::eFXSurfaceEffect));
Transaction()
- .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+ .setCrop(colorLayer, Rect(0, 0, 32, 32))
.setColor(colorLayer, half3(1.0f, -1.0f, 0.5f))
.apply();
@@ -775,7 +664,7 @@
ASSERT_NO_FATAL_FAILURE(colorLayer =
createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
ISurfaceComposerClient::eFXSurfaceEffect));
- Transaction().setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)).apply();
+ Transaction().setCrop(colorLayer, Rect(0, 0, 32, 32)).apply();
const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
const float alpha = 0.25f;
@@ -802,7 +691,7 @@
ASSERT_NO_FATAL_FAILURE(colorLayer = createLayer("childWithColor", 0 /* buffer width */,
0 /* buffer height */,
ISurfaceComposerClient::eFXSurfaceEffect));
- Transaction().setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)).apply();
+ Transaction().setCrop(colorLayer, Rect(0, 0, 32, 32)).apply();
const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
const float alpha = 0.25f;
const ubyte3 expected((vec3(color) * alpha + vec3(1.0f, 0.0f, 0.0f) * (1.0f - alpha)) * 255.0f);
@@ -810,7 +699,7 @@
// channel) should be less than one
const uint8_t tolerance = 1;
Transaction()
- .reparent(colorLayer, parentLayer->getHandle())
+ .reparent(colorLayer, parentLayer)
.setColor(colorLayer, color)
.setAlpha(parentLayer, alpha)
.setLayer(parentLayer, mLayerZBase + 1)
@@ -868,42 +757,39 @@
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
Color::BLUE, Color::WHITE));
- Transaction()
- .setMatrix(layer, 1.0f, 0.0f, 0.0f, 1.0f)
- .setFrame(layer, Rect(0, 0, 32, 32))
- .apply();
+ Transaction().setPosition(layer, 32, 32).setMatrix(layer, 1.0f, 0.0f, 0.0f, 1.0f).apply();
{
SCOPED_TRACE("IDENTITY");
- getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+ getScreenCapture()->expectQuadrant(Rect(32, 32, 64, 64), Color::RED, Color::GREEN,
Color::BLUE, Color::WHITE);
}
Transaction().setMatrix(layer, -1.0f, 0.0f, 0.0f, 1.0f).apply();
{
SCOPED_TRACE("FLIP_H");
- getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
- Color::BLUE, Color::WHITE);
+ getScreenCapture()->expectQuadrant(Rect(0, 32, 32, 64), Color::GREEN, Color::RED,
+ Color::WHITE, Color::BLUE);
}
Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, -1.0f).apply();
{
SCOPED_TRACE("FLIP_V");
- getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
- Color::BLUE, Color::WHITE);
+ getScreenCapture()->expectQuadrant(Rect(32, 0, 64, 32), Color::BLUE, Color::WHITE,
+ Color::RED, Color::GREEN);
}
Transaction().setMatrix(layer, 0.0f, 1.0f, -1.0f, 0.0f).apply();
{
SCOPED_TRACE("ROT_90");
- getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
- Color::BLUE, Color::WHITE);
+ getScreenCapture()->expectQuadrant(Rect(0, 32, 32, 64), Color::BLUE, Color::RED,
+ Color::WHITE, Color::GREEN);
}
Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).apply();
{
SCOPED_TRACE("SCALE");
- getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
- Color::BLUE, Color::WHITE);
+ getScreenCapture()->expectQuadrant(Rect(32, 32, 96, 96), Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE, 1 /* tolerance */);
}
}
@@ -930,70 +816,13 @@
shot->expectColor(get8x8Rect(2 * unit, 3 * unit), Color::WHITE);
}
-TEST_P(LayerRenderTypeTransactionTest, SetMatrixWithResize_BufferQueue) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
- // setMatrix is applied after any pending resize, unlike setPosition
- Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).setSize(layer, 64, 64).apply();
- {
- SCOPED_TRACE("resize pending");
- auto shot = getScreenCapture();
- const Rect rect(0, 0, 32, 32);
- shot->expectColor(rect, Color::RED);
- shot->expectBorder(rect, Color::BLACK);
- }
-
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
- {
- SCOPED_TRACE("resize applied");
- const Rect rect(0, 0, 128, 128);
- getScreenCapture()->expectColor(rect, Color::RED);
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetMatrixWithScaleToWindow_BufferQueue) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
- // setMatrix is immediate with SCALE_TO_WINDOW, unlike setPosition
- Transaction()
- .setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f)
- .setSize(layer, 64, 64)
- .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
- .apply();
- getScreenCapture()->expectColor(Rect(0, 0, 128, 128), Color::RED);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetOverrideScalingModeBasic_BufferQueue) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
- Color::BLUE, Color::WHITE));
-
- // XXX SCALE_CROP is not respected; calling setSize and
- // setOverrideScalingMode in separate transactions does not work
- // (b/69315456)
- Transaction()
- .setSize(layer, 64, 16)
- .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
- .apply();
- {
- SCOPED_TRACE("SCALE_TO_WINDOW");
- getScreenCapture()->expectQuadrant(Rect(0, 0, 64, 16), Color::RED, Color::GREEN,
- Color::BLUE, Color::WHITE, true /* filtered */);
- }
-}
-
TEST_P(LayerRenderTypeTransactionTest, SetCropBasic_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
const Rect crop(8, 8, 24, 24);
- Transaction().setCrop_legacy(layer, crop).apply();
+ Transaction().setCrop(layer, crop).apply();
auto shot = getScreenCapture();
shot->expectColor(crop, Color::RED);
shot->expectBorder(crop, Color::BLACK);
@@ -1008,8 +837,8 @@
Transaction().setCrop(layer, crop).apply();
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ shot->expectColor(crop, Color::RED);
+ shot->expectBorder(crop, Color::BLACK);
}
TEST_P(LayerRenderTypeTransactionTest, SetCropEmpty_BufferQueue) {
@@ -1019,13 +848,13 @@
{
SCOPED_TRACE("empty rect");
- Transaction().setCrop_legacy(layer, Rect(8, 8, 8, 8)).apply();
+ Transaction().setCrop(layer, Rect(8, 8, 8, 8)).apply();
getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
{
SCOPED_TRACE("negative rect");
- Transaction().setCrop_legacy(layer, Rect(8, 8, 0, 0)).apply();
+ Transaction().setCrop(layer, Rect(8, 8, 0, 0)).apply();
getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
}
@@ -1039,13 +868,13 @@
{
SCOPED_TRACE("empty rect");
Transaction().setCrop(layer, Rect(8, 8, 8, 8)).apply();
- getScreenCapture()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
{
SCOPED_TRACE("negative rect");
Transaction().setCrop(layer, Rect(8, 8, 0, 0)).apply();
- getScreenCapture()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
}
@@ -1054,7 +883,7 @@
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
- Transaction().setCrop_legacy(layer, Rect(-128, -64, 128, 64)).apply();
+ Transaction().setCrop(layer, Rect(-128, -64, 128, 64)).apply();
auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
@@ -1065,15 +894,10 @@
ASSERT_NO_FATAL_FAILURE(
layer = createLayer("test", 32, 64, ISurfaceComposerClient::eFXSurfaceBufferState));
sp<GraphicBuffer> buffer =
- new GraphicBuffer(32, 64, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
+ new GraphicBuffer(32, 64, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 16), Color::BLUE);
TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 16, 32, 64), Color::RED);
- Transaction().setFrame(layer, Rect(0, 0, 64, 64)).apply();
-
Transaction().setBuffer(layer, buffer).apply();
// Partially out of bounds in the negative (upper left) direction
@@ -1081,8 +905,8 @@
{
SCOPED_TRACE("out of bounds, negative (upper left) direction");
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, 64, 64), Color::BLUE);
- shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK);
+ shot->expectColor(Rect(0, 0, 32, 16), Color::BLUE);
+ shot->expectBorder(Rect(0, 0, 32, 16), Color::BLACK);
}
// Partially out of bounds in the positive (lower right) direction
@@ -1090,8 +914,8 @@
{
SCOPED_TRACE("out of bounds, positive (lower right) direction");
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, 64, 64), Color::RED);
- shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK);
+ shot->expectColor(Rect(0, 16, 32, 64), Color::RED);
+ shot->expectBorder(Rect(0, 16, 32, 64), Color::BLACK);
}
// Fully out of buffer space bounds
@@ -1099,9 +923,7 @@
{
SCOPED_TRACE("Fully out of bounds");
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, 64, 16), Color::BLUE);
- shot->expectColor(Rect(0, 16, 64, 64), Color::RED);
- shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK);
+ shot->expectColor(Rect(0, 0, 64, 64), Color::BLACK);
}
}
@@ -1112,7 +934,7 @@
const Point position(32, 32);
const Rect crop(8, 8, 24, 24);
- Transaction().setPosition(layer, position.x, position.y).setCrop_legacy(layer, crop).apply();
+ Transaction().setPosition(layer, position.x, position.y).setCrop(layer, crop).apply();
auto shot = getScreenCapture();
shot->expectColor(crop + position, Color::RED);
shot->expectBorder(crop + position, Color::BLACK);
@@ -1124,12 +946,11 @@
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
- const Rect frame(32, 32, 64, 64);
const Rect crop(8, 8, 24, 24);
- Transaction().setFrame(layer, frame).setCrop(layer, crop).apply();
+ Transaction().setPosition(layer, 32, 32).setCrop(layer, crop).apply();
auto shot = getScreenCapture();
- shot->expectColor(frame, Color::RED);
- shot->expectBorder(frame, Color::BLACK);
+ shot->expectColor(Rect(40, 40, 56, 56), Color::RED);
+ shot->expectBorder(Rect(40, 40, 56, 56), Color::BLACK);
}
TEST_P(LayerRenderTypeTransactionTest, SetCropWithScale_BufferQueue) {
@@ -1137,39 +958,16 @@
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
- // crop_legacy is affected by matrix
+ // crop is affected by matrix
Transaction()
.setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f)
- .setCrop_legacy(layer, Rect(8, 8, 24, 24))
+ .setCrop(layer, Rect(8, 8, 24, 24))
.apply();
auto shot = getScreenCapture();
shot->expectColor(Rect(16, 16, 48, 48), Color::RED);
shot->expectBorder(Rect(16, 16, 48, 48), Color::BLACK);
}
-TEST_P(LayerRenderTypeTransactionTest, SetCropWithResize_BufferQueue) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
- // setCrop_legacy is applied immediately by default, with or without resize pending
- Transaction().setCrop_legacy(layer, Rect(8, 8, 24, 24)).setSize(layer, 16, 16).apply();
- {
- SCOPED_TRACE("resize pending");
- auto shot = getScreenCapture();
- shot->expectColor(Rect(8, 8, 24, 24), Color::RED);
- shot->expectBorder(Rect(8, 8, 24, 24), Color::BLACK);
- }
-
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 16, 16));
- {
- SCOPED_TRACE("resize applied");
- auto shot = getScreenCapture();
- shot->expectColor(Rect(8, 8, 16, 16), Color::RED);
- shot->expectBorder(Rect(8, 8, 16, 16), Color::BLACK);
- }
-}
-
TEST_P(LayerRenderTypeTransactionTest, SetFrameBasic_BufferState) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(
@@ -1177,7 +975,10 @@
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
const Rect frame(8, 8, 24, 24);
- Transaction().setFrame(layer, frame).apply();
+ Transaction t;
+ TransactionUtils::setFrame(t, layer, Rect(0, 0, 32, 32), frame);
+ t.apply();
+
auto shot = getScreenCapture();
shot->expectColor(frame, Color::RED);
shot->expectBorder(frame, Color::BLACK);
@@ -1189,16 +990,23 @@
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+ Transaction t;
{
SCOPED_TRACE("empty rect");
- Transaction().setFrame(layer, Rect(8, 8, 8, 8)).apply();
+ TransactionUtils::setFrame(t, layer, Rect(0, 0, 32, 32), Rect(8, 8, 8, 8));
+ t.apply();
+
getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
}
{
SCOPED_TRACE("negative rect");
- Transaction().setFrame(layer, Rect(8, 8, 0, 0)).apply();
- getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+ TransactionUtils::setFrame(t, layer, Rect(0, 0, 32, 32), Rect(8, 8, 0, 0));
+ t.apply();
+
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 8, 8), Color::RED);
+ shot->expectBorder(Rect(0, 0, 8, 8), Color::BLACK);
}
}
@@ -1208,10 +1016,10 @@
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 10, 10));
- // A parentless layer will default to a frame with the same size as the buffer
+ // A layer with a buffer will have a computed size that matches the buffer size.
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ shot->expectColor(Rect(0, 0, 10, 10), Color::RED);
+ shot->expectBorder(Rect(0, 0, 10, 10), Color::BLACK);
}
TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultBSParent_BufferState) {
@@ -1219,17 +1027,16 @@
ASSERT_NO_FATAL_FAILURE(
parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32));
- Transaction().setFrame(parent, Rect(0, 0, 32, 32)).apply();
ASSERT_NO_FATAL_FAILURE(
- child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ child = createLayer("test", 10, 10, ISurfaceComposerClient::eFXSurfaceBufferState));
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
- Transaction().reparent(child, parent->getHandle()).apply();
+ Transaction().reparent(child, parent).apply();
- // A layer will default to the frame of its parent
+ // A layer with a buffer will have a computed size that matches the buffer size.
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+ shot->expectColor(Rect(0, 0, 10, 10), Color::BLUE);
shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
@@ -1239,14 +1046,14 @@
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(parent, Color::RED, 32, 32));
ASSERT_NO_FATAL_FAILURE(
- child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ child = createLayer("test", 10, 10, ISurfaceComposerClient::eFXSurfaceBufferState));
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
- Transaction().reparent(child, parent->getHandle()).apply();
+ Transaction().reparent(child, parent).apply();
- // A layer will default to the frame of its parent
+ // A layer with a buffer will have a computed size that matches the buffer size.
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+ shot->expectColor(Rect(0, 0, 10, 10), Color::BLUE);
shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
@@ -1255,11 +1062,10 @@
ASSERT_NO_FATAL_FAILURE(
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
- Transaction().setFrame(layer, Rect(0, 0, 32, 32)).apply();
std::this_thread::sleep_for(500ms);
- Transaction().setFrame(layer, Rect(16, 16, 48, 48)).apply();
+ Transaction().setPosition(layer, 16, 16).apply();
auto shot = getScreenCapture();
shot->expectColor(Rect(16, 16, 48, 48), Color::RED);
@@ -1271,18 +1077,20 @@
ASSERT_NO_FATAL_FAILURE(
parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
ASSERT_NO_FATAL_FAILURE(
- child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
- Transaction().reparent(child, parent->getHandle()).apply();
+ child = createLayer("test", 10, 10, ISurfaceComposerClient::eFXSurfaceBufferState));
+ Transaction().reparent(child, parent).apply();
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32));
- Transaction().setFrame(parent, Rect(0, 0, 32, 32)).apply();
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
- Transaction().setFrame(child, Rect(0, 16, 32, 32)).apply();
+ Rect childDst(0, 16, 32, 32);
+ Transaction t;
+ TransactionUtils::setFrame(t, child, Rect(0, 0, 10, 10), childDst);
+ t.apply();
auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 16), Color::RED);
- shot->expectColor(Rect(0, 16, 32, 32), Color::BLUE);
+ shot->expectColor(childDst, Color::BLUE);
shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
@@ -1294,8 +1102,8 @@
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleBuffers_BufferState) {
@@ -1308,8 +1116,8 @@
{
SCOPED_TRACE("set buffer 1");
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLUE, 32, 32));
@@ -1317,8 +1125,8 @@
{
SCOPED_TRACE("set buffer 2");
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLUE);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
@@ -1326,8 +1134,8 @@
{
SCOPED_TRACE("set buffer 3");
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
}
@@ -1342,7 +1150,6 @@
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer1, Color::RED, 64, 64));
- Transaction().setFrame(layer1, Rect(0, 0, 64, 64)).apply();
{
SCOPED_TRACE("set layer 1 buffer red");
auto shot = getScreenCapture();
@@ -1351,7 +1158,6 @@
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer2, Color::BLUE, 32, 32));
- Transaction().setFrame(layer2, Rect(0, 0, 32, 32)).apply();
{
SCOPED_TRACE("set layer 2 buffer blue");
auto shot = getScreenCapture();
@@ -1391,10 +1197,7 @@
size_t idx = 0;
for (auto& buffer : buffers) {
- buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
+ buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
Color color = colors[idx % colors.size()];
TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
idx++;
@@ -1409,8 +1212,8 @@
Color color = colors[idx % colors.size()];
auto shot = screenshot();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ shot->expectColor(Rect(0, 0, 32, 32), color);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
idx++;
}
@@ -1427,10 +1230,7 @@
size_t idx = 0;
for (auto& buffer : buffers) {
- buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
+ buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
Color color = colors[idx % colors.size()];
TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
idx++;
@@ -1445,8 +1245,8 @@
Color color = colors[idx % colors.size()];
auto shot = screenshot();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ shot->expectColor(Rect(0, 0, 32, 32), color);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
idx++;
}
@@ -1463,10 +1263,7 @@
size_t idx = 0;
for (auto& buffer : buffers) {
- buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
+ buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
Color color = colors[idx % colors.size()];
TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
idx++;
@@ -1481,8 +1278,8 @@
Color color = colors[idx % colors.size()];
auto shot = screenshot();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ shot->expectColor(Rect(0, 0, 32, 32), color);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
if (idx == 0) {
buffers[0].clear();
@@ -1500,7 +1297,6 @@
Color::BLUE, Color::WHITE));
Transaction()
- .setFrame(layer, Rect(0, 0, 32, 32))
.setTransform(layer, NATIVE_WINDOW_TRANSFORM_ROT_90)
.apply();
@@ -1517,7 +1313,6 @@
Color::BLUE, Color::WHITE));
Transaction()
- .setFrame(layer, Rect(0, 0, 32, 32))
.setTransform(layer, NATIVE_WINDOW_TRANSFORM_FLIP_H)
.apply();
@@ -1534,7 +1329,6 @@
Color::BLUE, Color::WHITE));
Transaction()
- .setFrame(layer, Rect(0, 0, 32, 32))
.setTransform(layer, NATIVE_WINDOW_TRANSFORM_FLIP_V)
.apply();
@@ -1549,10 +1343,7 @@
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
sp<GraphicBuffer> buffer =
- new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
+ new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
sp<Fence> fence;
@@ -1578,10 +1369,7 @@
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
sp<GraphicBuffer> buffer =
- new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
+ new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
sp<Fence> fence = Fence::NO_FENCE;
@@ -1589,8 +1377,8 @@
Transaction().setBuffer(layer, buffer).setAcquireFence(layer, fence).apply();
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
TEST_P(LayerRenderTypeTransactionTest, SetDataspaceBasic_BufferState) {
@@ -1599,17 +1387,14 @@
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
sp<GraphicBuffer> buffer =
- new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
+ new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
Transaction().setBuffer(layer, buffer).setDataspace(layer, ui::Dataspace::UNKNOWN).apply();
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
TEST_P(LayerRenderTypeTransactionTest, SetHdrMetadataBasic_BufferState) {
@@ -1618,10 +1403,7 @@
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
sp<GraphicBuffer> buffer =
- new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
+ new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
HdrMetadata hdrMetadata;
@@ -1629,8 +1411,8 @@
Transaction().setBuffer(layer, buffer).setHdrMetadata(layer, hdrMetadata).apply();
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
TEST_P(LayerRenderTypeTransactionTest, SetSurfaceDamageRegionBasic_BufferState) {
@@ -1639,10 +1421,7 @@
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
sp<GraphicBuffer> buffer =
- new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
+ new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
Region region;
@@ -1650,8 +1429,8 @@
Transaction().setBuffer(layer, buffer).setSurfaceDamageRegion(layer, region).apply();
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
TEST_P(LayerRenderTypeTransactionTest, SetApiBasic_BufferState) {
@@ -1660,17 +1439,14 @@
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
sp<GraphicBuffer> buffer =
- new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
+ new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
Transaction().setBuffer(layer, buffer).setApi(layer, NATIVE_WINDOW_API_CPU).apply();
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
TEST_P(LayerRenderTypeTransactionTest, SetColorTransformBasic) {
@@ -1679,7 +1455,7 @@
createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
ISurfaceComposerClient::eFXSurfaceEffect));
Transaction()
- .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+ .setCrop(colorLayer, Rect(0, 0, 32, 32))
.setLayer(colorLayer, mLayerZBase + 1)
.apply();
{
@@ -1736,8 +1512,8 @@
ISurfaceComposerClient::eFXSurfaceEffect, parentLayer.get()));
Transaction()
- .setCrop_legacy(parentLayer, Rect(0, 0, 100, 100))
- .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+ .setCrop(parentLayer, Rect(0, 0, 100, 100))
+ .setCrop(colorLayer, Rect(0, 0, 32, 32))
.setLayer(parentLayer, mLayerZBase + 1)
.apply();
{
@@ -1797,8 +1573,8 @@
ISurfaceComposerClient::eFXSurfaceEffect, parentLayer.get()));
Transaction()
- .setCrop_legacy(parentLayer, Rect(0, 0, 100, 100))
- .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+ .setCrop(parentLayer, Rect(0, 0, 100, 100))
+ .setCrop(colorLayer, Rect(0, 0, 32, 32))
.setLayer(parentLayer, mLayerZBase + 1)
.apply();
{
diff --git a/services/surfaceflinger/tests/LayerState_test.cpp b/services/surfaceflinger/tests/LayerState_test.cpp
new file mode 100644
index 0000000..fa1a5ed
--- /dev/null
+++ b/services/surfaceflinger/tests/LayerState_test.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2020 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 <gtest/gtest.h>
+
+#include <binder/Binder.h>
+#include <binder/Parcel.h>
+
+#include <gui/LayerState.h>
+
+namespace android {
+using gui::ScreenCaptureResults;
+
+namespace test {
+
+TEST(LayerStateTest, ParcellingDisplayCaptureArgs) {
+ DisplayCaptureArgs args;
+ args.pixelFormat = ui::PixelFormat::RGB_565;
+ args.sourceCrop = Rect(0, 0, 500, 200);
+ args.frameScaleX = 2;
+ args.frameScaleY = 4;
+ args.captureSecureLayers = true;
+ args.displayToken = new BBinder();
+ args.width = 10;
+ args.height = 20;
+ args.useIdentityTransform = true;
+ args.grayscale = true;
+
+ Parcel p;
+ args.write(p);
+ p.setDataPosition(0);
+
+ DisplayCaptureArgs args2;
+ args2.read(p);
+
+ ASSERT_EQ(args.pixelFormat, args2.pixelFormat);
+ ASSERT_EQ(args.sourceCrop, args2.sourceCrop);
+ ASSERT_EQ(args.frameScaleX, args2.frameScaleX);
+ ASSERT_EQ(args.frameScaleY, args2.frameScaleY);
+ ASSERT_EQ(args.captureSecureLayers, args2.captureSecureLayers);
+ ASSERT_EQ(args.displayToken, args2.displayToken);
+ ASSERT_EQ(args.width, args2.width);
+ ASSERT_EQ(args.height, args2.height);
+ ASSERT_EQ(args.useIdentityTransform, args2.useIdentityTransform);
+ ASSERT_EQ(args.grayscale, args2.grayscale);
+}
+
+TEST(LayerStateTest, ParcellingLayerCaptureArgs) {
+ LayerCaptureArgs args;
+ args.pixelFormat = ui::PixelFormat::RGB_565;
+ args.sourceCrop = Rect(0, 0, 500, 200);
+ args.frameScaleX = 2;
+ args.frameScaleY = 4;
+ args.captureSecureLayers = true;
+ args.layerHandle = new BBinder();
+ args.excludeHandles = {new BBinder(), new BBinder()};
+ args.childrenOnly = false;
+ args.grayscale = true;
+
+ Parcel p;
+ args.write(p);
+ p.setDataPosition(0);
+
+ LayerCaptureArgs args2;
+ args2.read(p);
+
+ ASSERT_EQ(args.pixelFormat, args2.pixelFormat);
+ ASSERT_EQ(args.sourceCrop, args2.sourceCrop);
+ ASSERT_EQ(args.frameScaleX, args2.frameScaleX);
+ ASSERT_EQ(args.frameScaleY, args2.frameScaleY);
+ ASSERT_EQ(args.captureSecureLayers, args2.captureSecureLayers);
+ ASSERT_EQ(args.layerHandle, args2.layerHandle);
+ ASSERT_EQ(args.excludeHandles, args2.excludeHandles);
+ ASSERT_EQ(args.childrenOnly, args2.childrenOnly);
+ ASSERT_EQ(args.grayscale, args2.grayscale);
+}
+
+TEST(LayerStateTest, ParcellingScreenCaptureResults) {
+ ScreenCaptureResults results;
+ results.buffer = new GraphicBuffer(100, 200, PIXEL_FORMAT_RGBA_8888, 1, 0);
+ results.fence = new Fence(dup(fileno(tmpfile())));
+ results.capturedSecureLayers = true;
+ results.capturedDataspace = ui::Dataspace::DISPLAY_P3;
+ results.result = BAD_VALUE;
+
+ Parcel p;
+ results.writeToParcel(&p);
+ p.setDataPosition(0);
+
+ ScreenCaptureResults results2;
+ results2.readFromParcel(&p);
+
+ // GraphicBuffer object is reallocated so compare the data in the graphic buffer
+ // rather than the object itself
+ ASSERT_EQ(results.buffer->getWidth(), results2.buffer->getWidth());
+ ASSERT_EQ(results.buffer->getHeight(), results2.buffer->getHeight());
+ ASSERT_EQ(results.buffer->getPixelFormat(), results2.buffer->getPixelFormat());
+ ASSERT_EQ(results.fence->isValid(), results2.fence->isValid());
+ ASSERT_EQ(results.capturedSecureLayers, results2.capturedSecureLayers);
+ ASSERT_EQ(results.capturedDataspace, results2.capturedDataspace);
+ ASSERT_EQ(results.result, results2.result);
+}
+
+} // namespace test
+} // namespace android
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index f3e11d8..0bc8fe7 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -16,11 +16,17 @@
#pragma once
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
+
+#include <cutils/properties.h>
#include <gtest/gtest.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
#include <private/gui/ComposerService.h>
-#include <ui/DisplayConfig.h>
+#include <ui/DisplayMode.h>
#include "BufferGenerator.h"
#include "utils/ScreenshotUtils.h"
@@ -40,6 +46,8 @@
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
ASSERT_NO_FATAL_FAILURE(sf->getColorManagement(&mColorManagementUsed));
+
+ mCaptureArgs.displayToken = mDisplay;
}
virtual void TearDown() {
@@ -73,8 +81,9 @@
PixelFormat format, uint32_t flags,
SurfaceControl* parent = nullptr,
uint32_t* outTransformHint = nullptr) {
- auto layer = client->createSurface(String8(name), width, height, format, flags, parent,
- LayerMetadata(), outTransformHint);
+ sp<IBinder> parentHandle = (parent) ? parent->getHandle() : nullptr;
+ auto layer = client->createSurface(String8(name), width, height, format, flags,
+ parentHandle, LayerMetadata(), outTransformHint);
EXPECT_NE(nullptr, layer.get()) << "failed to create SurfaceControl";
return layer;
}
@@ -116,7 +125,7 @@
}
virtual void fillBufferQueueLayerColor(const sp<SurfaceControl>& layer, const Color& color,
- int32_t bufferWidth, int32_t bufferHeight) {
+ uint32_t bufferWidth, uint32_t bufferHeight) {
ANativeWindow_Buffer buffer;
ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
TransactionUtils::fillANativeWindowBufferColor(buffer,
@@ -130,7 +139,7 @@
sp<GraphicBuffer> buffer =
new GraphicBuffer(bufferWidth, bufferHeight, PIXEL_FORMAT_RGBA_8888, 1,
BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
+ BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE,
"test");
TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, bufferWidth, bufferHeight),
color);
@@ -138,7 +147,7 @@
}
void fillLayerColor(uint32_t mLayerType, const sp<SurfaceControl>& layer, const Color& color,
- int32_t bufferWidth, int32_t bufferHeight) {
+ uint32_t bufferWidth, uint32_t bufferHeight) {
switch (mLayerType) {
case ISurfaceComposerClient::eFXSurfaceBufferQueue:
fillBufferQueueLayerColor(layer, color, bufferWidth, bufferHeight);
@@ -199,7 +208,7 @@
sp<GraphicBuffer> buffer =
new GraphicBuffer(bufferWidth, bufferHeight, PIXEL_FORMAT_RGBA_8888, 1,
BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
+ BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE,
"test");
ASSERT_TRUE(bufferWidth % 2 == 0 && bufferHeight % 2 == 0);
@@ -235,8 +244,25 @@
return bufferGenerator.get(outBuffer, outFence);
}
+ static ui::Size getBufferSize() {
+ static BufferGenerator bufferGenerator;
+ return bufferGenerator.getSize();
+ }
+
sp<SurfaceComposerClient> mClient;
+ bool deviceSupportsBlurs() {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("ro.surface_flinger.supports_background_blur", value, "0");
+ return atoi(value);
+ }
+
+ bool deviceUsesSkiaRenderEngine() {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("debug.renderengine.backend", value, "default");
+ return strstr(value, "skia") != nullptr;
+ }
+
sp<IBinder> mDisplay;
uint32_t mDisplayWidth;
uint32_t mDisplayHeight;
@@ -249,21 +275,24 @@
sp<SurfaceControl> mBlackBgSurface;
bool mColorManagementUsed;
+ DisplayCaptureArgs mCaptureArgs;
+ ScreenCaptureResults mCaptureResults;
+
private:
void SetUpDisplay() {
mDisplay = mClient->getInternalDisplayToken();
ASSERT_FALSE(mDisplay == nullptr) << "failed to get display";
- DisplayConfig config;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(mDisplay, &config));
- mDisplayRect = Rect(config.resolution);
+ ui::DisplayMode mode;
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(mDisplay, &mode));
+ mDisplayRect = Rect(mode.resolution);
mDisplayWidth = mDisplayRect.getWidth();
mDisplayHeight = mDisplayRect.getHeight();
// After a new buffer is queued, SurfaceFlinger is notified and will
// latch the new buffer on next vsync. Let's heuristically wait for 3
// vsyncs.
- mBufferPostDelay = static_cast<int32_t>(1e6 / config.refreshRate) * 3;
+ mBufferPostDelay = static_cast<int32_t>(1e6 / mode.refreshRate) * 3;
mDisplayLayerStack = 0;
@@ -274,7 +303,7 @@
// set layer stack (b/68888219)
Transaction t;
t.setDisplayLayerStack(mDisplay, mDisplayLayerStack);
- t.setCrop_legacy(mBlackBgSurface, Rect(0, 0, mDisplayWidth, mDisplayHeight));
+ t.setCrop(mBlackBgSurface, Rect(0, 0, mDisplayWidth, mDisplayHeight));
t.setLayerStack(mBlackBgSurface, mDisplayLayerStack);
t.setColor(mBlackBgSurface, half3{0, 0, 0});
t.setLayer(mBlackBgSurface, mLayerZBase);
@@ -294,3 +323,6 @@
};
} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/tests/LayerTransaction_test.cpp b/services/surfaceflinger/tests/LayerTransaction_test.cpp
index 1f8f7ed..ef992d6 100644
--- a/services/surfaceflinger/tests/LayerTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTransaction_test.cpp
@@ -18,7 +18,6 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
-#include <private/android_filesystem_config.h>
#include <thread>
#include "LayerTransactionTest.h"
@@ -26,43 +25,6 @@
using android::hardware::graphics::common::V1_1::BufferUsage;
-TEST_F(LayerTransactionTest, SetFlagsSecureEUidSystem) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
- sp<ISurfaceComposer> composer = ComposerService::getComposerService();
- sp<GraphicBuffer> outBuffer;
- Transaction()
- .setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure)
- .apply(true);
- ASSERT_EQ(PERMISSION_DENIED,
- composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
-
- UIDFaker f(AID_SYSTEM);
-
- // By default the system can capture screenshots with secure layers but they
- // will be blacked out
- ASSERT_EQ(NO_ERROR, composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
-
- {
- SCOPED_TRACE("as system");
- auto shot = screenshot();
- shot->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
- }
-
- // Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able
- // to receive them...we are expected to take care with the results.
- bool outCapturedSecureLayers;
- ASSERT_EQ(NO_ERROR,
- composer->captureScreen(mDisplay, &outBuffer, outCapturedSecureLayers,
- ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(), 0,
- 0, false, ui::ROTATION_0, true));
- ASSERT_EQ(true, outCapturedSecureLayers);
- ScreenCapture sc(outBuffer);
- sc.expectColor(Rect(0, 0, 32, 32), Color::RED);
-}
-
TEST_F(LayerTransactionTest, SetTransformToDisplayInverse_BufferState) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(
@@ -88,7 +50,7 @@
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
- Transaction().reparent(layer, layer->getHandle()).apply();
+ Transaction().reparent(layer, layer).apply();
{
// We expect the transaction to be silently dropped, but for SurfaceFlinger
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index 7d4314f..2828d61 100644
--- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -18,7 +18,6 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
-#include <cutils/properties.h>
#include <gui/BufferItemConsumer.h>
#include "TransactionTestHarnesses.h"
@@ -40,6 +39,9 @@
protected:
LayerRenderPathTestHarness mRenderPathHarness;
+
+ static constexpr int64_t kUsageFlags = BufferUsage::CPU_READ_OFTEN |
+ BufferUsage::CPU_WRITE_OFTEN | BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE;
};
::testing::Environment* const binderEnv =
@@ -49,14 +51,9 @@
LayerTypeAndRenderTypeTransactionTests, LayerTypeAndRenderTypeTransactionTest,
::testing::Combine(
::testing::Values(
- static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferQueue),
static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferState)),
::testing::Values(RenderPath::VIRTUAL_DISPLAY, RenderPath::SCREENSHOT)));
-TEST_P(LayerTypeAndRenderTypeTransactionTest, SetSizeInvalid) {
- // cannot test robustness against invalid sizes (zero or really huge)
-}
-
TEST_P(LayerTypeAndRenderTypeTransactionTest, SetZBasic) {
sp<SurfaceControl> layerR;
sp<SurfaceControl> layerG;
@@ -87,10 +84,7 @@
ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
- Transaction()
- .setPosition(layerG, 16, 16)
- .setRelativeLayer(layerG, layerR->getHandle(), 1)
- .apply();
+ Transaction().setPosition(layerG, 16, 16).setRelativeLayer(layerG, layerR, 1).apply();
Transaction().reparent(layerG, nullptr).apply();
@@ -146,7 +140,7 @@
sp<SurfaceControl> parent =
LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
ISurfaceComposerClient::eFXSurfaceContainer);
- Transaction().setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
+ Transaction().setCrop(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
sp<SurfaceControl> layerR;
sp<SurfaceControl> layerG;
ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
@@ -154,10 +148,7 @@
ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
- Transaction()
- .reparent(layerR, parent->getHandle())
- .reparent(layerG, parent->getHandle())
- .apply();
+ Transaction().reparent(layerR, parent).reparent(layerG, parent).apply();
Transaction().setLayer(layerR, -1).setLayer(layerG, -2).apply();
{
SCOPED_TRACE("layerR");
@@ -200,17 +191,7 @@
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", size, size));
ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, size, size));
- if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
- Transaction()
- .setCornerRadius(layer, cornerRadius)
- .setCrop_legacy(layer, Rect(0, 0, size, size))
- .apply();
- } else {
- Transaction()
- .setCornerRadius(layer, cornerRadius)
- .setFrame(layer, Rect(0, 0, size, size))
- .apply();
- }
+ Transaction().setCornerRadius(layer, cornerRadius).apply();
{
const uint8_t bottom = size - 1;
const uint8_t right = size - 1;
@@ -238,19 +219,13 @@
ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size));
ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size));
- auto transaction = Transaction()
- .setCornerRadius(parent, cornerRadius)
- .setCrop_legacy(parent, Rect(0, 0, size, size))
- .reparent(child, parent->getHandle())
- .setPosition(child, 0, size)
- // Rotate by half PI
- .setMatrix(child, 0.0f, -1.0f, 1.0f, 0.0f);
- if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
- transaction.setCrop_legacy(parent, Rect(0, 0, size, size));
- } else {
- transaction.setFrame(parent, Rect(0, 0, size, size));
- }
- transaction.apply();
+ Transaction()
+ .setCornerRadius(parent, cornerRadius)
+ .reparent(child, parent)
+ .setPosition(child, 0, size)
+ // Rotate by half PI
+ .setMatrix(child, 0.0f, -1.0f, 1.0f, 0.0f)
+ .apply();
{
const uint8_t bottom = size - 1;
@@ -279,21 +254,12 @@
ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size / 2));
ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size / 2));
- if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
- Transaction()
- .setCornerRadius(parent, cornerRadius)
- .setCrop_legacy(parent, Rect(0, 0, size, size))
- .reparent(child, parent->getHandle())
- .setPosition(child, 0, size / 2)
- .apply();
- } else {
- Transaction()
- .setCornerRadius(parent, cornerRadius)
- .setFrame(parent, Rect(0, 0, size, size))
- .reparent(child, parent->getHandle())
- .setFrame(child, Rect(0, size / 2, size, size))
- .apply();
- }
+ Transaction()
+ .setCornerRadius(parent, cornerRadius)
+ .reparent(child, parent)
+ .setPosition(child, 0, size / 2)
+ .apply();
+
{
const uint8_t bottom = size - 1;
const uint8_t right = size - 1;
@@ -307,47 +273,81 @@
}
}
-TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBackgroundBlurRadius) {
- char value[PROPERTY_VALUE_MAX];
- property_get("ro.surface_flinger.supports_background_blur", value, "0");
- if (!atoi(value)) {
- // This device doesn't support blurs, no-op.
- return;
- }
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBackgroundBlurRadiusSimple) {
+ if (!deviceSupportsBlurs()) GTEST_SKIP();
+ if (!deviceUsesSkiaRenderEngine()) GTEST_SKIP();
- auto size = 256;
- auto center = size / 2;
- auto blurRadius = 50;
-
- sp<SurfaceControl> backgroundLayer;
- ASSERT_NO_FATAL_FAILURE(backgroundLayer = createLayer("background", size, size));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(backgroundLayer, Color::GREEN, size, size));
+ const auto canvasSize = 256;
sp<SurfaceControl> leftLayer;
- ASSERT_NO_FATAL_FAILURE(leftLayer = createLayer("left", size / 2, size));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(leftLayer, Color::RED, size / 2, size));
-
+ sp<SurfaceControl> rightLayer;
+ sp<SurfaceControl> greenLayer;
sp<SurfaceControl> blurLayer;
- ASSERT_NO_FATAL_FAILURE(blurLayer = createLayer("blur", size, size));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(blurLayer, Color::TRANSPARENT, size, size));
+ const auto leftRect = Rect(0, 0, canvasSize / 2, canvasSize);
+ const auto rightRect = Rect(canvasSize / 2, 0, canvasSize, canvasSize);
+ const auto blurRect = Rect(0, 0, canvasSize, canvasSize);
- Transaction().setBackgroundBlurRadius(blurLayer, blurRadius).apply();
+ ASSERT_NO_FATAL_FAILURE(leftLayer =
+ createLayer("Left", leftRect.getWidth(), leftRect.getHeight()));
+ ASSERT_NO_FATAL_FAILURE(
+ fillLayerColor(leftLayer, Color::BLUE, leftRect.getWidth(), leftRect.getHeight()));
+ ASSERT_NO_FATAL_FAILURE(greenLayer = createLayer("Green", canvasSize * 2, canvasSize * 2));
+ ASSERT_NO_FATAL_FAILURE(
+ fillLayerColor(greenLayer, Color::GREEN, canvasSize * 2, canvasSize * 2));
+ ASSERT_NO_FATAL_FAILURE(
+ rightLayer = createLayer("Right", rightRect.getWidth(), rightRect.getHeight()));
+ ASSERT_NO_FATAL_FAILURE(
+ fillLayerColor(rightLayer, Color::RED, rightRect.getWidth(), rightRect.getHeight()));
- auto shot = getScreenCapture();
- // Edges are mixed
- shot->expectColor(Rect(center - 1, center - 5, center, center + 5), Color{150, 150, 0, 255},
- 50 /* tolerance */);
- shot->expectColor(Rect(center, center - 5, center + 1, center + 5), Color{150, 150, 0, 255},
- 50 /* tolerance */);
+ Transaction()
+ .setLayer(greenLayer, mLayerZBase)
+ .setLayer(leftLayer, mLayerZBase + 1)
+ .setLayer(rightLayer, mLayerZBase + 2)
+ .setPosition(rightLayer, rightRect.left, rightRect.top)
+ .apply();
+
+ {
+ auto shot = getScreenCapture();
+ shot->expectColor(leftRect, Color::BLUE);
+ shot->expectColor(rightRect, Color::RED);
+ }
+
+ ASSERT_NO_FATAL_FAILURE(blurLayer = createColorLayer("BackgroundBlur", Color::TRANSPARENT));
+
+ const auto blurRadius = canvasSize / 2;
+ Transaction()
+ .setLayer(blurLayer, mLayerZBase + 3)
+ .setBackgroundBlurRadius(blurLayer, blurRadius)
+ .setCrop(blurLayer, blurRect)
+ .setAlpha(blurLayer, 0.0f)
+ .apply();
+
+ {
+ auto shot = getScreenCapture();
+
+ const auto stepSize = 1;
+ const auto blurAreaOffset = blurRadius * 0.7f;
+ const auto blurAreaStartX = canvasSize / 2 - blurRadius + blurAreaOffset;
+ const auto blurAreaEndX = canvasSize / 2 + blurRadius - blurAreaOffset;
+ Color previousColor;
+ Color currentColor;
+ for (int y = 0; y < canvasSize; y++) {
+ shot->checkPixel(0, y, /* r = */ 0, /* g = */ 0, /* b = */ 255);
+ previousColor = shot->getPixelColor(0, y);
+ for (int x = blurAreaStartX; x < blurAreaEndX; x += stepSize) {
+ currentColor = shot->getPixelColor(x, y);
+ ASSERT_GT(currentColor.r, previousColor.r);
+ ASSERT_LT(currentColor.b, previousColor.b);
+ ASSERT_EQ(0, currentColor.g);
+ }
+ shot->checkPixel(canvasSize - 1, y, 255, 0, 0);
+ }
+ }
}
TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBackgroundBlurRadiusOnMultipleLayers) {
- char value[PROPERTY_VALUE_MAX];
- property_get("ro.surface_flinger.supports_background_blur", value, "0");
- if (!atoi(value)) {
- // This device doesn't support blurs, no-op.
- return;
- }
+ if (!deviceSupportsBlurs()) GTEST_SKIP();
+ if (!deviceUsesSkiaRenderEngine()) GTEST_SKIP();
auto size = 256;
auto center = size / 2;
@@ -383,6 +383,59 @@
40 /* tolerance */);
}
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBackgroundBlurAffectedByParentAlpha) {
+ if (!deviceSupportsBlurs()) GTEST_SKIP();
+ if (!deviceUsesSkiaRenderEngine()) GTEST_SKIP();
+
+ sp<SurfaceControl> left;
+ sp<SurfaceControl> right;
+ sp<SurfaceControl> blur;
+ sp<SurfaceControl> blurParent;
+
+ const auto size = 256;
+ ASSERT_NO_FATAL_FAILURE(left = createLayer("Left", size, size));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(left, Color::BLUE, size, size));
+ ASSERT_NO_FATAL_FAILURE(right = createLayer("Right", size, size));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(right, Color::RED, size, size));
+
+ Transaction()
+ .setLayer(left, mLayerZBase + 1)
+ .setLayer(right, mLayerZBase + 2)
+ .setPosition(right, size, 0)
+ .apply();
+
+ {
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, size, size), Color::BLUE);
+ shot->expectColor(Rect(size, 0, size * 2, size), Color::RED);
+ }
+
+ ASSERT_NO_FATAL_FAILURE(blur = createLayer("BackgroundBlur", size * 2, size));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(blur, Color::TRANSPARENT, size * 2, size));
+ ASSERT_NO_FATAL_FAILURE(blurParent = createLayer("BackgroundBlurParent", size * 2, size));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(blurParent, Color::TRANSPARENT, size * 2, size));
+
+ Transaction()
+ .setLayer(blurParent, mLayerZBase + 3)
+ .setAlpha(blurParent, 0.5)
+ .setLayer(blur, mLayerZBase + 4)
+ .setBackgroundBlurRadius(blur, size) // set the blur radius to the size of one rect
+ .reparent(blur, blurParent)
+ .apply();
+
+ {
+ auto shot = getScreenCapture();
+ // assert that outer sides of the red and blue rects are not blended with the other color;
+ // if the blur didn't take into account parent alpha, the outer sides would have traces of
+ // the other color
+ shot->expectColor(Rect(0, 0, size / 2, size), Color::BLUE);
+ shot->expectColor(Rect(size + size / 2, 0, size * 2, size), Color::RED);
+ // assert that middle line has blended red and blur color; adding a tolerance of 10 to
+ // account for future blur algorithm changes
+ shot->expectColor(Rect(size, 0, size + 1, size), {136, 0, 119, 255}, 10);
+ }
+}
+
TEST_P(LayerTypeAndRenderTypeTransactionTest, SetColorWithBuffer) {
sp<SurfaceControl> bufferLayer;
ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test", 32, 32));
@@ -425,7 +478,7 @@
.setLayer(layer, INT32_MAX - 1)
.show(layer)
.setLayerStack(behindLayer, mDisplayLayerStack)
- .setCrop_legacy(behindLayer, crop)
+ .setCrop(behindLayer, crop)
.setLayer(behindLayer, INT32_MAX - 2)
.show(behindLayer)
.apply();
@@ -433,10 +486,7 @@
sp<Surface> surface = layer->getSurface();
sp<GraphicBuffer> buffer =
- new GraphicBuffer(width, height, PIXEL_FORMAT_RGBX_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
+ new GraphicBuffer(width, height, PIXEL_FORMAT_RGBX_8888, 1, kUsageFlags, "test");
ASSERT_NO_FATAL_FAILURE(
TransactionUtils::fillGraphicBufferColor(buffer, crop, Color::TRANSPARENT));
@@ -452,10 +502,7 @@
shot->expectColor(crop, Color::BLACK);
}
- buffer = new GraphicBuffer(width, height, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
+ buffer = new GraphicBuffer(width, height, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
ASSERT_NO_FATAL_FAILURE(
TransactionUtils::fillGraphicBufferColor(buffer, crop, Color::TRANSPARENT));
diff --git a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
index 84780ba..4753362 100644
--- a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
@@ -43,7 +43,7 @@
sp<SurfaceControl> parent =
LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
ISurfaceComposerClient::eFXSurfaceContainer);
- Transaction().setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
+ Transaction().setCrop(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
sp<SurfaceControl> layerR;
sp<SurfaceControl> layerG;
sp<SurfaceControl> layerB;
@@ -54,15 +54,17 @@
ASSERT_NO_FATAL_FAILURE(layerB = createLayer("test B", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerB, Color::BLUE, 32, 32));
- Transaction().reparent(layerB, parent->getHandle()).apply();
+ Transaction().reparent(layerB, parent).apply();
// layerR = mLayerZBase, layerG = layerR - 1, layerB = -2
- Transaction().setRelativeLayer(layerG, layerR->getHandle(), -1).setLayer(layerB, -2).apply();
+ Transaction().setRelativeLayer(layerG, layerR, -1).setLayer(layerB, -2).apply();
std::unique_ptr<ScreenCapture> screenshot;
// only layerB is in this range
- sp<IBinder> parentHandle = parent->getHandle();
- ScreenCapture::captureLayers(&screenshot, parentHandle, Rect(0, 0, 32, 32));
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = parent->getHandle();
+ captureArgs.sourceCrop = {0, 0, 32, 32};
+ ScreenCapture::captureLayers(&screenshot, captureArgs);
screenshot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
}
@@ -82,14 +84,11 @@
.setColor(parent, half3{0.0f, 0.0f, 0.0f})
.show(childLayer)
.show(parent)
- .setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight))
- .setCrop_legacy(childLayer, Rect(0, 0, 20, 30))
+ .setCrop(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight))
+ .setCrop(childLayer, Rect(0, 0, 20, 30))
.apply();
- Transaction()
- .setRelativeLayer(childLayer, parent->getHandle(), -1)
- .setLayer(childLayer, 1)
- .apply();
+ Transaction().setRelativeLayer(childLayer, parent, -1).setLayer(childLayer, 1).apply();
{
SCOPED_TRACE("setLayer above");
@@ -99,10 +98,7 @@
screenshot->expectColor(Rect(0, 0, 20, 30), Color::RED);
}
- Transaction()
- .setLayer(childLayer, 1)
- .setRelativeLayer(childLayer, parent->getHandle(), -1)
- .apply();
+ Transaction().setLayer(childLayer, 1).setRelativeLayer(childLayer, parent, -1).apply();
{
SCOPED_TRACE("setRelative below");
@@ -139,7 +135,7 @@
.setLayer(relativeParent, mLayerZBase)
.apply();
- Transaction().setRelativeLayer(childLayer, relativeParent->getHandle(), 1).apply();
+ Transaction().setRelativeLayer(childLayer, relativeParent, 1).apply();
{
SCOPED_TRACE("setLayer above");
@@ -165,17 +161,21 @@
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
- sp<ISurfaceComposer> composer = ComposerService::getComposerService();
sp<GraphicBuffer> outBuffer;
Transaction()
.setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure)
.apply(true);
- ASSERT_EQ(PERMISSION_DENIED,
- composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
+
+ DisplayCaptureArgs args;
+ args.displayToken = mDisplay;
+
+ ScreenCaptureResults captureResults;
+ ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(args, captureResults));
Transaction().setFlags(layer, 0, layer_state_t::eLayerSecure).apply(true);
- ASSERT_EQ(NO_ERROR, composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(args, captureResults));
}
+
TEST_P(LayerTypeTransactionTest, RefreshRateIsInitialized) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
index cdd9d92..ee4d367 100644
--- a/services/surfaceflinger/tests/LayerUpdate_test.cpp
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -36,9 +36,9 @@
const auto display = SurfaceComposerClient::getInternalDisplayToken();
ASSERT_FALSE(display == nullptr);
- DisplayConfig config;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
- const ui::Size& resolution = config.resolution;
+ ui::DisplayMode mode;
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+ const ui::Size& resolution = mode.resolution;
// Background surface
mBGSurfaceControl = createLayer(String8("BG Test Surface"), resolution.getWidth(),
@@ -103,41 +103,6 @@
sp<SurfaceControl> mSyncSurfaceControl;
};
-TEST_F(LayerUpdateTest, RelativesAreNotDetached) {
- std::unique_ptr<ScreenCapture> sc;
-
- sp<SurfaceControl> relative = createLayer(String8("relativeTestSurface"), 10, 10, 0);
- TransactionUtils::fillSurfaceRGBA8(relative, 10, 10, 10);
- waitForPostedBuffers();
-
- Transaction{}
- .setRelativeLayer(relative, mFGSurfaceControl->getHandle(), 1)
- .setPosition(relative, 64, 64)
- .apply();
-
- {
- // The relative should be on top of the FG control.
- ScreenCapture::captureScreen(&sc);
- sc->checkPixel(64, 64, 10, 10, 10);
- }
- Transaction{}.detachChildren(mFGSurfaceControl).apply();
-
- {
- // Nothing should change at this point.
- ScreenCapture::captureScreen(&sc);
- sc->checkPixel(64, 64, 10, 10, 10);
- }
-
- Transaction{}.hide(relative).apply();
-
- {
- // Ensure that the relative was actually hidden, rather than
- // being left in the detached but visible state.
- ScreenCapture::captureScreen(&sc);
- sc->expectFGColor(64, 64);
- }
-}
-
class GeometryLatchingTest : public LayerUpdateTest {
protected:
void EXPECT_INITIAL_STATE(const char* trace) {
@@ -162,15 +127,7 @@
TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
waitForPostedBuffers();
}
- void restoreInitialState() {
- asTransaction([&](Transaction& t) {
- t.setSize(mFGSurfaceControl, 64, 64);
- t.setPosition(mFGSurfaceControl, 64, 64);
- t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 64, 64));
- });
- EXPECT_INITIAL_STATE("After restoring initial state");
- }
std::unique_ptr<ScreenCapture> sc;
};
@@ -195,61 +152,6 @@
}
};
-TEST_F(LayerUpdateTest, DeferredTransactionTest) {
- std::unique_ptr<ScreenCapture> sc;
- {
- SCOPED_TRACE("before anything");
- ScreenCapture::captureScreen(&sc);
- sc->expectBGColor(32, 32);
- sc->expectFGColor(96, 96);
- sc->expectBGColor(160, 160);
- }
-
- // set up two deferred transactions on different frames
- asTransaction([&](Transaction& t) {
- t.setAlpha(mFGSurfaceControl, 0.75);
- t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(),
- mSyncSurfaceControl->getSurface()->getNextFrameNumber());
- });
-
- asTransaction([&](Transaction& t) {
- t.setPosition(mFGSurfaceControl, 128, 128);
- t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(),
- mSyncSurfaceControl->getSurface()->getNextFrameNumber() + 1);
- });
-
- {
- SCOPED_TRACE("before any trigger");
- ScreenCapture::captureScreen(&sc);
- sc->expectBGColor(32, 32);
- sc->expectFGColor(96, 96);
- sc->expectBGColor(160, 160);
- }
-
- // should trigger the first deferred transaction, but not the second one
- TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
- {
- SCOPED_TRACE("after first trigger");
- ScreenCapture::captureScreen(&sc);
- sc->expectBGColor(32, 32);
- sc->checkPixel(96, 96, 162, 63, 96);
- sc->expectBGColor(160, 160);
- }
-
- // should show up immediately since it's not deferred
- asTransaction([&](Transaction& t) { t.setAlpha(mFGSurfaceControl, 1.0); });
-
- // trigger the second deferred transaction
- TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
- {
- SCOPED_TRACE("after second trigger");
- ScreenCapture::captureScreen(&sc);
- sc->expectBGColor(32, 32);
- sc->expectBGColor(96, 96);
- sc->expectFGColor(160, 160);
- }
-}
-
TEST_F(LayerUpdateTest, LayerWithNoBuffersResizesImmediately) {
std::unique_ptr<ScreenCapture> sc;
@@ -260,7 +162,7 @@
PIXEL_FORMAT_RGBA_8888, 0, childNoBuffer.get());
TransactionUtils::fillSurfaceRGBA8(childBuffer, 200, 200, 200);
SurfaceComposerClient::Transaction{}
- .setCrop_legacy(childNoBuffer, Rect(0, 0, 10, 10))
+ .setCrop(childNoBuffer, Rect(0, 0, 10, 10))
.show(childNoBuffer)
.show(childBuffer)
.apply(true);
@@ -269,9 +171,7 @@
sc->expectChildColor(73, 73);
sc->expectFGColor(74, 74);
}
- SurfaceComposerClient::Transaction{}
- .setCrop_legacy(childNoBuffer, Rect(0, 0, 20, 20))
- .apply(true);
+ SurfaceComposerClient::Transaction{}.setCrop(childNoBuffer, Rect(0, 0, 20, 20)).apply(true);
{
ScreenCapture::captureScreen(&sc);
sc->expectChildColor(73, 73);
@@ -334,6 +234,7 @@
mChild = createSurface(mClient, "Child surface", 10, 15, PIXEL_FORMAT_RGBA_8888, 0,
mFGSurfaceControl.get());
TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200);
+ waitForPostedBuffers();
{
SCOPED_TRACE("before anything");
@@ -385,7 +286,7 @@
t.show(mChild);
t.setPosition(mChild, 0, 0);
t.setPosition(mFGSurfaceControl, 0, 0);
- t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 5, 5));
+ t.setCrop(mFGSurfaceControl, Rect(0, 0, 5, 5));
});
{
@@ -485,49 +386,16 @@
{
mCapture = screenshot();
- // Child and BG blended.
- mCapture->checkPixel(0, 0, 127, 127, 0);
+ // Child and BG blended. See b/175352694 for tolerance.
+ mCapture->expectColor(Rect(0, 0, 1, 1), Color{127, 127, 0, 255}, 1);
}
asTransaction([&](Transaction& t) { t.setAlpha(mFGSurfaceControl, 0.5); });
{
mCapture = screenshot();
- // Child and BG blended.
- mCapture->checkPixel(0, 0, 95, 64, 95);
- }
-}
-
-TEST_F(ChildLayerTest, ReparentChildren) {
- asTransaction([&](Transaction& t) {
- t.show(mChild);
- t.setPosition(mChild, 10, 10);
- t.setPosition(mFGSurfaceControl, 64, 64);
- });
-
- {
- mCapture = screenshot();
- // Top left of foreground must now be visible
- mCapture->expectFGColor(64, 64);
- // But 10 pixels in we should see the child surface
- mCapture->expectChildColor(74, 74);
- // And 10 more pixels we should be back to the foreground surface
- mCapture->expectFGColor(84, 84);
- }
-
- asTransaction([&](Transaction& t) {
- t.reparentChildren(mFGSurfaceControl, mBGSurfaceControl->getHandle());
- });
-
- {
- mCapture = screenshot();
- mCapture->expectFGColor(64, 64);
- // In reparenting we should have exposed the entire foreground surface.
- mCapture->expectFGColor(74, 74);
- // And the child layer should now begin at 10, 10 (since the BG
- // layer is at (0, 0)).
- mCapture->expectBGColor(9, 9);
- mCapture->expectChildColor(10, 10);
+ // Child and BG blended. See b/175352694 for tolerance.
+ mCapture->expectColor(Rect(0, 0, 1, 1), Color{95, 64, 95, 255}, 1);
}
}
@@ -551,7 +419,7 @@
mCapture->expectFGColor(64, 64);
}
- asTransaction([&](Transaction& t) { t.reparent(mGrandChild, mFGSurfaceControl->getHandle()); });
+ asTransaction([&](Transaction& t) { t.reparent(mGrandChild, mFGSurfaceControl); });
{
SCOPED_TRACE("After reparenting grandchild");
@@ -566,9 +434,7 @@
TransactionUtils::fillSurfaceRGBA8(mGrandChild, 111, 111, 111);
// draw grand child behind the foreground surface
- asTransaction([&](Transaction& t) {
- t.setRelativeLayer(mGrandChild, mFGSurfaceControl->getHandle(), -1);
- });
+ asTransaction([&](Transaction& t) { t.setRelativeLayer(mGrandChild, mFGSurfaceControl, -1); });
{
SCOPED_TRACE("Child visible");
@@ -578,7 +444,7 @@
asTransaction([&](Transaction& t) {
t.reparent(mChild, nullptr);
- t.reparentChildren(mChild, mFGSurfaceControl->getHandle());
+ t.reparent(mGrandChild, mFGSurfaceControl);
});
{
@@ -588,380 +454,6 @@
}
}
-TEST_F(ChildLayerTest, DetachChildrenSameClient) {
- asTransaction([&](Transaction& t) {
- t.show(mChild);
- t.setPosition(mChild, 10, 10);
- t.setPosition(mFGSurfaceControl, 64, 64);
- });
-
- {
- mCapture = screenshot();
- // Top left of foreground must now be visible
- mCapture->expectFGColor(64, 64);
- // But 10 pixels in we should see the child surface
- mCapture->expectChildColor(74, 74);
- // And 10 more pixels we should be back to the foreground surface
- mCapture->expectFGColor(84, 84);
- }
-
- asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); });
-
- asTransaction([&](Transaction& t) { t.hide(mChild); });
-
- // Since the child has the same client as the parent, it will not get
- // detached and will be hidden.
- {
- mCapture = screenshot();
- mCapture->expectFGColor(64, 64);
- mCapture->expectFGColor(74, 74);
- mCapture->expectFGColor(84, 84);
- }
-}
-
-TEST_F(ChildLayerTest, DetachChildrenDifferentClient) {
- sp<SurfaceComposerClient> mNewComposerClient = new SurfaceComposerClient;
- sp<SurfaceControl> mChildNewClient =
- createSurface(mNewComposerClient, "New Child Test Surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-
- ASSERT_TRUE(mChildNewClient->isValid());
-
- TransactionUtils::fillSurfaceRGBA8(mChildNewClient, 200, 200, 200);
-
- asTransaction([&](Transaction& t) {
- t.hide(mChild);
- t.show(mChildNewClient);
- t.setPosition(mChildNewClient, 10, 10);
- t.setPosition(mFGSurfaceControl, 64, 64);
- });
-
- {
- mCapture = screenshot();
- // Top left of foreground must now be visible
- mCapture->expectFGColor(64, 64);
- // But 10 pixels in we should see the child surface
- mCapture->expectChildColor(74, 74);
- // And 10 more pixels we should be back to the foreground surface
- mCapture->expectFGColor(84, 84);
- }
-
- asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); });
-
- asTransaction([&](Transaction& t) { t.hide(mChildNewClient); });
-
- // Nothing should have changed.
- {
- mCapture = screenshot();
- mCapture->expectFGColor(64, 64);
- mCapture->expectChildColor(74, 74);
- mCapture->expectFGColor(84, 84);
- }
-}
-
-TEST_F(ChildLayerTest, DetachChildrenThenAttach) {
- sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
- sp<SurfaceControl> childNewClient =
- newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-
- ASSERT_TRUE(childNewClient != nullptr);
- ASSERT_TRUE(childNewClient->isValid());
-
- TransactionUtils::fillSurfaceRGBA8(childNewClient, 200, 200, 200);
-
- Transaction()
- .hide(mChild)
- .show(childNewClient)
- .setPosition(childNewClient, 10, 10)
- .setPosition(mFGSurfaceControl, 64, 64)
- .apply();
-
- {
- mCapture = screenshot();
- // Top left of foreground must now be visible
- mCapture->expectFGColor(64, 64);
- // But 10 pixels in we should see the child surface
- mCapture->expectChildColor(74, 74);
- // And 10 more pixels we should be back to the foreground surface
- mCapture->expectFGColor(84, 84);
- }
-
- Transaction().detachChildren(mFGSurfaceControl).apply();
- Transaction().hide(childNewClient).apply();
-
- // Nothing should have changed.
- {
- mCapture = screenshot();
- mCapture->expectFGColor(64, 64);
- mCapture->expectChildColor(74, 74);
- mCapture->expectFGColor(84, 84);
- }
-
- sp<SurfaceControl> newParentSurface = createLayer(String8("New Parent Surface"), 32, 32, 0);
- fillLayerColor(ISurfaceComposerClient::eFXSurfaceBufferQueue, newParentSurface, Color::RED, 32,
- 32);
- Transaction()
- .setLayer(newParentSurface, INT32_MAX - 1)
- .show(newParentSurface)
- .setPosition(newParentSurface, 20, 20)
- .reparent(childNewClient, newParentSurface->getHandle())
- .apply();
- {
- mCapture = screenshot();
- // Child is now hidden.
- mCapture->expectColor(Rect(20, 20, 52, 52), Color::RED);
- }
-}
-TEST_F(ChildLayerTest, DetachChildrenWithDeferredTransaction) {
- sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
- sp<SurfaceControl> childNewClient =
- newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-
- ASSERT_TRUE(childNewClient != nullptr);
- ASSERT_TRUE(childNewClient->isValid());
-
- TransactionUtils::fillSurfaceRGBA8(childNewClient, 200, 200, 200);
-
- Transaction()
- .hide(mChild)
- .show(childNewClient)
- .setPosition(childNewClient, 10, 10)
- .setPosition(mFGSurfaceControl, 64, 64)
- .apply();
-
- {
- mCapture = screenshot();
- Rect rect = Rect(74, 74, 84, 84);
- mCapture->expectBorder(rect, Color{195, 63, 63, 255});
- mCapture->expectColor(rect, Color{200, 200, 200, 255});
- }
-
- Transaction()
- .deferTransactionUntil_legacy(childNewClient, mFGSurfaceControl->getHandle(),
- mFGSurfaceControl->getSurface()->getNextFrameNumber())
- .apply();
- Transaction().detachChildren(mFGSurfaceControl).apply();
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(mFGSurfaceControl, Color::RED, 32, 32));
-
- // BufferLayer can still dequeue buffers even though there's a detached layer with a
- // deferred transaction.
- {
- SCOPED_TRACE("new buffer");
- mCapture = screenshot();
- Rect rect = Rect(74, 74, 84, 84);
- mCapture->expectBorder(rect, Color::RED);
- mCapture->expectColor(rect, Color{200, 200, 200, 255});
- }
-}
-
-TEST_F(ChildLayerTest, ChildrenInheritNonTransformScalingFromParent) {
- asTransaction([&](Transaction& t) {
- t.show(mChild);
- t.setPosition(mChild, 0, 0);
- t.setPosition(mFGSurfaceControl, 0, 0);
- });
-
- {
- mCapture = screenshot();
- // We've positioned the child in the top left.
- mCapture->expectChildColor(0, 0);
- // But it's only 10x15.
- mCapture->expectFGColor(10, 15);
- }
-
- asTransaction([&](Transaction& t) {
- t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
- // We cause scaling by 2.
- t.setSize(mFGSurfaceControl, 128, 128);
- });
-
- {
- mCapture = screenshot();
- // We've positioned the child in the top left.
- mCapture->expectChildColor(0, 0);
- mCapture->expectChildColor(10, 10);
- mCapture->expectChildColor(19, 29);
- // And now it should be scaled all the way to 20x30
- mCapture->expectFGColor(20, 30);
- }
-}
-
-// Regression test for b/37673612
-TEST_F(ChildLayerTest, ChildrenWithParentBufferTransform) {
- asTransaction([&](Transaction& t) {
- t.show(mChild);
- t.setPosition(mChild, 0, 0);
- t.setPosition(mFGSurfaceControl, 0, 0);
- });
-
- {
- mCapture = screenshot();
- // We've positioned the child in the top left.
- mCapture->expectChildColor(0, 0);
- mCapture->expectChildColor(9, 14);
- // But it's only 10x15.
- mCapture->expectFGColor(10, 15);
- }
- // We set things up as in b/37673612 so that there is a mismatch between the buffer size and
- // the WM specified state size.
- asTransaction([&](Transaction& t) { t.setSize(mFGSurfaceControl, 128, 64); });
- sp<Surface> s = mFGSurfaceControl->getSurface();
- auto anw = static_cast<ANativeWindow*>(s.get());
- native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
- native_window_set_buffers_dimensions(anw, 64, 128);
- TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
- waitForPostedBuffers();
-
- {
- // The child should still be in the same place and not have any strange scaling as in
- // b/37673612.
- mCapture = screenshot();
- mCapture->expectChildColor(0, 0);
- mCapture->expectFGColor(10, 10);
- }
-}
-
-// A child with a buffer transform from its parents should be cropped by its parent bounds.
-TEST_F(ChildLayerTest, ChildCroppedByParentWithBufferTransform) {
- asTransaction([&](Transaction& t) {
- t.show(mChild);
- t.setPosition(mChild, 0, 0);
- t.setPosition(mFGSurfaceControl, 0, 0);
- t.setSize(mChild, 100, 100);
- });
- TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200);
-
- {
- mCapture = screenshot();
-
- mCapture->expectChildColor(0, 0);
- mCapture->expectChildColor(63, 63);
- mCapture->expectBGColor(64, 64);
- }
-
- asTransaction([&](Transaction& t) { t.setSize(mFGSurfaceControl, 128, 64); });
- sp<Surface> s = mFGSurfaceControl->getSurface();
- auto anw = static_cast<ANativeWindow*>(s.get());
- // Apply a 90 transform on the buffer.
- native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
- native_window_set_buffers_dimensions(anw, 64, 128);
- TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
- waitForPostedBuffers();
-
- // The child should be cropped by the new parent bounds.
- {
- mCapture = screenshot();
- mCapture->expectChildColor(0, 0);
- mCapture->expectChildColor(99, 63);
- mCapture->expectFGColor(100, 63);
- mCapture->expectBGColor(128, 64);
- }
-}
-
-// A child with a scale transform from its parents should be cropped by its parent bounds.
-TEST_F(ChildLayerTest, ChildCroppedByParentWithBufferScale) {
- asTransaction([&](Transaction& t) {
- t.show(mChild);
- t.setPosition(mChild, 0, 0);
- t.setPosition(mFGSurfaceControl, 0, 0);
- t.setSize(mChild, 200, 200);
- });
- TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200);
-
- {
- mCapture = screenshot();
-
- mCapture->expectChildColor(0, 0);
- mCapture->expectChildColor(63, 63);
- mCapture->expectBGColor(64, 64);
- }
-
- asTransaction([&](Transaction& t) {
- t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
- // Set a scaling by 2.
- t.setSize(mFGSurfaceControl, 128, 128);
- });
-
- // Child should inherit its parents scale but should be cropped by its parent bounds.
- {
- mCapture = screenshot();
- mCapture->expectChildColor(0, 0);
- mCapture->expectChildColor(127, 127);
- mCapture->expectBGColor(128, 128);
- }
-}
-
-// Regression test for b/127368943
-// Child should ignore the buffer transform but apply parent scale transform.
-TEST_F(ChildLayerTest, ChildrenWithParentBufferTransformAndScale) {
- asTransaction([&](Transaction& t) {
- t.show(mChild);
- t.setPosition(mChild, 0, 0);
- t.setPosition(mFGSurfaceControl, 0, 0);
- });
-
- {
- mCapture = screenshot();
- mCapture->expectChildColor(0, 0);
- mCapture->expectChildColor(9, 14);
- mCapture->expectFGColor(10, 15);
- }
-
- // Change the size of the foreground to 128 * 64 so we can test rotation as well.
- asTransaction([&](Transaction& t) {
- t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
- t.setSize(mFGSurfaceControl, 128, 64);
- });
- sp<Surface> s = mFGSurfaceControl->getSurface();
- auto anw = static_cast<ANativeWindow*>(s.get());
- // Apply a 90 transform on the buffer and submit a buffer half the expected size so that we
- // have an effective scale of 2.0 applied to the buffer along with a rotation transform.
- native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
- native_window_set_buffers_dimensions(anw, 32, 64);
- TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
- waitForPostedBuffers();
-
- // The child should ignore the buffer transform but apply the 2.0 scale from parent.
- {
- mCapture = screenshot();
- mCapture->expectChildColor(0, 0);
- mCapture->expectChildColor(19, 29);
- mCapture->expectFGColor(20, 30);
- }
-}
-
-TEST_F(ChildLayerTest, Bug36858924) {
- // Destroy the child layer
- mChild.clear();
-
- // Now recreate it as hidden
- mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eHidden, mFGSurfaceControl.get());
-
- // Show the child layer in a deferred transaction
- asTransaction([&](Transaction& t) {
- t.deferTransactionUntil_legacy(mChild, mFGSurfaceControl->getHandle(),
- mFGSurfaceControl->getSurface()->getNextFrameNumber());
- t.show(mChild);
- });
-
- // Render the foreground surface a few times
- //
- // Prior to the bugfix for b/36858924, this would usually hang while trying to fill the third
- // frame because SurfaceFlinger would never process the deferred transaction and would therefore
- // never acquire/release the first buffer
- ALOGI("Filling 1");
- TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 0, 255, 0);
- ALOGI("Filling 2");
- TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 0, 0, 255);
- ALOGI("Filling 3");
- TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 255, 0, 0);
- ALOGI("Filling 4");
- TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 0, 255, 0);
-}
-
TEST_F(ChildLayerTest, Reparent) {
asTransaction([&](Transaction& t) {
t.show(mChild);
@@ -979,7 +471,7 @@
mCapture->expectFGColor(84, 84);
}
- asTransaction([&](Transaction& t) { t.reparent(mChild, mBGSurfaceControl->getHandle()); });
+ asTransaction([&](Transaction& t) { t.reparent(mChild, mBGSurfaceControl); });
{
mCapture = screenshot();
@@ -1041,7 +533,7 @@
mCapture->checkPixel(10, 10, 63, 195, 63);
}
- asTransaction([&](Transaction& t) { t.reparent(newSurface, mFGSurfaceControl->getHandle()); });
+ asTransaction([&](Transaction& t) { t.reparent(newSurface, mFGSurfaceControl); });
{
mCapture = screenshot();
@@ -1072,7 +564,7 @@
Transaction t;
t.setLayer(relative, INT32_MAX)
- .setRelativeLayer(mChild, relative->getHandle(), 1)
+ .setRelativeLayer(mChild, relative, 1)
.setPosition(mFGSurfaceControl, 0, 0)
.apply(true);
@@ -1143,7 +635,7 @@
ISurfaceComposerClient::eFXSurfaceEffect, cropLayer.get());
ASSERT_TRUE(colorLayer->isValid());
asTransaction([&](Transaction& t) {
- t.setCrop_legacy(cropLayer, Rect(5, 5, 10, 10));
+ t.setCrop(cropLayer, Rect(5, 5, 10, 10));
t.setColor(colorLayer, half3{0, 0, 0});
t.show(cropLayer);
t.show(colorLayer);
@@ -1203,7 +695,7 @@
t.show(boundlessLayerRightShift);
t.setPosition(boundlessLayerDownShift, 0, 32);
t.show(boundlessLayerDownShift);
- t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
+ t.setCrop(colorLayer, Rect(0, 0, 64, 64));
t.setColor(colorLayer, half3{0, 0, 0});
t.show(colorLayer);
});
@@ -1225,12 +717,13 @@
TEST_F(BoundlessLayerTest, IntermediateBoundlessLayerDoNotCrop) {
sp<SurfaceControl> boundlessLayer =
mClient->createSurface(String8("BoundlessLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
- 0 /* flags */, mFGSurfaceControl.get());
+ 0 /* flags */, mFGSurfaceControl->getHandle());
ASSERT_TRUE(boundlessLayer != nullptr);
ASSERT_TRUE(boundlessLayer->isValid());
sp<SurfaceControl> colorLayer =
mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceEffect, boundlessLayer.get());
+ ISurfaceComposerClient::eFXSurfaceEffect,
+ boundlessLayer->getHandle());
ASSERT_TRUE(colorLayer != nullptr);
ASSERT_TRUE(colorLayer->isValid());
asTransaction([&](Transaction& t) {
@@ -1238,7 +731,7 @@
// expect the child layer to be cropped.
t.setPosition(boundlessLayer, 32, 32);
t.show(boundlessLayer);
- t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
+ t.setCrop(colorLayer, Rect(0, 0, 64, 64));
// undo shift by parent
t.setPosition(colorLayer, -32, -32);
t.setColor(colorLayer, half3{0, 0, 0});
@@ -1269,7 +762,7 @@
t.setLayer(rootBoundlessLayer, INT32_MAX - 1);
t.setPosition(rootBoundlessLayer, 32, 32);
t.show(rootBoundlessLayer);
- t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
+ t.setCrop(colorLayer, Rect(0, 0, 64, 64));
t.setColor(colorLayer, half3{0, 0, 0});
t.show(colorLayer);
t.hide(mFGSurfaceControl);
@@ -1287,432 +780,6 @@
}
}
-class ScreenCaptureTest : public LayerUpdateTest {
-protected:
- std::unique_ptr<ScreenCapture> mCapture;
-};
-
-TEST_F(ScreenCaptureTest, CaptureSingleLayer) {
- auto bgHandle = mBGSurfaceControl->getHandle();
- ScreenCapture::captureLayers(&mCapture, bgHandle);
- mCapture->expectBGColor(0, 0);
- // Doesn't capture FG layer which is at 64, 64
- mCapture->expectBGColor(64, 64);
-}
-
-TEST_F(ScreenCaptureTest, CaptureLayerWithChild) {
- auto fgHandle = mFGSurfaceControl->getHandle();
-
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
-
- SurfaceComposerClient::Transaction().show(child).apply(true);
-
- // Captures mFGSurfaceControl layer and its child.
- ScreenCapture::captureLayers(&mCapture, fgHandle);
- mCapture->expectFGColor(10, 10);
- mCapture->expectChildColor(0, 0);
-}
-
-TEST_F(ScreenCaptureTest, CaptureLayerChildOnly) {
- auto fgHandle = mFGSurfaceControl->getHandle();
-
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
-
- SurfaceComposerClient::Transaction().show(child).apply(true);
-
- // Captures mFGSurfaceControl's child
- ScreenCapture::captureChildLayers(&mCapture, fgHandle);
- mCapture->checkPixel(10, 10, 0, 0, 0);
- mCapture->expectChildColor(0, 0);
-}
-
-TEST_F(ScreenCaptureTest, CaptureLayerExclude) {
- auto fgHandle = mFGSurfaceControl->getHandle();
-
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
- sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200);
-
- SurfaceComposerClient::Transaction()
- .show(child)
- .show(child2)
- .setLayer(child, 1)
- .setLayer(child2, 2)
- .apply(true);
-
- // Child2 would be visible but its excluded, so we should see child1 color instead.
- ScreenCapture::captureChildLayersExcluding(&mCapture, fgHandle, {child2->getHandle()});
- mCapture->checkPixel(10, 10, 0, 0, 0);
- mCapture->checkPixel(0, 0, 200, 200, 200);
-}
-
-// Like the last test but verifies that children are also exclude.
-TEST_F(ScreenCaptureTest, CaptureLayerExcludeTree) {
- auto fgHandle = mFGSurfaceControl->getHandle();
-
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
- sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200);
- sp<SurfaceControl> child3 = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, child2.get());
- TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200);
-
- SurfaceComposerClient::Transaction()
- .show(child)
- .show(child2)
- .show(child3)
- .setLayer(child, 1)
- .setLayer(child2, 2)
- .apply(true);
-
- // Child2 would be visible but its excluded, so we should see child1 color instead.
- ScreenCapture::captureChildLayersExcluding(&mCapture, fgHandle, {child2->getHandle()});
- mCapture->checkPixel(10, 10, 0, 0, 0);
- mCapture->checkPixel(0, 0, 200, 200, 200);
-}
-
-TEST_F(ScreenCaptureTest, CaptureTransparent) {
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-
- TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
-
- SurfaceComposerClient::Transaction().show(child).apply(true);
-
- auto childHandle = child->getHandle();
-
- // Captures child
- ScreenCapture::captureLayers(&mCapture, childHandle, {0, 0, 10, 20});
- mCapture->expectColor(Rect(0, 0, 9, 9), {200, 200, 200, 255});
- // Area outside of child's bounds is transparent.
- mCapture->expectColor(Rect(0, 10, 9, 19), {0, 0, 0, 0});
-}
-
-TEST_F(ScreenCaptureTest, DontCaptureRelativeOutsideTree) {
- auto fgHandle = mFGSurfaceControl->getHandle();
-
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- ASSERT_NE(nullptr, child.get()) << "failed to create surface";
- sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 10, 10, 0);
- TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
- TransactionUtils::fillSurfaceRGBA8(relative, 100, 100, 100);
-
- SurfaceComposerClient::Transaction()
- .show(child)
- // Set relative layer above fg layer so should be shown above when computing all layers.
- .setRelativeLayer(relative, fgHandle, 1)
- .show(relative)
- .apply(true);
-
- // Captures mFGSurfaceControl layer and its child. Relative layer shouldn't be captured.
- ScreenCapture::captureLayers(&mCapture, fgHandle);
- mCapture->expectFGColor(10, 10);
- mCapture->expectChildColor(0, 0);
-}
-
-TEST_F(ScreenCaptureTest, CaptureRelativeInTree) {
- auto fgHandle = mFGSurfaceControl->getHandle();
-
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- sp<SurfaceControl> relative = createSurface(mClient, "Relative surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
- TransactionUtils::fillSurfaceRGBA8(relative, 100, 100, 100);
-
- SurfaceComposerClient::Transaction()
- .show(child)
- // Set relative layer below fg layer but relative to child layer so it should be shown
- // above child layer.
- .setLayer(relative, -1)
- .setRelativeLayer(relative, child->getHandle(), 1)
- .show(relative)
- .apply(true);
-
- // Captures mFGSurfaceControl layer and its children. Relative layer is a child of fg so its
- // relative value should be taken into account, placing it above child layer.
- ScreenCapture::captureLayers(&mCapture, fgHandle);
- mCapture->expectFGColor(10, 10);
- // Relative layer is showing on top of child layer
- mCapture->expectColor(Rect(0, 0, 9, 9), {100, 100, 100, 255});
-}
-
-TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithSourceCrop) {
- sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
- SurfaceComposerClient::Transaction().show(child).apply(true);
-
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- sp<GraphicBuffer> outBuffer;
- Rect sourceCrop(0, 0, 10, 10);
- ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
- ScreenCapture sc(outBuffer);
-
- sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
-}
-
-TEST_F(ScreenCaptureTest, CaptureBoundedLayerWithoutSourceCrop) {
- sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
- Rect layerCrop(0, 0, 10, 10);
- SurfaceComposerClient::Transaction().setCrop_legacy(child, layerCrop).show(child).apply(true);
-
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- sp<GraphicBuffer> outBuffer;
- Rect sourceCrop = Rect();
- ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
- ScreenCapture sc(outBuffer);
-
- sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
-}
-
-TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithoutSourceCropFails) {
- sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
- SurfaceComposerClient::Transaction().show(child).apply(true);
-
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- sp<GraphicBuffer> outBuffer;
- Rect sourceCrop = Rect();
-
- ASSERT_EQ(BAD_VALUE, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
-}
-
-TEST_F(ScreenCaptureTest, CaptureBufferLayerWithoutBufferFails) {
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- SurfaceComposerClient::Transaction().show(child).apply(true);
-
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- sp<GraphicBuffer> outBuffer;
- Rect sourceCrop = Rect();
- ASSERT_EQ(BAD_VALUE, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
-
- TransactionUtils::fillSurfaceRGBA8(child, Color::RED);
- SurfaceComposerClient::Transaction().apply(true);
- ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
- ScreenCapture sc(outBuffer);
- sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
-}
-
-// In the following tests we verify successful skipping of a parent layer,
-// so we use the same verification logic and only change how we mutate
-// the parent layer to verify that various properties are ignored.
-class ScreenCaptureChildOnlyTest : public LayerUpdateTest {
-public:
- void SetUp() override {
- LayerUpdateTest::SetUp();
-
- mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888, 0,
- mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200);
-
- SurfaceComposerClient::Transaction().show(mChild).apply(true);
- }
-
- void verify(std::function<void()> verifyStartingState) {
- // Verify starting state before a screenshot is taken.
- verifyStartingState();
-
- // Verify child layer does not inherit any of the properties of its
- // parent when its screenshot is captured.
- auto fgHandle = mFGSurfaceControl->getHandle();
- ScreenCapture::captureChildLayers(&mCapture, fgHandle);
- mCapture->checkPixel(10, 10, 0, 0, 0);
- mCapture->expectChildColor(0, 0);
-
- // Verify all assumptions are still true after the screenshot is taken.
- verifyStartingState();
- }
-
- std::unique_ptr<ScreenCapture> mCapture;
- sp<SurfaceControl> mChild;
-};
-
-// Regression test b/76099859
-TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentVisibility) {
- SurfaceComposerClient::Transaction().hide(mFGSurfaceControl).apply(true);
-
- // Even though the parent is hidden we should still capture the child.
-
- // Before and after reparenting, verify child is properly hidden
- // when rendering full-screen.
- verify([&] { screenshot()->expectBGColor(64, 64); });
-}
-
-TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentCrop) {
- SurfaceComposerClient::Transaction()
- .setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 1, 1))
- .apply(true);
-
- // Even though the parent is cropped out we should still capture the child.
-
- // Before and after reparenting, verify child is cropped by parent.
- verify([&] { screenshot()->expectBGColor(65, 65); });
-}
-
-// Regression test b/124372894
-TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresTransform) {
- SurfaceComposerClient::Transaction().setMatrix(mFGSurfaceControl, 2, 0, 0, 2).apply(true);
-
- // We should not inherit the parent scaling.
-
- // Before and after reparenting, verify child is properly scaled.
- verify([&] { screenshot()->expectChildColor(80, 80); });
-}
-
-TEST_F(ScreenCaptureTest, CaptureLayerWithGrandchild) {
- auto fgHandle = mFGSurfaceControl->getHandle();
-
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
-
- sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
- PIXEL_FORMAT_RGBA_8888, 0, child.get());
-
- TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50);
- SurfaceComposerClient::Transaction()
- .show(child)
- .setPosition(grandchild, 5, 5)
- .show(grandchild)
- .apply(true);
-
- // Captures mFGSurfaceControl, its child, and the grandchild.
- ScreenCapture::captureLayers(&mCapture, fgHandle);
- mCapture->expectFGColor(10, 10);
- mCapture->expectChildColor(0, 0);
- mCapture->checkPixel(5, 5, 50, 50, 50);
-}
-
-TEST_F(ScreenCaptureTest, CaptureChildOnly) {
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
- auto childHandle = child->getHandle();
-
- SurfaceComposerClient::Transaction().setPosition(child, 5, 5).show(child).apply(true);
-
- // Captures only the child layer, and not the parent.
- ScreenCapture::captureLayers(&mCapture, childHandle);
- mCapture->expectChildColor(0, 0);
- mCapture->expectChildColor(9, 9);
-}
-
-TEST_F(ScreenCaptureTest, CaptureGrandchildOnly) {
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
- auto childHandle = child->getHandle();
-
- sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
- PIXEL_FORMAT_RGBA_8888, 0, child.get());
- TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50);
-
- SurfaceComposerClient::Transaction()
- .show(child)
- .setPosition(grandchild, 5, 5)
- .show(grandchild)
- .apply(true);
-
- auto grandchildHandle = grandchild->getHandle();
-
- // Captures only the grandchild.
- ScreenCapture::captureLayers(&mCapture, grandchildHandle);
- mCapture->checkPixel(0, 0, 50, 50, 50);
- mCapture->checkPixel(4, 4, 50, 50, 50);
-}
-
-TEST_F(ScreenCaptureTest, CaptureCrop) {
- sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
- sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30,
- PIXEL_FORMAT_RGBA_8888, 0, redLayer.get());
-
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
-
- SurfaceComposerClient::Transaction()
- .setLayer(redLayer, INT32_MAX - 1)
- .show(redLayer)
- .show(blueLayer)
- .apply(true);
-
- auto redLayerHandle = redLayer->getHandle();
-
- // Capturing full screen should have both red and blue are visible.
- ScreenCapture::captureLayers(&mCapture, redLayerHandle);
- mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
- // red area below the blue area
- mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED);
- // red area to the right of the blue area
- mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
-
- const Rect crop = Rect(0, 0, 30, 30);
- ScreenCapture::captureLayers(&mCapture, redLayerHandle, crop);
- // Capturing the cropped screen, cropping out the shown red area, should leave only the blue
- // area visible.
- mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
- mCapture->checkPixel(30, 30, 0, 0, 0);
-}
-
-TEST_F(ScreenCaptureTest, CaptureSize) {
- sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
- sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30,
- PIXEL_FORMAT_RGBA_8888, 0, redLayer.get());
-
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
-
- SurfaceComposerClient::Transaction()
- .setLayer(redLayer, INT32_MAX - 1)
- .show(redLayer)
- .show(blueLayer)
- .apply(true);
-
- auto redLayerHandle = redLayer->getHandle();
-
- // Capturing full screen should have both red and blue are visible.
- ScreenCapture::captureLayers(&mCapture, redLayerHandle);
- mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
- // red area below the blue area
- mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED);
- // red area to the right of the blue area
- mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
-
- ScreenCapture::captureLayers(&mCapture, redLayerHandle, Rect::EMPTY_RECT, 0.5);
- // Capturing the downsized area (30x30) should leave both red and blue but in a smaller area.
- mCapture->expectColor(Rect(0, 0, 14, 14), Color::BLUE);
- // red area below the blue area
- mCapture->expectColor(Rect(0, 15, 29, 29), Color::RED);
- // red area to the right of the blue area
- mCapture->expectColor(Rect(15, 0, 29, 29), Color::RED);
- mCapture->checkPixel(30, 30, 0, 0, 0);
-}
-
-TEST_F(ScreenCaptureTest, CaptureInvalidLayer) {
- sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
-
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
-
- auto redLayerHandle = redLayer->getHandle();
- Transaction().reparent(redLayer, nullptr).apply();
- redLayer.clear();
- SurfaceComposerClient::Transaction().apply(true);
-
- sp<GraphicBuffer> outBuffer;
-
- // Layer was deleted so captureLayers should fail with NAME_NOT_FOUND
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- ASSERT_EQ(NAME_NOT_FOUND, sf->captureLayers(redLayerHandle, &outBuffer, Rect::EMPTY_RECT, 1.0));
-}
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp
index b49bd54..ccf434d 100644
--- a/services/surfaceflinger/tests/MirrorLayer_test.cpp
+++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp
@@ -36,7 +36,7 @@
asTransaction([&](Transaction& t) {
t.setDisplayLayerStack(display, 0);
t.setLayer(mParentLayer, INT32_MAX - 2).show(mParentLayer);
- t.setCrop_legacy(mChildLayer, Rect(0, 0, 400, 400)).show(mChildLayer);
+ t.setCrop(mChildLayer, Rect(0, 0, 400, 400)).show(mChildLayer);
t.setPosition(mChildLayer, 50, 50);
t.setFlags(mParentLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
t.setFlags(mChildLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
@@ -58,7 +58,7 @@
createColorLayer("Grandchild layer", Color::BLUE, mChildLayer.get());
Transaction()
.setFlags(grandchild, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque)
- .setCrop_legacy(grandchild, Rect(0, 0, 200, 200))
+ .setCrop(grandchild, Rect(0, 0, 200, 200))
.show(grandchild)
.apply();
@@ -68,7 +68,7 @@
// Add mirrorLayer as child of mParentLayer so it's shown on the display
Transaction()
- .reparent(mirrorLayer, mParentLayer->getHandle())
+ .reparent(mirrorLayer, mParentLayer)
.setPosition(mirrorLayer, 500, 500)
.show(mirrorLayer)
.apply();
@@ -127,7 +127,7 @@
}
// Add grandchild layer to offscreen layer
- Transaction().reparent(grandchild, mChildLayer->getHandle()).apply();
+ Transaction().reparent(grandchild, mChildLayer).apply();
{
SCOPED_TRACE("Added Grandchild Layer");
auto shot = screenshot();
@@ -138,7 +138,7 @@
}
// Add child layer
- Transaction().reparent(mChildLayer, mParentLayer->getHandle()).apply();
+ Transaction().reparent(mChildLayer, mParentLayer).apply();
{
SCOPED_TRACE("Added Child Layer");
auto shot = screenshot();
@@ -157,7 +157,7 @@
sp<SurfaceControl> mirrorLayer = mClient->mirrorSurface(mChildLayer.get());
Transaction()
- .reparent(mirrorLayer, mParentLayer->getHandle())
+ .reparent(mirrorLayer, mParentLayer)
.setPosition(mirrorLayer, 500, 500)
.show(mirrorLayer)
.apply();
@@ -195,7 +195,7 @@
createLayer("BufferStateLayer", 200, 200, ISurfaceComposerClient::eFXSurfaceBufferState,
mChildLayer.get());
fillBufferStateLayerColor(bufferStateLayer, Color::BLUE, 200, 200);
- Transaction().setFrame(bufferStateLayer, Rect(0, 0, 200, 200)).show(bufferStateLayer).apply();
+ Transaction().show(bufferStateLayer).apply();
{
SCOPED_TRACE("Initial Mirror BufferStateLayer");
diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
index 06e8761..08de01c 100644
--- a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
+++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
@@ -37,13 +37,13 @@
mMainDisplay = SurfaceComposerClient::getInternalDisplayToken();
SurfaceComposerClient::getDisplayState(mMainDisplay, &mMainDisplayState);
- SurfaceComposerClient::getActiveDisplayConfig(mMainDisplay, &mMainDisplayConfig);
+ SurfaceComposerClient::getActiveDisplayMode(mMainDisplay, &mMainDisplayMode);
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&mProducer, &consumer);
consumer->setConsumerName(String8("Virtual disp consumer"));
- consumer->setDefaultBufferSize(mMainDisplayConfig.resolution.getWidth(),
- mMainDisplayConfig.resolution.getHeight());
+ consumer->setDefaultBufferSize(mMainDisplayMode.resolution.getWidth(),
+ mMainDisplayMode.resolution.getHeight());
}
virtual void TearDown() {
@@ -59,7 +59,7 @@
t.setDisplaySurface(mVirtualDisplay, mProducer);
t.setDisplayLayerStack(mVirtualDisplay, layerStack);
t.setDisplayProjection(mVirtualDisplay, mMainDisplayState.orientation,
- Rect(layerStackSize), Rect(mMainDisplayConfig.resolution));
+ Rect(layerStackSize), Rect(mMainDisplayMode.resolution));
});
}
@@ -71,7 +71,7 @@
ASSERT_TRUE(mColorLayer->isValid());
asTransaction([&](Transaction& t) {
t.setLayerStack(mColorLayer, layerStack);
- t.setCrop_legacy(mColorLayer, Rect(0, 0, 30, 40));
+ t.setCrop(mColorLayer, Rect(0, 0, 30, 40));
t.setLayer(mColorLayer, INT32_MAX - 2);
t.setColor(mColorLayer,
half3{mExpectedColor.r / 255.0f, mExpectedColor.g / 255.0f,
@@ -81,7 +81,7 @@
}
ui::DisplayState mMainDisplayState;
- DisplayConfig mMainDisplayConfig;
+ ui::DisplayMode mMainDisplayMode;
sp<IBinder> mMainDisplay;
sp<IBinder> mVirtualDisplay;
sp<IGraphicBufferProducer> mProducer;
@@ -90,7 +90,7 @@
};
TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInVirtualDisplay) {
- createDisplay(mMainDisplayState.viewport, 1 /* layerStack */);
+ createDisplay(mMainDisplayState.layerStackSpaceRect, 1 /* layerStack */);
createColorLayer(1 /* layerStack */);
asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
@@ -111,9 +111,9 @@
// Create a display and set its layer stack to the main display's layer stack so
// the contents of the main display are mirrored on to the virtual display.
- // Assumption here is that the new mirrored display has the same viewport as the
+ // Assumption here is that the new mirrored display has the same layer stack rect as the
// primary display that it is mirroring.
- createDisplay(mMainDisplayState.viewport, 0 /* layerStack */);
+ createDisplay(mMainDisplayState.layerStackSpaceRect, 0 /* layerStack */);
createColorLayer(0 /* layerStack */);
asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
diff --git a/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp b/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp
new file mode 100644
index 0000000..05858bf
--- /dev/null
+++ b/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2020 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 <thread>
+
+#include <gtest/gtest.h>
+
+#include <gui/SurfaceComposerClient.h>
+#include <private/gui/ComposerService.h>
+
+static constexpr int kRefreshRateOverlayCode = 1034;
+static constexpr int kRefreshRateOverlayEnable = 1;
+static constexpr int kRefreshRateOverlayDisable = 0;
+static constexpr int kRefreshRateOverlayQuery = 2;
+
+// These values must match the ones we used for developer options in
+// com.android.settings.development.ShowRefreshRatePreferenceController
+static_assert(kRefreshRateOverlayCode == 1034);
+static_assert(kRefreshRateOverlayEnable == 1);
+static_assert(kRefreshRateOverlayDisable == 0);
+static_assert(kRefreshRateOverlayQuery == 2);
+
+namespace android {
+
+namespace {
+void sendCommandToSf(int command, Parcel& reply) {
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ Parcel request;
+ request.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
+ request.writeInt32(command);
+ ASSERT_EQ(NO_ERROR,
+ IInterface::asBinder(sf)->transact(kRefreshRateOverlayCode, request, &reply));
+}
+
+bool isOverlayEnabled() {
+ Parcel reply;
+ sendCommandToSf(kRefreshRateOverlayQuery, reply);
+ return reply.readBool();
+}
+
+void waitForOverlay(bool enabled) {
+ static constexpr auto kTimeout = std::chrono::nanoseconds(1s);
+ static constexpr auto kIterations = 10;
+ for (int i = 0; i < kIterations; i++) {
+ if (enabled == isOverlayEnabled()) {
+ return;
+ }
+ std::this_thread::sleep_for(kTimeout / kIterations);
+ }
+}
+
+void toggleOverlay(bool enabled) {
+ if (enabled == isOverlayEnabled()) {
+ return;
+ }
+
+ Parcel reply;
+ const auto command = enabled ? kRefreshRateOverlayEnable : kRefreshRateOverlayDisable;
+ sendCommandToSf(command, reply);
+ waitForOverlay(enabled);
+ ASSERT_EQ(enabled, isOverlayEnabled());
+}
+
+} // namespace
+
+TEST(RefreshRateOverlayTest, enableOverlay) {
+ toggleOverlay(true);
+}
+
+TEST(RefreshRateOverlayTest, disableOverlay) {
+ toggleOverlay(false);
+}
+
+TEST(RefreshRateOverlayTest, enableAndDisableOverlay) {
+ toggleOverlay(true);
+ toggleOverlay(false);
+
+ toggleOverlay(true);
+ toggleOverlay(false);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/RelativeZ_test.cpp b/services/surfaceflinger/tests/RelativeZ_test.cpp
index 3e0b3c6..fde6e6e 100644
--- a/services/surfaceflinger/tests/RelativeZ_test.cpp
+++ b/services/surfaceflinger/tests/RelativeZ_test.cpp
@@ -70,10 +70,7 @@
sp<SurfaceControl> childLayer =
createColorLayer("Child layer", Color::BLUE, mBackgroundLayer.get());
- Transaction{}
- .setRelativeLayer(childLayer, mForegroundLayer->getHandle(), 1)
- .show(childLayer)
- .apply();
+ Transaction{}.setRelativeLayer(childLayer, mForegroundLayer, 1).show(childLayer).apply();
{
// The childLayer should be in front of the FG control.
@@ -88,7 +85,7 @@
// Background layer (RED)
// Child layer (WHITE)
// Foregroud layer (GREEN)
- Transaction{}.reparent(childLayer, mBackgroundLayer->getHandle()).apply();
+ Transaction{}.reparent(childLayer, mBackgroundLayer).apply();
{
// The relative z info for child layer should be reset, leaving FG control on top.
@@ -118,7 +115,7 @@
createColorLayer("child level 3", Color::GREEN, childLevel2a.get());
Transaction{}
- .setRelativeLayer(childLevel3, childLevel2b->getHandle(), 1)
+ .setRelativeLayer(childLevel3, childLevel2b, 1)
.show(childLevel2a)
.show(childLevel2b)
.show(childLevel3)
@@ -140,7 +137,7 @@
// child level 2 back (BLUE)
// child level 3 (GREEN) (relative to child level 2b)
// child level 2 front (BLACK)
- Transaction{}.reparent(childLevel1, mForegroundLayer->getHandle()).apply();
+ Transaction{}.reparent(childLevel1, mForegroundLayer).apply();
{
// Nothing should change at this point since relative z info was preserved.
@@ -162,7 +159,7 @@
createColorLayer("Relative layer", Color::WHITE, mForegroundLayer.get());
Transaction{}
- .setRelativeLayer(childLayer, relativeToLayer->getHandle(), 1)
+ .setRelativeLayer(childLayer, relativeToLayer, 1)
.show(childLayer)
.show(relativeToLayer)
.apply();
@@ -199,7 +196,7 @@
// Background layer (RED)
// Foregroud layer (GREEN)
// Child layer (BLUE)
- Transaction{}.reparent(childLayer, mForegroundLayer->getHandle()).apply();
+ Transaction{}.reparent(childLayer, mForegroundLayer).apply();
{
// The relative z info for child layer should be reset, leaving the child layer on top.
@@ -230,7 +227,7 @@
createColorLayer("child level 2b", Color::BLACK, childLevel1b.get());
Transaction{}
- .setRelativeLayer(childLevel1a, childLevel2b->getHandle(), 1)
+ .setRelativeLayer(childLevel1a, childLevel2b, 1)
.show(childLevel1a)
.show(childLevel1b)
.show(childLevel2a)
@@ -250,7 +247,7 @@
// // Background layer (RED)
// // Foregroud layer (GREEN)
- Transaction{}.reparent(childLevel1a, childLevel2a->getHandle()).apply();
+ Transaction{}.reparent(childLevel1a, childLevel2a).apply();
{
// The childLevel1a and childLevel1b are no longer on screen
@@ -264,7 +261,7 @@
// child level 2a (BLUE)
// child level 1a (testLayerColor) (relative to child level 2b)
// child level 2b (BLACK)
- Transaction{}.reparent(childLevel1b, mForegroundLayer->getHandle()).apply();
+ Transaction{}.reparent(childLevel1b, mForegroundLayer).apply();
{
// Nothing should change at this point since relative z info was preserved.
diff --git a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
new file mode 100644
index 0000000..fb7d41c
--- /dev/null
+++ b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2021 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 "LayerTransactionTest.h"
+#include "utils/CallbackUtils.h"
+
+using namespace std::chrono_literals;
+
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+::testing::Environment* const binderEnv =
+ ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+// b/181132765 - disabled until cuttlefish failures are investigated
+class ReleaseBufferCallbackHelper {
+public:
+ static void function(void* callbackContext, uint64_t graphicsBufferId,
+ const sp<Fence>& releaseFence) {
+ if (!callbackContext) {
+ FAIL() << "failed to get callback context";
+ }
+ ReleaseBufferCallbackHelper* helper =
+ static_cast<ReleaseBufferCallbackHelper*>(callbackContext);
+ std::lock_guard lock(helper->mMutex);
+ helper->mCallbackDataQueue.emplace(graphicsBufferId, releaseFence);
+ helper->mConditionVariable.notify_all();
+ }
+
+ void getCallbackData(uint64_t* bufferId) {
+ std::unique_lock lock(mMutex);
+ if (mCallbackDataQueue.empty()) {
+ if (!mConditionVariable.wait_for(lock, std::chrono::seconds(3),
+ [&] { return !mCallbackDataQueue.empty(); })) {
+ FAIL() << "failed to get releaseBuffer callback";
+ }
+ }
+
+ auto callbackData = mCallbackDataQueue.front();
+ mCallbackDataQueue.pop();
+ *bufferId = callbackData.first;
+ }
+
+ void verifyNoCallbacks() {
+ // Wait to see if there are extra callbacks
+ std::this_thread::sleep_for(300ms);
+
+ std::lock_guard lock(mMutex);
+ EXPECT_EQ(mCallbackDataQueue.size(), 0) << "extra callbacks received";
+ mCallbackDataQueue = {};
+ }
+
+ android::ReleaseBufferCallback getCallback() {
+ return std::bind(function, static_cast<void*>(this) /* callbackContext */,
+ std::placeholders::_1, std::placeholders::_2);
+ }
+
+ std::mutex mMutex;
+ std::condition_variable mConditionVariable;
+ std::queue<std::pair<uint64_t, sp<Fence>>> mCallbackDataQueue;
+};
+
+class ReleaseBufferCallbackTest : public LayerTransactionTest {
+public:
+ virtual sp<SurfaceControl> createBufferStateLayer() {
+ return createLayer(mClient, "test", 0, 0, ISurfaceComposerClient::eFXSurfaceBufferState);
+ }
+
+ static void submitBuffer(const sp<SurfaceControl>& layer, sp<GraphicBuffer> buffer,
+ sp<Fence> fence, CallbackHelper& callback,
+ ReleaseBufferCallbackHelper& releaseCallback) {
+ Transaction t;
+ t.setBuffer(layer, buffer, releaseCallback.getCallback());
+ t.setAcquireFence(layer, fence);
+ t.addTransactionCompletedCallback(callback.function, callback.getContext());
+ t.apply();
+ }
+
+ static void waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult) {
+ CallbackData callbackData;
+ helper.getCallbackData(&callbackData);
+ expectedResult.verifyCallbackData(callbackData);
+ }
+
+ static void waitForReleaseBufferCallback(ReleaseBufferCallbackHelper& releaseCallback,
+ uint64_t expectedReleaseBufferId) {
+ uint64_t actualReleaseBufferId;
+ releaseCallback.getCallbackData(&actualReleaseBufferId);
+ EXPECT_EQ(expectedReleaseBufferId, actualReleaseBufferId);
+ releaseCallback.verifyNoCallbacks();
+ }
+ static ReleaseBufferCallbackHelper* getReleaseBufferCallbackHelper() {
+ static std::vector<ReleaseBufferCallbackHelper*> sCallbacks;
+ sCallbacks.emplace_back(new ReleaseBufferCallbackHelper());
+ return sCallbacks.back();
+ }
+
+ static sp<GraphicBuffer> getBuffer() {
+ return new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ }
+};
+
+TEST_F(ReleaseBufferCallbackTest, DISABLED_PresentBuffer) {
+ sp<SurfaceControl> layer = createBufferStateLayer();
+ CallbackHelper transactionCallback;
+ ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
+
+ // If a buffer is being presented, we should not emit a release callback.
+ sp<GraphicBuffer> firstBuffer = getBuffer();
+ submitBuffer(layer, firstBuffer, Fence::NO_FENCE, transactionCallback, *releaseCallback);
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+ ExpectedResult::Buffer::NOT_ACQUIRED);
+ ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
+ EXPECT_NO_FATAL_FAILURE(releaseCallback->verifyNoCallbacks());
+
+ // if state doesn't change, no release callbacks are expected
+ Transaction t;
+ t.addTransactionCompletedCallback(transactionCallback.function,
+ transactionCallback.getContext());
+ t.apply();
+ ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, ExpectedResult()));
+ EXPECT_NO_FATAL_FAILURE(releaseCallback->verifyNoCallbacks());
+
+ // If a presented buffer is replaced, we should emit a release callback for the
+ // previously presented buffer.
+ sp<GraphicBuffer> secondBuffer = getBuffer();
+ submitBuffer(layer, secondBuffer, Fence::NO_FENCE, transactionCallback, *releaseCallback);
+ expected = ExpectedResult();
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+ ExpectedResult::Buffer::NOT_ACQUIRED,
+ ExpectedResult::PreviousBuffer::RELEASED);
+ ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
+ ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBuffer->getId()));
+}
+
+TEST_F(ReleaseBufferCallbackTest, DISABLED_OffScreenLayer) {
+ sp<SurfaceControl> layer = createBufferStateLayer();
+
+ CallbackHelper transactionCallback;
+ ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
+
+ // If a buffer is being presented, we should not emit a release callback.
+ sp<GraphicBuffer> firstBuffer = getBuffer();
+ submitBuffer(layer, firstBuffer, Fence::NO_FENCE, transactionCallback, *releaseCallback);
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+ ExpectedResult::Buffer::NOT_ACQUIRED);
+ ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
+ releaseCallback->verifyNoCallbacks();
+
+ // If a layer is parented offscreen then it should not emit a callback since sf still owns
+ // the buffer and can render it again.
+ Transaction t;
+ t.reparent(layer, nullptr);
+ t.addTransactionCompletedCallback(transactionCallback.function,
+ transactionCallback.getContext());
+ t.apply();
+ expected = ExpectedResult();
+ expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer,
+ ExpectedResult::Buffer::NOT_ACQUIRED,
+ ExpectedResult::PreviousBuffer::NOT_RELEASED);
+ ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
+ ASSERT_NO_FATAL_FAILURE(releaseCallback->verifyNoCallbacks());
+
+ // If a presented buffer is replaced, we should emit a release callback for the
+ // previously presented buffer.
+ sp<GraphicBuffer> secondBuffer = getBuffer();
+ submitBuffer(layer, secondBuffer, Fence::NO_FENCE, transactionCallback, *releaseCallback);
+ expected = ExpectedResult();
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+ ExpectedResult::Buffer::NOT_ACQUIRED,
+ ExpectedResult::PreviousBuffer::NOT_RELEASED);
+ ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
+ ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBuffer->getId()));
+
+ // If continue to submit buffer we continue to get release callbacks
+ sp<GraphicBuffer> thirdBuffer = getBuffer();
+ submitBuffer(layer, thirdBuffer, Fence::NO_FENCE, transactionCallback, *releaseCallback);
+ expected = ExpectedResult();
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+ ExpectedResult::Buffer::NOT_ACQUIRED,
+ ExpectedResult::PreviousBuffer::NOT_RELEASED);
+ ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
+ ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, secondBuffer->getId()));
+}
+
+TEST_F(ReleaseBufferCallbackTest, DISABLED_LayerLifecycle_layerdestroy) {
+ sp<SurfaceControl> layer = createBufferStateLayer();
+ CallbackHelper* transactionCallback = new CallbackHelper();
+ ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
+
+ // If a buffer is being presented, we should not emit a release callback.
+ sp<GraphicBuffer> firstBuffer = getBuffer();
+ submitBuffer(layer, firstBuffer, Fence::NO_FENCE, *transactionCallback, *releaseCallback);
+ {
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+ ExpectedResult::Buffer::NOT_ACQUIRED);
+ ASSERT_NO_FATAL_FAILURE(waitForCallback(*transactionCallback, expected));
+ ASSERT_NO_FATAL_FAILURE(releaseCallback->verifyNoCallbacks());
+ }
+
+ // Destroying a currently presenting layer emits a callback.
+ Transaction t;
+ t.reparent(layer, nullptr);
+ t.apply();
+ layer = nullptr;
+
+ ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBuffer->getId()));
+}
+
+// Destroying a never presented layer emits a callback.
+TEST_F(ReleaseBufferCallbackTest, DISABLED_LayerLifecycle_OffScreenLayerDestroy) {
+ sp<SurfaceControl> layer = createBufferStateLayer();
+
+ // make layer offscreen
+ Transaction t;
+ t.reparent(layer, nullptr);
+ t.apply();
+
+ CallbackHelper* transactionCallback = new CallbackHelper();
+ ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
+
+ // Submitting a buffer does not emit a callback.
+ sp<GraphicBuffer> firstBuffer = getBuffer();
+ submitBuffer(layer, firstBuffer, Fence::NO_FENCE, *transactionCallback, *releaseCallback);
+ {
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+ ExpectedResult::Buffer::NOT_ACQUIRED);
+ ASSERT_NO_FATAL_FAILURE(waitForCallback(*transactionCallback, expected));
+ ASSERT_NO_FATAL_FAILURE(releaseCallback->verifyNoCallbacks());
+ }
+
+ // Submitting a second buffer will replace the drawing state buffer and emit a callback.
+ sp<GraphicBuffer> secondBuffer = getBuffer();
+ submitBuffer(layer, secondBuffer, Fence::NO_FENCE, *transactionCallback, *releaseCallback);
+ {
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+ ExpectedResult::Buffer::NOT_ACQUIRED);
+ ASSERT_NO_FATAL_FAILURE(waitForCallback(*transactionCallback, expected));
+ ASSERT_NO_FATAL_FAILURE(
+ waitForReleaseBufferCallback(*releaseCallback, firstBuffer->getId()));
+ }
+
+ // Destroying the offscreen layer emits a callback.
+ layer = nullptr;
+ ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, secondBuffer->getId()));
+}
+
+TEST_F(ReleaseBufferCallbackTest, DISABLED_FrameDropping) {
+ sp<SurfaceControl> layer = createBufferStateLayer();
+ CallbackHelper transactionCallback;
+ ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
+
+ // If a buffer is being presented, we should not emit a release callback.
+ sp<GraphicBuffer> firstBuffer = getBuffer();
+
+ // Try to present 100ms in the future
+ nsecs_t time = systemTime() + std::chrono::nanoseconds(100ms).count();
+
+ Transaction t;
+ t.setBuffer(layer, firstBuffer, releaseCallback->getCallback());
+ t.setAcquireFence(layer, Fence::NO_FENCE);
+ t.addTransactionCompletedCallback(transactionCallback.function,
+ transactionCallback.getContext());
+ t.setDesiredPresentTime(time);
+ t.apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+ ExpectedResult::Buffer::NOT_ACQUIRED);
+ ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
+ EXPECT_NO_FATAL_FAILURE(releaseCallback->verifyNoCallbacks());
+
+ // Dropping frames in transaction queue emits a callback
+ sp<GraphicBuffer> secondBuffer = getBuffer();
+ t.setBuffer(layer, secondBuffer, releaseCallback->getCallback());
+ t.setAcquireFence(layer, Fence::NO_FENCE);
+ t.addTransactionCompletedCallback(transactionCallback.function,
+ transactionCallback.getContext());
+ t.setDesiredPresentTime(time);
+ t.apply();
+
+ expected = ExpectedResult();
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+ ExpectedResult::Buffer::NOT_ACQUIRED,
+ ExpectedResult::PreviousBuffer::RELEASED);
+ ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
+ ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBuffer->getId()));
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
new file mode 100644
index 0000000..2e9c10c
--- /dev/null
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -0,0 +1,888 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <private/android_filesystem_config.h>
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+class ScreenCaptureTest : public LayerTransactionTest {
+protected:
+ virtual void SetUp() {
+ LayerTransactionTest::SetUp();
+ ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ ASSERT_FALSE(display == nullptr);
+
+ ui::DisplayMode mode;
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+ const ui::Size& resolution = mode.resolution;
+
+ // Background surface
+ mBGSurfaceControl = createLayer(String8("BG Test Surface"), resolution.getWidth(),
+ resolution.getHeight(), 0);
+ ASSERT_TRUE(mBGSurfaceControl != nullptr);
+ ASSERT_TRUE(mBGSurfaceControl->isValid());
+ TransactionUtils::fillSurfaceRGBA8(mBGSurfaceControl, 63, 63, 195);
+
+ // Foreground surface
+ mFGSurfaceControl = createLayer(String8("FG Test Surface"), 64, 64, 0);
+
+ ASSERT_TRUE(mFGSurfaceControl != nullptr);
+ ASSERT_TRUE(mFGSurfaceControl->isValid());
+
+ TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
+
+ asTransaction([&](Transaction& t) {
+ t.setDisplayLayerStack(display, 0);
+
+ t.setLayer(mBGSurfaceControl, INT32_MAX - 2).show(mBGSurfaceControl);
+
+ t.setLayer(mFGSurfaceControl, INT32_MAX - 1)
+ .setPosition(mFGSurfaceControl, 64, 64)
+ .show(mFGSurfaceControl);
+ });
+ }
+
+ virtual void TearDown() {
+ LayerTransactionTest::TearDown();
+ mBGSurfaceControl = 0;
+ mFGSurfaceControl = 0;
+ }
+
+ sp<SurfaceControl> mBGSurfaceControl;
+ sp<SurfaceControl> mFGSurfaceControl;
+ std::unique_ptr<ScreenCapture> mCapture;
+};
+
+TEST_F(ScreenCaptureTest, SetFlagsSecureEUidSystem) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32,
+ ISurfaceComposerClient::eSecure |
+ ISurfaceComposerClient::eFXSurfaceBufferQueue));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+ Transaction().show(layer).setLayer(layer, INT32_MAX).apply(true);
+
+ ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(mCaptureArgs, mCaptureResults));
+
+ UIDFaker f(AID_SYSTEM);
+
+ // By default the system can capture screenshots with secure layers but they
+ // will be blacked out
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(mCaptureArgs, mCaptureResults));
+
+ {
+ SCOPED_TRACE("as system");
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+ }
+
+ // Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able
+ // to receive them...we are expected to take care with the results.
+ DisplayCaptureArgs args;
+ args.displayToken = mDisplay;
+ args.captureSecureLayers = true;
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(args, mCaptureResults));
+ ASSERT_TRUE(mCaptureResults.capturedSecureLayers);
+ ScreenCapture sc(mCaptureResults.buffer);
+ sc.expectColor(Rect(0, 0, 32, 32), Color::RED);
+}
+
+TEST_F(ScreenCaptureTest, CaptureChildSetParentFlagsSecureEUidSystem) {
+ sp<SurfaceControl> parentLayer;
+ ASSERT_NO_FATAL_FAILURE(
+ parentLayer = createLayer("parent-test", 32, 32,
+ ISurfaceComposerClient::eSecure |
+ ISurfaceComposerClient::eFXSurfaceBufferQueue));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(parentLayer, Color::RED, 32, 32));
+
+ sp<SurfaceControl> childLayer;
+ ASSERT_NO_FATAL_FAILURE(childLayer = createLayer("child-test", 10, 10,
+ ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ parentLayer.get()));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(childLayer, Color::BLUE, 10, 10));
+
+ Transaction().show(parentLayer).setLayer(parentLayer, INT32_MAX).show(childLayer).apply(true);
+
+ UIDFaker f(AID_SYSTEM);
+
+ {
+ SCOPED_TRACE("as system");
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, 10, 10), Color::BLACK);
+ }
+
+ // Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able
+ // to receive them...we are expected to take care with the results.
+ DisplayCaptureArgs args;
+ args.displayToken = mDisplay;
+ args.captureSecureLayers = true;
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(args, mCaptureResults));
+ ASSERT_TRUE(mCaptureResults.capturedSecureLayers);
+ ScreenCapture sc(mCaptureResults.buffer);
+ sc.expectColor(Rect(0, 0, 10, 10), Color::BLUE);
+}
+
+TEST_F(ScreenCaptureTest, CaptureSingleLayer) {
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = mBGSurfaceControl->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectBGColor(0, 0);
+ // Doesn't capture FG layer which is at 64, 64
+ mCapture->expectBGColor(64, 64);
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerWithChild) {
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+
+ SurfaceComposerClient::Transaction().show(child).apply(true);
+
+ // Captures mFGSurfaceControl layer and its child.
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = mFGSurfaceControl->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectFGColor(10, 10);
+ mCapture->expectChildColor(0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerChildOnly) {
+ auto fgHandle = mFGSurfaceControl->getHandle();
+
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+
+ SurfaceComposerClient::Transaction().show(child).apply(true);
+
+ // Captures mFGSurfaceControl's child
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = fgHandle;
+ captureArgs.childrenOnly = true;
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->checkPixel(10, 10, 0, 0, 0);
+ mCapture->expectChildColor(0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerExclude) {
+ auto fgHandle = mFGSurfaceControl->getHandle();
+
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+ sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200);
+
+ SurfaceComposerClient::Transaction()
+ .show(child)
+ .show(child2)
+ .setLayer(child, 1)
+ .setLayer(child2, 2)
+ .apply(true);
+
+ // Child2 would be visible but its excluded, so we should see child1 color instead.
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = fgHandle;
+ captureArgs.childrenOnly = true;
+ captureArgs.excludeHandles = {child2->getHandle()};
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->checkPixel(10, 10, 0, 0, 0);
+ mCapture->checkPixel(0, 0, 200, 200, 200);
+}
+
+// Like the last test but verifies that children are also exclude.
+TEST_F(ScreenCaptureTest, CaptureLayerExcludeTree) {
+ auto fgHandle = mFGSurfaceControl->getHandle();
+
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+ sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200);
+ sp<SurfaceControl> child3 = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, child2.get());
+ TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200);
+
+ SurfaceComposerClient::Transaction()
+ .show(child)
+ .show(child2)
+ .show(child3)
+ .setLayer(child, 1)
+ .setLayer(child2, 2)
+ .apply(true);
+
+ // Child2 would be visible but its excluded, so we should see child1 color instead.
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = fgHandle;
+ captureArgs.childrenOnly = true;
+ captureArgs.excludeHandles = {child2->getHandle()};
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->checkPixel(10, 10, 0, 0, 0);
+ mCapture->checkPixel(0, 0, 200, 200, 200);
+}
+
+TEST_F(ScreenCaptureTest, CaptureTransparent) {
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+
+ TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+
+ SurfaceComposerClient::Transaction().show(child).apply(true);
+
+ // Captures child
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = child->getHandle();
+ captureArgs.sourceCrop = {0, 0, 10, 20};
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(0, 0, 9, 9), {200, 200, 200, 255});
+ // Area outside of child's bounds is transparent.
+ mCapture->expectColor(Rect(0, 10, 9, 19), {0, 0, 0, 0});
+}
+
+TEST_F(ScreenCaptureTest, DontCaptureRelativeOutsideTree) {
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ ASSERT_NE(nullptr, child.get()) << "failed to create surface";
+ sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 10, 10, 0);
+ TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+ TransactionUtils::fillSurfaceRGBA8(relative, 100, 100, 100);
+
+ SurfaceComposerClient::Transaction()
+ .show(child)
+ // Set relative layer above fg layer so should be shown above when computing all layers.
+ .setRelativeLayer(relative, mFGSurfaceControl, 1)
+ .show(relative)
+ .apply(true);
+
+ // Captures mFGSurfaceControl layer and its child. Relative layer shouldn't be captured.
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = mFGSurfaceControl->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectFGColor(10, 10);
+ mCapture->expectChildColor(0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureRelativeInTree) {
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ sp<SurfaceControl> relative = createSurface(mClient, "Relative surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+ TransactionUtils::fillSurfaceRGBA8(relative, 100, 100, 100);
+
+ SurfaceComposerClient::Transaction()
+ .show(child)
+ // Set relative layer below fg layer but relative to child layer so it should be shown
+ // above child layer.
+ .setLayer(relative, -1)
+ .setRelativeLayer(relative, child, 1)
+ .show(relative)
+ .apply(true);
+
+ // Captures mFGSurfaceControl layer and its children. Relative layer is a child of fg so its
+ // relative value should be taken into account, placing it above child layer.
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = mFGSurfaceControl->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectFGColor(10, 10);
+ // Relative layer is showing on top of child layer
+ mCapture->expectColor(Rect(0, 0, 9, 9), {100, 100, 100, 255});
+}
+
+TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithSourceCrop) {
+ sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
+ SurfaceComposerClient::Transaction().show(child).apply(true);
+
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = child->getHandle();
+ captureArgs.sourceCrop = {0, 0, 10, 10};
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+
+ mCapture->expectColor(Rect(0, 0, 9, 9), Color::RED);
+}
+
+TEST_F(ScreenCaptureTest, CaptureBoundedLayerWithoutSourceCrop) {
+ sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
+ Rect layerCrop(0, 0, 10, 10);
+ SurfaceComposerClient::Transaction().setCrop(child, layerCrop).show(child).apply(true);
+
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = child->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+
+ mCapture->expectColor(Rect(0, 0, 9, 9), Color::RED);
+}
+
+TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithoutSourceCropFails) {
+ sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
+ SurfaceComposerClient::Transaction().show(child).apply(true);
+
+ LayerCaptureArgs args;
+ args.layerHandle = child->getHandle();
+
+ ScreenCaptureResults captureResults;
+ ASSERT_EQ(BAD_VALUE, ScreenCapture::captureLayers(args, captureResults));
+}
+
+TEST_F(ScreenCaptureTest, CaptureBufferLayerWithoutBufferFails) {
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState,
+ mFGSurfaceControl.get());
+
+ SurfaceComposerClient::Transaction().show(child).apply(true);
+ sp<GraphicBuffer> outBuffer;
+
+ LayerCaptureArgs args;
+ args.layerHandle = child->getHandle();
+ args.childrenOnly = false;
+
+ ScreenCaptureResults captureResults;
+ ASSERT_EQ(BAD_VALUE, ScreenCapture::captureLayers(args, captureResults));
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::RED, 32, 32));
+ SurfaceComposerClient::Transaction().apply(true);
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(args, captureResults));
+ ScreenCapture sc(captureResults.buffer);
+ sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerWithGrandchild) {
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+
+ sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
+ PIXEL_FORMAT_RGBA_8888, 0, child.get());
+
+ TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50);
+ SurfaceComposerClient::Transaction()
+ .show(child)
+ .setPosition(grandchild, 5, 5)
+ .show(grandchild)
+ .apply(true);
+
+ // Captures mFGSurfaceControl, its child, and the grandchild.
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = mFGSurfaceControl->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectFGColor(10, 10);
+ mCapture->expectChildColor(0, 0);
+ mCapture->checkPixel(5, 5, 50, 50, 50);
+}
+
+TEST_F(ScreenCaptureTest, CaptureChildOnly) {
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+
+ SurfaceComposerClient::Transaction().setPosition(child, 5, 5).show(child).apply(true);
+
+ // Captures only the child layer, and not the parent.
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = child->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectChildColor(0, 0);
+ mCapture->expectChildColor(9, 9);
+}
+
+TEST_F(ScreenCaptureTest, CaptureGrandchildOnly) {
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+ auto childHandle = child->getHandle();
+
+ sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
+ PIXEL_FORMAT_RGBA_8888, 0, child.get());
+ TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50);
+
+ SurfaceComposerClient::Transaction()
+ .show(child)
+ .setPosition(grandchild, 5, 5)
+ .show(grandchild)
+ .apply(true);
+
+ // Captures only the grandchild.
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = grandchild->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->checkPixel(0, 0, 50, 50, 50);
+ mCapture->checkPixel(4, 4, 50, 50, 50);
+}
+
+TEST_F(ScreenCaptureTest, CaptureCrop) {
+ sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60,
+ ISurfaceComposerClient::eFXSurfaceBufferState);
+ sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30,
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState,
+ redLayer.get());
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(redLayer, Color::RED, 60, 60));
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(blueLayer, Color::BLUE, 30, 30));
+
+ SurfaceComposerClient::Transaction()
+ .setLayer(redLayer, INT32_MAX - 1)
+ .show(redLayer)
+ .show(blueLayer)
+ .apply(true);
+
+ // Capturing full screen should have both red and blue are visible.
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = redLayer->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
+ // red area below the blue area
+ mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED);
+ // red area to the right of the blue area
+ mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
+
+ captureArgs.sourceCrop = {0, 0, 30, 30};
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ // Capturing the cropped screen, cropping out the shown red area, should leave only the blue
+ // area visible.
+ mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
+ mCapture->checkPixel(30, 30, 0, 0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureSize) {
+ sp<SurfaceControl> redLayer =
+ createLayer(String8("Red surface"), 60, 60, ISurfaceComposerClient::eFXSurfaceBufferState);
+ sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30,
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState,
+ redLayer.get());
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(redLayer, Color::RED, 60, 60));
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(blueLayer, Color::BLUE, 30, 30));
+
+ SurfaceComposerClient::Transaction()
+ .setLayer(redLayer, INT32_MAX - 1)
+ .show(redLayer)
+ .show(blueLayer)
+ .apply(true);
+
+ // Capturing full screen should have both red and blue are visible.
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = redLayer->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
+ // red area below the blue area
+ mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED);
+ // red area to the right of the blue area
+ mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
+
+ captureArgs.frameScaleX = 0.5f;
+ captureArgs.frameScaleY = 0.5f;
+ sleep(1);
+
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ // Capturing the downsized area (30x30) should leave both red and blue but in a smaller area.
+ mCapture->expectColor(Rect(0, 0, 14, 14), Color::BLUE);
+ // red area below the blue area
+ mCapture->expectColor(Rect(0, 15, 29, 29), Color::RED);
+ // red area to the right of the blue area
+ mCapture->expectColor(Rect(15, 0, 29, 29), Color::RED);
+ mCapture->checkPixel(30, 30, 0, 0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureInvalidLayer) {
+ sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
+
+ auto redLayerHandle = redLayer->getHandle();
+ Transaction().reparent(redLayer, nullptr).apply();
+ redLayer.clear();
+ SurfaceComposerClient::Transaction().apply(true);
+
+ LayerCaptureArgs args;
+ args.layerHandle = redLayerHandle;
+
+ ScreenCaptureResults captureResults;
+ // Layer was deleted so captureLayers should fail with NAME_NOT_FOUND
+ ASSERT_EQ(NAME_NOT_FOUND, ScreenCapture::captureLayers(args, captureResults));
+}
+
+TEST_F(ScreenCaptureTest, CaputureSecureLayer) {
+ sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60,
+ ISurfaceComposerClient::eFXSurfaceBufferState);
+ sp<SurfaceControl> secureLayer =
+ createLayer(String8("Secure surface"), 30, 30,
+ ISurfaceComposerClient::eSecure |
+ ISurfaceComposerClient::eFXSurfaceBufferState,
+ redLayer.get());
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(redLayer, Color::RED, 60, 60));
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(secureLayer, Color::BLUE, 30, 30));
+
+ auto redLayerHandle = redLayer->getHandle();
+ Transaction()
+ .show(redLayer)
+ .show(secureLayer)
+ .setLayerStack(redLayer, 0)
+ .setLayer(redLayer, INT32_MAX)
+ .apply();
+
+ LayerCaptureArgs args;
+ args.layerHandle = redLayerHandle;
+ args.childrenOnly = false;
+ ScreenCaptureResults captureResults;
+
+ // Call from outside system with secure layers will result in permission denied
+ ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureLayers(args, captureResults));
+
+ UIDFaker f(AID_SYSTEM);
+
+ // From system request, only red layer will be screenshot since the blue layer is secure.
+ // Black will be present where the secure layer is.
+ ScreenCapture::captureLayers(&mCapture, args);
+ mCapture->expectColor(Rect(0, 0, 30, 30), Color::BLACK);
+ mCapture->expectColor(Rect(30, 30, 60, 60), Color::RED);
+
+ // Passing flag secure so the blue layer should be screenshot too.
+ args.captureSecureLayers = true;
+ ScreenCapture::captureLayers(&mCapture, args);
+ mCapture->expectColor(Rect(0, 0, 30, 30), Color::BLUE);
+ mCapture->expectColor(Rect(30, 30, 60, 60), Color::RED);
+}
+
+TEST_F(ScreenCaptureTest, CaptureDisplayWithUid) {
+ uid_t fakeUid = 12345;
+
+ DisplayCaptureArgs captureArgs;
+ captureArgs.displayToken = mDisplay;
+
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
+ ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ mBGSurfaceControl.get()));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+ Transaction().show(layer).setLayer(layer, INT32_MAX).apply();
+
+ // Make sure red layer with the background layer is screenshot.
+ ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ mCapture->expectBorder(Rect(0, 0, 32, 32), {63, 63, 195, 255});
+
+ // From non system uid, can't request screenshot without a specified uid.
+ UIDFaker f(fakeUid);
+ ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(captureArgs, mCaptureResults));
+
+ // Make screenshot request with current uid set. No layers were created with the current
+ // uid so screenshot will be black.
+ captureArgs.uid = fakeUid;
+ ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+ mCapture->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+
+ sp<SurfaceControl> layerWithFakeUid;
+ // Create a new layer with the current uid
+ ASSERT_NO_FATAL_FAILURE(layerWithFakeUid =
+ createLayer("new test layer", 32, 32,
+ ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ mBGSurfaceControl.get()));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layerWithFakeUid, Color::GREEN, 32, 32));
+ Transaction()
+ .show(layerWithFakeUid)
+ .setLayer(layerWithFakeUid, INT32_MAX)
+ .setPosition(layerWithFakeUid, 128, 128)
+ .apply();
+
+ // Screenshot from the fakeUid caller with the uid requested allows the layer
+ // with that uid to be screenshotted. Everything else is black
+ ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(128, 128, 160, 160), Color::GREEN);
+ mCapture->expectBorder(Rect(128, 128, 160, 160), Color::BLACK);
+}
+
+TEST_F(ScreenCaptureTest, CaptureDisplayPrimaryDisplayOnly) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test layer", 0, 0, ISurfaceComposerClient::eFXSurfaceEffect));
+
+ const Color layerColor = Color::RED;
+ const Rect bounds = Rect(10, 10, 40, 40);
+
+ Transaction()
+ .show(layer)
+ .hide(mFGSurfaceControl)
+ .setLayerStack(layer, 0)
+ .setLayer(layer, INT32_MAX)
+ .setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255})
+ .setCrop(layer, bounds)
+ .apply();
+
+ DisplayCaptureArgs captureArgs;
+ captureArgs.displayToken = mDisplay;
+
+ {
+ ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ mCapture->expectColor(bounds, layerColor);
+ mCapture->expectBorder(bounds, {63, 63, 195, 255});
+ }
+
+ Transaction()
+ .setFlags(layer, layer_state_t::eLayerSkipScreenshot,
+ layer_state_t::eLayerSkipScreenshot)
+ .apply();
+
+ {
+ // Can't screenshot test layer since it now has flag
+ // eLayerSkipScreenshot
+ ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ mCapture->expectColor(bounds, {63, 63, 195, 255});
+ mCapture->expectBorder(bounds, {63, 63, 195, 255});
+ }
+}
+
+TEST_F(ScreenCaptureTest, CaptureDisplayChildPrimaryDisplayOnly) {
+ sp<SurfaceControl> layer;
+ sp<SurfaceControl> childLayer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test layer", 0, 0, ISurfaceComposerClient::eFXSurfaceEffect));
+ ASSERT_NO_FATAL_FAILURE(childLayer = createLayer("test layer", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceEffect,
+ layer.get()));
+
+ const Color layerColor = Color::RED;
+ const Color childColor = Color::BLUE;
+ const Rect bounds = Rect(10, 10, 40, 40);
+ const Rect childBounds = Rect(20, 20, 30, 30);
+
+ Transaction()
+ .show(layer)
+ .show(childLayer)
+ .hide(mFGSurfaceControl)
+ .setLayerStack(layer, 0)
+ .setLayer(layer, INT32_MAX)
+ .setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255})
+ .setColor(childLayer, {childColor.r / 255, childColor.g / 255, childColor.b / 255})
+ .setCrop(layer, bounds)
+ .setCrop(childLayer, childBounds)
+ .apply();
+
+ DisplayCaptureArgs captureArgs;
+ captureArgs.displayToken = mDisplay;
+
+ {
+ ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ mCapture->expectColor(childBounds, childColor);
+ mCapture->expectBorder(childBounds, layerColor);
+ mCapture->expectBorder(bounds, {63, 63, 195, 255});
+ }
+
+ Transaction()
+ .setFlags(layer, layer_state_t::eLayerSkipScreenshot,
+ layer_state_t::eLayerSkipScreenshot)
+ .apply();
+
+ {
+ // Can't screenshot child layer since the parent has the flag
+ // eLayerSkipScreenshot
+ ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ mCapture->expectColor(childBounds, {63, 63, 195, 255});
+ mCapture->expectBorder(childBounds, {63, 63, 195, 255});
+ mCapture->expectBorder(bounds, {63, 63, 195, 255});
+ }
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerWithUid) {
+ uid_t fakeUid = 12345;
+
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
+ ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ mBGSurfaceControl.get()));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+ Transaction().show(layer).setLayer(layer, INT32_MAX).apply();
+
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = mBGSurfaceControl->getHandle();
+ captureArgs.childrenOnly = false;
+
+ // Make sure red layer with the background layer is screenshot.
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ mCapture->expectBorder(Rect(0, 0, 32, 32), {63, 63, 195, 255});
+
+ // From non system uid, can't request screenshot without a specified uid.
+ std::unique_ptr<UIDFaker> uidFaker = std::make_unique<UIDFaker>(fakeUid);
+
+ ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureLayers(captureArgs, mCaptureResults));
+
+ // Make screenshot request with current uid set. No layers were created with the current
+ // uid so screenshot will be black.
+ captureArgs.uid = fakeUid;
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(0, 0, 32, 32), Color::TRANSPARENT);
+ mCapture->expectBorder(Rect(0, 0, 32, 32), Color::TRANSPARENT);
+
+ sp<SurfaceControl> layerWithFakeUid;
+ // Create a new layer with the current uid
+ ASSERT_NO_FATAL_FAILURE(layerWithFakeUid =
+ createLayer("new test layer", 32, 32,
+ ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ mBGSurfaceControl.get()));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layerWithFakeUid, Color::GREEN, 32, 32));
+ Transaction()
+ .show(layerWithFakeUid)
+ .setLayer(layerWithFakeUid, INT32_MAX)
+ .setPosition(layerWithFakeUid, 128, 128)
+ // reparent a layer that was created with a different uid to the new layer.
+ .reparent(layer, layerWithFakeUid)
+ .apply();
+
+ // Screenshot from the fakeUid caller with the uid requested allows the layer
+ // with that uid to be screenshotted. The child layer is skipped since it was created
+ // from a different uid.
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(128, 128, 160, 160), Color::GREEN);
+ mCapture->expectBorder(Rect(128, 128, 160, 160), Color::TRANSPARENT);
+
+ // Clear fake calling uid so it's back to system.
+ uidFaker = nullptr;
+ // Screenshot from the test caller with the uid requested allows the layer
+ // with that uid to be screenshotted. The child layer is skipped since it was created
+ // from a different uid.
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(128, 128, 160, 160), Color::GREEN);
+ mCapture->expectBorder(Rect(128, 128, 160, 160), Color::TRANSPARENT);
+
+ // Screenshot from the fakeUid caller with no uid requested allows everything to be screenshot.
+ captureArgs.uid = -1;
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(128, 128, 160, 160), Color::RED);
+ mCapture->expectBorder(Rect(128, 128, 160, 160), {63, 63, 195, 255});
+}
+
+TEST_F(ScreenCaptureTest, CaptureWithGrayscale) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
+ ISurfaceComposerClient::eFXSurfaceBufferState,
+ mBGSurfaceControl.get()));
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+ Transaction().show(layer).setLayer(layer, INT32_MAX).apply();
+
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = layer->getHandle();
+
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(0, 0, 32, 32), Color::RED);
+
+ captureArgs.grayscale = true;
+
+ const uint8_t tolerance = 1;
+
+ // Values based on SurfaceFlinger::calculateColorMatrix
+ float3 luminance{0.213f, 0.715f, 0.072f};
+
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+
+ uint8_t expectedColor = luminance.r * 255;
+ mCapture->expectColor(Rect(0, 0, 32, 32),
+ Color{expectedColor, expectedColor, expectedColor, 255}, tolerance);
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLUE, 32, 32));
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+
+ expectedColor = luminance.b * 255;
+ mCapture->expectColor(Rect(0, 0, 32, 32),
+ Color{expectedColor, expectedColor, expectedColor, 255}, tolerance);
+}
+
+// In the following tests we verify successful skipping of a parent layer,
+// so we use the same verification logic and only change how we mutate
+// the parent layer to verify that various properties are ignored.
+class ScreenCaptureChildOnlyTest : public ScreenCaptureTest {
+public:
+ void SetUp() override {
+ ScreenCaptureTest::SetUp();
+
+ mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888, 0,
+ mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200);
+
+ SurfaceComposerClient::Transaction().show(mChild).apply(true);
+ }
+
+ void verify(std::function<void()> verifyStartingState) {
+ // Verify starting state before a screenshot is taken.
+ verifyStartingState();
+
+ // Verify child layer does not inherit any of the properties of its
+ // parent when its screenshot is captured.
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = mFGSurfaceControl->getHandle();
+ captureArgs.childrenOnly = true;
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->checkPixel(10, 10, 0, 0, 0);
+ mCapture->expectChildColor(0, 0);
+
+ // Verify all assumptions are still true after the screenshot is taken.
+ verifyStartingState();
+ }
+
+ std::unique_ptr<ScreenCapture> mCapture;
+ sp<SurfaceControl> mChild;
+};
+
+// Regression test b/76099859
+TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentVisibility) {
+ SurfaceComposerClient::Transaction().hide(mFGSurfaceControl).apply(true);
+
+ // Even though the parent is hidden we should still capture the child.
+
+ // Before and after reparenting, verify child is properly hidden
+ // when rendering full-screen.
+ verify([&] { screenshot()->expectBGColor(64, 64); });
+}
+
+TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentCrop) {
+ SurfaceComposerClient::Transaction().setCrop(mFGSurfaceControl, Rect(0, 0, 1, 1)).apply(true);
+
+ // Even though the parent is cropped out we should still capture the child.
+
+ // Before and after reparenting, verify child is cropped by parent.
+ verify([&] { screenshot()->expectBGColor(65, 65); });
+}
+
+// Regression test b/124372894
+TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresTransform) {
+ SurfaceComposerClient::Transaction().setMatrix(mFGSurfaceControl, 2, 0, 0, 2).apply(true);
+
+ // We should not inherit the parent scaling.
+
+ // Before and after reparenting, verify child is properly scaled.
+ verify([&] { screenshot()->expectChildColor(80, 80); });
+}
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/SetFrameRate_test.cpp b/services/surfaceflinger/tests/SetFrameRate_test.cpp
index 02ba9e2..d1bed0c 100644
--- a/services/surfaceflinger/tests/SetFrameRate_test.cpp
+++ b/services/surfaceflinger/tests/SetFrameRate_test.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#include <system/window.h>
#include <thread>
@@ -50,8 +46,8 @@
}
}
- const int mLayerWidth = 32;
- const int mLayerHeight = 32;
+ const uint32_t mLayerWidth = 32;
+ const uint32_t mLayerHeight = 32;
sp<SurfaceControl> mLayer;
uint32_t mLayerType;
};
@@ -59,26 +55,27 @@
TEST_F(SetFrameRateTest, BufferQueueLayerSetFrameRate) {
CreateLayer(ISurfaceComposerClient::eFXSurfaceBufferQueue);
native_window_set_frame_rate(mLayer->getSurface().get(), 100.f,
- ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT);
+ ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ /* shouldBeSeamless */ true);
ASSERT_NO_FATAL_FAILURE(PostBuffers(Color::RED));
Transaction()
- .setFrameRate(mLayer, 200.f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT)
+ .setFrameRate(mLayer, 200.f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ /* shouldBeSeamless */ true)
.apply();
ASSERT_NO_FATAL_FAILURE(PostBuffers(Color::RED));
native_window_set_frame_rate(mLayer->getSurface().get(), 300.f,
- ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT);
+ ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ /* shouldBeSeamless */ true);
ASSERT_NO_FATAL_FAILURE(PostBuffers(Color::RED));
}
TEST_F(SetFrameRateTest, BufferStateLayerSetFrameRate) {
CreateLayer(ISurfaceComposerClient::eFXSurfaceBufferState);
Transaction()
- .setFrameRate(mLayer, 400.f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT)
+ .setFrameRate(mLayer, 400.f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ /* shouldBeSeamless */ true)
.apply();
ASSERT_NO_FATAL_FAILURE(PostBuffers(Color::GREEN));
}
} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index 8d97f27..ee4e863 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -17,6 +17,7 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
@@ -26,7 +27,7 @@
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <private/gui/ComposerService.h>
-#include <ui/DisplayConfig.h>
+#include <ui/DisplayMode.h>
#include <fstream>
#include <random>
@@ -39,12 +40,10 @@
using Trace = surfaceflinger::Trace;
using Increment = surfaceflinger::Increment;
-constexpr int32_t SCALING_UPDATE = 1;
constexpr uint32_t BUFFER_UPDATES = 18;
constexpr uint32_t LAYER_UPDATE = INT_MAX - 2;
constexpr uint32_t SIZE_UPDATE = 134;
constexpr uint32_t STACK_UPDATE = 1;
-constexpr uint64_t DEFERRED_UPDATE = 0;
constexpr int32_t RELATIVE_Z = 42;
constexpr float ALPHA_UPDATE = 0.29f;
constexpr float CORNER_RADIUS_UPDATE = 0.2f;
@@ -52,6 +51,7 @@
constexpr float POSITION_UPDATE = 121;
const Rect CROP_UPDATE(16, 16, 32, 32);
const float SHADOW_RADIUS_UPDATE = 35.0f;
+std::vector<BlurRegion> BLUR_REGIONS_UPDATE;
const String8 DISPLAY_NAME("SurfaceInterceptor Display Test");
constexpr auto TEST_BG_SURFACE_NAME = "BG Interceptor Test Surface";
@@ -182,6 +182,7 @@
bool cornerRadiusUpdateFound(const SurfaceChange& change, bool foundCornerRadius);
bool backgroundBlurRadiusUpdateFound(const SurfaceChange& change,
bool foundBackgroundBlurRadius);
+ bool blurRegionsUpdateFound(const SurfaceChange& change, bool foundBlurRegions);
bool matrixUpdateFound(const SurfaceChange& change, bool foundMatrix);
bool scalingModeUpdateFound(const SurfaceChange& change, bool foundScalingMode);
bool transparentRegionHintUpdateFound(const SurfaceChange& change, bool foundTransparentRegion);
@@ -189,11 +190,8 @@
bool hiddenFlagUpdateFound(const SurfaceChange& change, bool foundHiddenFlag);
bool opaqueFlagUpdateFound(const SurfaceChange& change, bool foundOpaqueFlag);
bool secureFlagUpdateFound(const SurfaceChange& change, bool foundSecureFlag);
- bool deferredTransactionUpdateFound(const SurfaceChange& change, bool foundDeferred);
bool reparentUpdateFound(const SurfaceChange& change, bool found);
bool relativeParentUpdateFound(const SurfaceChange& change, bool found);
- bool detachChildrenUpdateFound(const SurfaceChange& change, bool found);
- bool reparentChildrenUpdateFound(const SurfaceChange& change, bool found);
bool shadowRadiusUpdateFound(const SurfaceChange& change, bool found);
bool surfaceUpdateFound(const Trace& trace, SurfaceChange::SurfaceChangeCase changeCase);
@@ -220,18 +218,15 @@
void cropUpdate(Transaction&);
void cornerRadiusUpdate(Transaction&);
void backgroundBlurRadiusUpdate(Transaction&);
+ void blurRegionsUpdate(Transaction&);
void matrixUpdate(Transaction&);
- void overrideScalingModeUpdate(Transaction&);
void transparentRegionHintUpdate(Transaction&);
void layerStackUpdate(Transaction&);
void hiddenFlagUpdate(Transaction&);
void opaqueFlagUpdate(Transaction&);
void secureFlagUpdate(Transaction&);
- void deferredTransactionUpdate(Transaction&);
void reparentUpdate(Transaction&);
void relativeParentUpdate(Transaction&);
- void detachChildrenUpdate(Transaction&);
- void reparentChildrenUpdate(Transaction&);
void shadowRadiusUpdate(Transaction&);
void surfaceCreation(Transaction&);
void displayCreation(Transaction&);
@@ -267,9 +262,9 @@
const auto display = SurfaceComposerClient::getInternalDisplayToken();
ASSERT_FALSE(display == nullptr);
- DisplayConfig config;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
- const ui::Size& resolution = config.resolution;
+ ui::DisplayMode mode;
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+ const ui::Size& resolution = mode.resolution;
// Background surface
mBGSurfaceControl =
@@ -359,22 +354,24 @@
t.setBackgroundBlurRadius(mBGSurfaceControl, BACKGROUND_BLUR_RADIUS_UPDATE);
}
+void SurfaceInterceptorTest::blurRegionsUpdate(Transaction& t) {
+ BLUR_REGIONS_UPDATE.empty();
+ BLUR_REGIONS_UPDATE.push_back(BlurRegion());
+ t.setBlurRegions(mBGSurfaceControl, BLUR_REGIONS_UPDATE);
+}
+
void SurfaceInterceptorTest::layerUpdate(Transaction& t) {
t.setLayer(mBGSurfaceControl, LAYER_UPDATE);
}
void SurfaceInterceptorTest::cropUpdate(Transaction& t) {
- t.setCrop_legacy(mBGSurfaceControl, CROP_UPDATE);
+ t.setCrop(mBGSurfaceControl, CROP_UPDATE);
}
void SurfaceInterceptorTest::matrixUpdate(Transaction& t) {
t.setMatrix(mBGSurfaceControl, M_SQRT1_2, M_SQRT1_2, -M_SQRT1_2, M_SQRT1_2);
}
-void SurfaceInterceptorTest::overrideScalingModeUpdate(Transaction& t) {
- t.setOverrideScalingMode(mBGSurfaceControl, SCALING_UPDATE);
-}
-
void SurfaceInterceptorTest::transparentRegionHintUpdate(Transaction& t) {
Region region(CROP_UPDATE);
t.setTransparentRegionHint(mBGSurfaceControl, region);
@@ -396,25 +393,12 @@
t.setFlags(mBGSurfaceControl, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure);
}
-void SurfaceInterceptorTest::deferredTransactionUpdate(Transaction& t) {
- t.deferTransactionUntil_legacy(mBGSurfaceControl, mBGSurfaceControl->getHandle(),
- DEFERRED_UPDATE);
-}
-
void SurfaceInterceptorTest::reparentUpdate(Transaction& t) {
- t.reparent(mBGSurfaceControl, mFGSurfaceControl->getHandle());
+ t.reparent(mBGSurfaceControl, mFGSurfaceControl);
}
void SurfaceInterceptorTest::relativeParentUpdate(Transaction& t) {
- t.setRelativeLayer(mBGSurfaceControl, mFGSurfaceControl->getHandle(), RELATIVE_Z);
-}
-
-void SurfaceInterceptorTest::detachChildrenUpdate(Transaction& t) {
- t.detachChildren(mBGSurfaceControl);
-}
-
-void SurfaceInterceptorTest::reparentChildrenUpdate(Transaction& t) {
- t.reparentChildren(mBGSurfaceControl, mFGSurfaceControl->getHandle());
+ t.setRelativeLayer(mBGSurfaceControl, mFGSurfaceControl, RELATIVE_Z);
}
void SurfaceInterceptorTest::shadowRadiusUpdate(Transaction& t) {
@@ -422,7 +406,7 @@
}
void SurfaceInterceptorTest::displayCreation(Transaction&) {
- sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, true);
+ sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false);
SurfaceComposerClient::destroyDisplay(testDisplay);
}
@@ -437,19 +421,16 @@
runInTransaction(&SurfaceInterceptorTest::alphaUpdate);
runInTransaction(&SurfaceInterceptorTest::cornerRadiusUpdate);
runInTransaction(&SurfaceInterceptorTest::backgroundBlurRadiusUpdate);
+ runInTransaction(&SurfaceInterceptorTest::blurRegionsUpdate);
runInTransaction(&SurfaceInterceptorTest::layerUpdate);
runInTransaction(&SurfaceInterceptorTest::cropUpdate);
runInTransaction(&SurfaceInterceptorTest::matrixUpdate);
- runInTransaction(&SurfaceInterceptorTest::overrideScalingModeUpdate);
runInTransaction(&SurfaceInterceptorTest::transparentRegionHintUpdate);
runInTransaction(&SurfaceInterceptorTest::layerStackUpdate);
runInTransaction(&SurfaceInterceptorTest::hiddenFlagUpdate);
runInTransaction(&SurfaceInterceptorTest::opaqueFlagUpdate);
runInTransaction(&SurfaceInterceptorTest::secureFlagUpdate);
- runInTransaction(&SurfaceInterceptorTest::deferredTransactionUpdate);
runInTransaction(&SurfaceInterceptorTest::reparentUpdate);
- runInTransaction(&SurfaceInterceptorTest::reparentChildrenUpdate);
- runInTransaction(&SurfaceInterceptorTest::detachChildrenUpdate);
runInTransaction(&SurfaceInterceptorTest::relativeParentUpdate);
runInTransaction(&SurfaceInterceptorTest::shadowRadiusUpdate);
}
@@ -526,6 +507,17 @@
return foundBackgroundBlur;
}
+bool SurfaceInterceptorTest::blurRegionsUpdateFound(const SurfaceChange& change,
+ bool foundBlurRegions) {
+ bool hasBlurRegions(change.blur_regions().blur_regions_size() == BLUR_REGIONS_UPDATE.size());
+ if (hasBlurRegions && !foundBlurRegions) {
+ foundBlurRegions = true;
+ } else if (hasBlurRegions && foundBlurRegions) {
+ []() { FAIL(); }();
+ }
+ return foundBlurRegions;
+}
+
bool SurfaceInterceptorTest::layerUpdateFound(const SurfaceChange& change, bool foundLayer) {
bool hasLayer(change.layer().layer() == LAYER_UPDATE);
if (hasLayer && !foundLayer) {
@@ -562,17 +554,6 @@
return foundMatrix;
}
-bool SurfaceInterceptorTest::scalingModeUpdateFound(const SurfaceChange& change,
- bool foundScalingMode) {
- bool hasScalingUpdate(change.override_scaling_mode().override_scaling_mode() == SCALING_UPDATE);
- if (hasScalingUpdate && !foundScalingMode) {
- foundScalingMode = true;
- } else if (hasScalingUpdate && foundScalingMode) {
- [] () { FAIL(); }();
- }
- return foundScalingMode;
-}
-
bool SurfaceInterceptorTest::transparentRegionHintUpdateFound(const SurfaceChange& change,
bool foundTransparentRegion) {
auto traceRegion = change.transparent_region_hint().region(0);
@@ -632,18 +613,6 @@
return foundSecureFlag;
}
-bool SurfaceInterceptorTest::deferredTransactionUpdateFound(const SurfaceChange& change,
- bool foundDeferred) {
- bool hasId(change.deferred_transaction().layer_id() == mBGLayerId);
- bool hasFrameNumber(change.deferred_transaction().frame_number() == DEFERRED_UPDATE);
- if (hasId && hasFrameNumber && !foundDeferred) {
- foundDeferred = true;
- } else if (hasId && hasFrameNumber && foundDeferred) {
- [] () { FAIL(); }();
- }
- return foundDeferred;
-}
-
bool SurfaceInterceptorTest::reparentUpdateFound(const SurfaceChange& change, bool found) {
bool hasId(change.reparent().parent_id() == mFGLayerId);
if (hasId && !found) {
@@ -664,26 +633,6 @@
return found;
}
-bool SurfaceInterceptorTest::detachChildrenUpdateFound(const SurfaceChange& change, bool found) {
- bool detachChildren(change.detach_children().detach_children());
- if (detachChildren && !found) {
- found = true;
- } else if (detachChildren && found) {
- []() { FAIL(); }();
- }
- return found;
-}
-
-bool SurfaceInterceptorTest::reparentChildrenUpdateFound(const SurfaceChange& change, bool found) {
- bool hasId(change.reparent_children().parent_id() == mFGLayerId);
- if (hasId && !found) {
- found = true;
- } else if (hasId && found) {
- []() { FAIL(); }();
- }
- return found;
-}
-
bool SurfaceInterceptorTest::shadowRadiusUpdateFound(const SurfaceChange& change,
bool foundShadowRadius) {
bool hasShadowRadius(change.shadow_radius().radius() == SHADOW_RADIUS_UPDATE);
@@ -725,12 +674,12 @@
case SurfaceChange::SurfaceChangeCase::kBackgroundBlurRadius:
foundUpdate = backgroundBlurRadiusUpdateFound(change, foundUpdate);
break;
+ case SurfaceChange::SurfaceChangeCase::kBlurRegions:
+ foundUpdate = blurRegionsUpdateFound(change, foundUpdate);
+ break;
case SurfaceChange::SurfaceChangeCase::kMatrix:
foundUpdate = matrixUpdateFound(change, foundUpdate);
break;
- case SurfaceChange::SurfaceChangeCase::kOverrideScalingMode:
- foundUpdate = scalingModeUpdateFound(change, foundUpdate);
- break;
case SurfaceChange::SurfaceChangeCase::kTransparentRegionHint:
foundUpdate = transparentRegionHintUpdateFound(change, foundUpdate);
break;
@@ -746,21 +695,12 @@
case SurfaceChange::SurfaceChangeCase::kSecureFlag:
foundUpdate = secureFlagUpdateFound(change, foundUpdate);
break;
- case SurfaceChange::SurfaceChangeCase::kDeferredTransaction:
- foundUpdate = deferredTransactionUpdateFound(change, foundUpdate);
- break;
case SurfaceChange::SurfaceChangeCase::kReparent:
foundUpdate = reparentUpdateFound(change, foundUpdate);
break;
- case SurfaceChange::SurfaceChangeCase::kReparentChildren:
- foundUpdate = reparentChildrenUpdateFound(change, foundUpdate);
- break;
case SurfaceChange::SurfaceChangeCase::kRelativeParent:
foundUpdate = relativeParentUpdateFound(change, foundUpdate);
break;
- case SurfaceChange::SurfaceChangeCase::kDetachChildren:
- foundUpdate = detachChildrenUpdateFound(change, foundUpdate);
- break;
case SurfaceChange::SurfaceChangeCase::kShadowRadius:
foundUpdate = shadowRadiusUpdateFound(change, foundUpdate);
break;
@@ -781,17 +721,13 @@
ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kLayer));
ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kCrop));
ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kMatrix));
- ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kOverrideScalingMode));
ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kTransparentRegionHint));
ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kLayerStack));
ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kHiddenFlag));
ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kOpaqueFlag));
ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kSecureFlag));
- ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kDeferredTransaction));
ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kReparent));
- ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kReparentChildren));
ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kRelativeParent));
- ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kDetachChildren));
}
bool SurfaceInterceptorTest::surfaceCreationFound(const Increment& increment, bool foundSurface) {
@@ -819,7 +755,7 @@
bool SurfaceInterceptorTest::displayCreationFound(const Increment& increment, bool foundDisplay) {
bool isMatch(increment.display_creation().name() == DISPLAY_NAME.string() &&
- increment.display_creation().is_secure());
+ !increment.display_creation().is_secure());
if (isMatch && !foundDisplay) {
foundDisplay = true;
} else if (isMatch && foundDisplay) {
@@ -912,13 +848,13 @@
SurfaceChange::SurfaceChangeCase::kBackgroundBlurRadius);
}
-TEST_F(SurfaceInterceptorTest, InterceptMatrixUpdateWorks) {
- captureTest(&SurfaceInterceptorTest::matrixUpdate, SurfaceChange::SurfaceChangeCase::kMatrix);
+TEST_F(SurfaceInterceptorTest, InterceptBlurRegionsUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::blurRegionsUpdate,
+ SurfaceChange::SurfaceChangeCase::kBlurRegions);
}
-TEST_F(SurfaceInterceptorTest, InterceptOverrideScalingModeUpdateWorks) {
- captureTest(&SurfaceInterceptorTest::overrideScalingModeUpdate,
- SurfaceChange::SurfaceChangeCase::kOverrideScalingMode);
+TEST_F(SurfaceInterceptorTest, InterceptMatrixUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::matrixUpdate, SurfaceChange::SurfaceChangeCase::kMatrix);
}
TEST_F(SurfaceInterceptorTest, InterceptTransparentRegionHintUpdateWorks) {
@@ -946,31 +882,16 @@
SurfaceChange::SurfaceChangeCase::kSecureFlag);
}
-TEST_F(SurfaceInterceptorTest, InterceptDeferredTransactionUpdateWorks) {
- captureTest(&SurfaceInterceptorTest::deferredTransactionUpdate,
- SurfaceChange::SurfaceChangeCase::kDeferredTransaction);
-}
-
TEST_F(SurfaceInterceptorTest, InterceptReparentUpdateWorks) {
captureTest(&SurfaceInterceptorTest::reparentUpdate,
SurfaceChange::SurfaceChangeCase::kReparent);
}
-TEST_F(SurfaceInterceptorTest, InterceptReparentChildrenUpdateWorks) {
- captureTest(&SurfaceInterceptorTest::reparentChildrenUpdate,
- SurfaceChange::SurfaceChangeCase::kReparentChildren);
-}
-
TEST_F(SurfaceInterceptorTest, InterceptRelativeParentUpdateWorks) {
captureTest(&SurfaceInterceptorTest::relativeParentUpdate,
SurfaceChange::SurfaceChangeCase::kRelativeParent);
}
-TEST_F(SurfaceInterceptorTest, InterceptDetachChildrenUpdateWorks) {
- captureTest(&SurfaceInterceptorTest::detachChildrenUpdate,
- SurfaceChange::SurfaceChangeCase::kDetachChildren);
-}
-
TEST_F(SurfaceInterceptorTest, InterceptShadowRadiusUpdateWorks) {
captureTest(&SurfaceInterceptorTest::shadowRadiusUpdate,
SurfaceChange::SurfaceChangeCase::kShadowRadius);
@@ -1000,11 +921,6 @@
ASSERT_TRUE(singleIncrementFound(capturedTrace, Increment::IncrementCase::kDisplayDeletion));
}
-TEST_F(SurfaceInterceptorTest, InterceptBufferUpdateWorks) {
- captureTest(&SurfaceInterceptorTest::nBufferUpdates,
- &SurfaceInterceptorTest::bufferUpdatesFound);
-}
-
// If the interceptor is enabled while buffer updates are being pushed, the interceptor should
// first create a snapshot of the existing displays and surfaces and then start capturing
// the buffer updates
@@ -1020,26 +936,6 @@
const auto& firstIncrement = capturedTrace.mutable_increment(0);
ASSERT_EQ(firstIncrement->increment_case(), Increment::IncrementCase::kDisplayCreation);
}
-
-TEST_F(SurfaceInterceptorTest, InterceptSimultaneousUpdatesWorks) {
- enableInterceptor();
- setupBackgroundSurface();
- std::thread bufferUpdates(&SurfaceInterceptorTest::nBufferUpdates, this);
- std::thread surfaceUpdates(&SurfaceInterceptorTest::runAllUpdates, this);
- runInTransaction(&SurfaceInterceptorTest::surfaceCreation);
- bufferUpdates.join();
- surfaceUpdates.join();
- disableInterceptor();
-
- Trace capturedTrace;
- ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
- preProcessTrace(capturedTrace);
-
- assertAllUpdatesFound(capturedTrace);
- ASSERT_TRUE(bufferUpdatesFound(capturedTrace));
- ASSERT_TRUE(singleIncrementFound(capturedTrace, Increment::IncrementCase::kSurfaceCreation));
}
-}
-
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index f0af363..89f6086 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -40,9 +40,9 @@
ui::DisplayState displayState;
SurfaceComposerClient::getDisplayState(displayToken, &displayState);
- DisplayConfig displayConfig;
- SurfaceComposerClient::getActiveDisplayConfig(displayToken, &displayConfig);
- const ui::Size& resolution = displayConfig.resolution;
+ ui::DisplayMode displayMode;
+ SurfaceComposerClient::getActiveDisplayMode(displayToken, &displayMode);
+ const ui::Size& resolution = displayMode.resolution;
sp<IBinder> vDisplay;
sp<IGraphicBufferProducer> producer;
@@ -57,6 +57,8 @@
// Sample usage bits from screenrecord
GRALLOC_USAGE_HW_VIDEO_ENCODER |
GRALLOC_USAGE_SW_READ_OFTEN);
+ sp<BufferListener> listener = new BufferListener(this);
+ itemConsumer->setFrameAvailableListener(listener);
vDisplay = SurfaceComposerClient::createDisplay(String8("VirtualDisplay"),
false /*secure*/);
@@ -65,9 +67,16 @@
t.setDisplaySurface(vDisplay, producer);
t.setDisplayLayerStack(vDisplay, 0);
t.setDisplayProjection(vDisplay, displayState.orientation,
- Rect(displayState.viewport), Rect(resolution));
+ Rect(displayState.layerStackSpaceRect), Rect(resolution));
t.apply();
SurfaceComposerClient::Transaction().apply(true);
+
+ std::unique_lock lock(mMutex);
+ mAvailable = false;
+ // Wait for frame buffer ready.
+ mCondition.wait_for(lock, std::chrono::seconds(2),
+ [this]() NO_THREAD_SAFETY_ANALYSIS { return mAvailable; });
+
BufferItem item;
itemConsumer->acquireBuffer(&item, 0, true);
auto sc = std::make_unique<ScreenCapture>(item.mGraphicBuffer);
@@ -80,6 +89,23 @@
protected:
LayerTransactionTest* mDelegate;
RenderPath mRenderPath;
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+ bool mAvailable = false;
+
+ void onFrameAvailable() {
+ std::unique_lock lock(mMutex);
+ mAvailable = true;
+ mCondition.notify_all();
+ }
+
+ class BufferListener : public ConsumerBase::FrameAvailableListener {
+ public:
+ BufferListener(LayerRenderPathTestHarness* owner) : mOwner(owner) {}
+ LayerRenderPathTestHarness* mOwner;
+
+ void onFrameAvailable(const BufferItem& /*item*/) { mOwner->onFrameAvailable(); }
+ };
};
class LayerTypeTransactionHarness : public LayerTransactionTest {
@@ -98,14 +124,14 @@
outTransformHint, format);
}
- void fillLayerColor(const sp<SurfaceControl>& layer, const Color& color, int32_t bufferWidth,
- int32_t bufferHeight) {
+ void fillLayerColor(const sp<SurfaceControl>& layer, const Color& color, uint32_t bufferWidth,
+ uint32_t bufferHeight) {
ASSERT_NO_FATAL_FAILURE(LayerTransactionTest::fillLayerColor(mLayerType, layer, color,
bufferWidth, bufferHeight));
}
- void fillLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth,
- int32_t bufferHeight, const Color& topLeft, const Color& topRight,
+ void fillLayerQuadrant(const sp<SurfaceControl>& layer, uint32_t bufferWidth,
+ uint32_t bufferHeight, const Color& topLeft, const Color& topRight,
const Color& bottomLeft, const Color& bottomRight) {
ASSERT_NO_FATAL_FAILURE(LayerTransactionTest::fillLayerQuadrant(mLayerType, layer,
bufferWidth, bufferHeight,
diff --git a/services/surfaceflinger/tests/VirtualDisplay_test.cpp b/services/surfaceflinger/tests/VirtualDisplay_test.cpp
index 9fd2227..18e0806 100644
--- a/services/surfaceflinger/tests/VirtualDisplay_test.cpp
+++ b/services/surfaceflinger/tests/VirtualDisplay_test.cpp
@@ -52,6 +52,8 @@
virtualDisplay.clear();
// Sync here to ensure the display was completely destroyed in SF
t.apply(true);
+ // add another sync since we are deferring the display destruction
+ t.apply(true);
sp<Surface> surface = new Surface(mProducer);
sp<ANativeWindow> window(surface);
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
index 5cbf2ef..b38032d 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
@@ -907,6 +907,7 @@
}
void FakeComposerClient::onSurfaceFlingerStop() {
+ mSurfaceComposer->enableVSyncInjections(false);
mSurfaceComposer->dispose();
mSurfaceComposer.clear();
}
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index a03fd89..162711d 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -17,6 +17,7 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
// #define LOG_NDEBUG 0
#undef LOG_TAG
@@ -27,6 +28,7 @@
#include "FakeComposerUtils.h"
#include "MockComposerHal.h"
+#include <binder/Parcel.h>
#include <gui/DisplayEventReceiver.h>
#include <gui/ISurfaceComposer.h>
#include <gui/LayerDebugInfo.h>
@@ -41,7 +43,8 @@
#include <hwbinder/ProcessState.h>
#include <log/log.h>
#include <private/gui/ComposerService.h>
-#include <ui/DisplayConfig.h>
+#include <ui/DisplayMode.h>
+#include <ui/DynamicDisplayInfo.h>
#include <utils/Looper.h>
#include <gmock/gmock.h>
@@ -61,16 +64,20 @@
// Mock test helpers
using ::testing::_;
-using ::testing::AtLeast;
using ::testing::DoAll;
-using ::testing::Invoke;
using ::testing::Return;
using ::testing::SetArgPointee;
using Transaction = SurfaceComposerClient::Transaction;
using Attribute = V2_4::IComposerClient::Attribute;
+using Display = V2_1::Display;
///////////////////////////////////////////////
+constexpr PhysicalDisplayId physicalIdFromHwcDisplayId(Display hwcId) {
+ return PhysicalDisplayId::fromPort(hwcId);
+}
+constexpr PhysicalDisplayId kPrimaryDisplayId = physicalIdFromHwcDisplayId(PRIMARY_DISPLAY);
+constexpr PhysicalDisplayId kExternalDisplayId = physicalIdFromHwcDisplayId(EXTERNAL_DISPLAY);
struct TestColor {
public:
@@ -84,7 +91,6 @@
constexpr static TestColor LIGHT_RED = {255, 177, 177, 255};
constexpr static TestColor GREEN = {63, 195, 63, 255};
constexpr static TestColor BLUE = {63, 63, 195, 255};
-constexpr static TestColor DARK_GRAY = {63, 63, 63, 255};
constexpr static TestColor LIGHT_GRAY = {200, 200, 200, 255};
// Fill an RGBA_8888 formatted surface with a single color.
@@ -152,7 +158,7 @@
self->mReceivedDisplayEvents.push_back(buffer[i]);
}
}
- ALOGD_IF(n < 0, "Error reading events (%s)\n", strerror(-n));
+ ALOGD_IF(n < 0, "Error reading events (%s)", strerror(-n));
return 1;
}
@@ -168,7 +174,7 @@
void setExpectationsForConfigs(Display display, std::vector<TestConfig> testConfigs,
Config activeConfig, V2_4::VsyncPeriodNanos defaultVsyncPeriod) {
std::vector<Config> configIds;
- for (int i = 0; i < testConfigs.size(); i++) {
+ for (size_t i = 0; i < testConfigs.size(); i++) {
configIds.push_back(testConfigs[i].id);
EXPECT_CALL(*mMockComposer,
@@ -238,7 +244,7 @@
ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
mReceiver.reset(new DisplayEventReceiver(ISurfaceComposer::eVsyncSourceApp,
- ISurfaceComposer::eConfigChangedDispatch));
+ ISurfaceComposer::EventRegistration::modeChanged));
mLooper = new Looper(false);
mLooper->addFd(mReceiver->getFd(), 0, ALOOPER_EVENT_INPUT, processDisplayEvents, this);
}
@@ -262,16 +268,20 @@
mMockComposer = nullptr;
}
- void waitForDisplayTransaction() {
+ void waitForDisplayTransaction(Display display) {
// Both a refresh and a vsync event are needed to apply pending display
// transactions.
- mFakeComposerClient->refreshDisplay(EXTERNAL_DISPLAY);
+ mFakeComposerClient->refreshDisplay(display);
mFakeComposerClient->runVSyncAndWait();
// Extra vsync and wait to avoid a 10% flake due to a race.
mFakeComposerClient->runVSyncAndWait();
}
+ bool waitForHotplugEvent(Display displayId, bool connected) {
+ return waitForHotplugEvent(PhysicalDisplayId(displayId), connected);
+ }
+
bool waitForHotplugEvent(PhysicalDisplayId displayId, bool connected) {
int waitCount = 20;
while (waitCount--) {
@@ -280,9 +290,8 @@
mReceivedDisplayEvents.pop_front();
ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG,
- "event hotplug: displayId %" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
- ", connected %d\t",
- event.header.displayId, event.hotplug.connected);
+ "event hotplug: displayId %s, connected %d",
+ to_string(event.header.displayId).c_str(), event.hotplug.connected);
if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG &&
event.header.displayId == displayId && event.hotplug.connected == connected) {
@@ -295,20 +304,20 @@
return false;
}
- bool waitForConfigChangedEvent(PhysicalDisplayId displayId, int32_t configId) {
+ bool waitForModeChangedEvent(Display display, int32_t modeId) {
+ PhysicalDisplayId displayId(display);
int waitCount = 20;
while (waitCount--) {
while (!mReceivedDisplayEvents.empty()) {
auto event = mReceivedDisplayEvents.front();
mReceivedDisplayEvents.pop_front();
- ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED,
- "event config: displayId %" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
- ", configId %d\t",
- event.header.displayId, event.config.configId);
+ ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE,
+ "event mode: displayId %s, modeId %d",
+ to_string(event.header.displayId).c_str(), event.modeChange.modeId);
- if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED &&
- event.header.displayId == displayId && event.config.configId == configId) {
+ if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE &&
+ event.header.displayId == displayId && event.modeChange.modeId == modeId) {
return true;
}
}
@@ -331,18 +340,18 @@
mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
V2_1::IComposerCallback::Connection::CONNECTED);
- waitForDisplayTransaction();
+ waitForDisplayTransaction(EXTERNAL_DISPLAY);
EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
{
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
EXPECT_FALSE(display == nullptr);
- DisplayConfig config;
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
- const ui::Size& resolution = config.resolution;
+ ui::DisplayMode mode;
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+ const ui::Size& resolution = mode.resolution;
EXPECT_EQ(ui::Size(200, 400), resolution);
- EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
+ EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate);
auto surfaceControl =
mComposerClient->createSurface(String8("Display Test Surface Foo"),
@@ -362,16 +371,16 @@
mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
V2_1::IComposerCallback::Connection::DISCONNECTED);
- waitForDisplayTransaction();
+ waitForDisplayTransaction(EXTERNAL_DISPLAY);
mFakeComposerClient->clearFrames();
EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
{
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
EXPECT_TRUE(display == nullptr);
- DisplayConfig config;
- EXPECT_NE(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+ ui::DisplayMode mode;
+ EXPECT_NE(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
}
}
@@ -393,20 +402,20 @@
mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
V2_1::IComposerCallback::Connection::CONNECTED);
- waitForDisplayTransaction();
+ waitForDisplayTransaction(EXTERNAL_DISPLAY);
EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
EXPECT_FALSE(display == nullptr);
- DisplayConfig config;
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
- EXPECT_EQ(ui::Size(200, 400), config.resolution);
- EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
+ ui::DisplayMode mode;
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+ EXPECT_EQ(ui::Size(200, 400), mode.resolution);
+ EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate);
mFakeComposerClient->clearFrames();
{
- const ui::Size& resolution = config.resolution;
+ const ui::Size& resolution = mode.resolution;
auto surfaceControl =
mComposerClient->createSurface(String8("Display Test Surface Foo"),
resolution.getWidth(), resolution.getHeight(),
@@ -423,11 +432,12 @@
}
}
- Vector<DisplayConfig> configs;
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
- EXPECT_EQ(configs.size(), 2);
+ ui::DynamicDisplayInfo info;
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info));
+ const auto& modes = info.supportedDisplayModes;
+ EXPECT_EQ(modes.size(), 2);
- // change active config
+ // change active mode
if (mIs2_4Client) {
EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 2, _, _))
@@ -437,28 +447,28 @@
.WillOnce(Return(V2_1::Error::NONE));
}
- for (int i = 0; i < configs.size(); i++) {
- const auto& config = configs[i];
- if (config.resolution.getWidth() == 800) {
+ for (int i = 0; i < modes.size(); i++) {
+ const auto& mode = modes[i];
+ if (mode.resolution.getWidth() == 800) {
EXPECT_EQ(NO_ERROR,
- SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
- config.refreshRate,
- config.refreshRate,
- config.refreshRate,
- config.refreshRate));
- waitForDisplayTransaction();
- EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
+ SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false,
+ mode.refreshRate,
+ mode.refreshRate,
+ mode.refreshRate,
+ mode.refreshRate));
+ waitForDisplayTransaction(EXTERNAL_DISPLAY);
+ EXPECT_TRUE(waitForModeChangedEvent(EXTERNAL_DISPLAY, i));
break;
}
}
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
- EXPECT_EQ(ui::Size(800, 1600), config.resolution);
- EXPECT_EQ(1e9f / 11'111'111, config.refreshRate);
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+ EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
+ EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate);
mFakeComposerClient->clearFrames();
{
- const ui::Size& resolution = config.resolution;
+ const ui::Size& resolution = mode.resolution;
auto surfaceControl =
mComposerClient->createSurface(String8("Display Test Surface Foo"),
resolution.getWidth(), resolution.getHeight(),
@@ -477,7 +487,7 @@
mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
V2_1::IComposerCallback::Connection::DISCONNECTED);
- waitForDisplayTransaction();
+ waitForDisplayTransaction(EXTERNAL_DISPLAY);
mFakeComposerClient->clearFrames();
EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
}
@@ -500,20 +510,20 @@
mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
V2_1::IComposerCallback::Connection::CONNECTED);
- waitForDisplayTransaction();
+ waitForDisplayTransaction(EXTERNAL_DISPLAY);
EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
EXPECT_FALSE(display == nullptr);
- DisplayConfig config;
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
- EXPECT_EQ(ui::Size(800, 1600), config.resolution);
- EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
+ ui::DisplayMode mode;
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+ EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
+ EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate);
mFakeComposerClient->clearFrames();
{
- const ui::Size& resolution = config.resolution;
+ const ui::Size& resolution = mode.resolution;
auto surfaceControl =
mComposerClient->createSurface(String8("Display Test Surface Foo"),
resolution.getWidth(), resolution.getHeight(),
@@ -530,11 +540,12 @@
}
}
- Vector<DisplayConfig> configs;
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
- EXPECT_EQ(configs.size(), 2);
+ ui::DynamicDisplayInfo info;
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info));
+ const auto& modes = info.supportedDisplayModes;
+ EXPECT_EQ(modes.size(), 2);
- // change active config
+ // change active mode
if (mIs2_4Client) {
EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 3, _, _))
.WillOnce(Return(V2_4::Error::NONE));
@@ -543,28 +554,28 @@
.WillOnce(Return(V2_1::Error::NONE));
}
- for (int i = 0; i < configs.size(); i++) {
- const auto& config = configs[i];
- if (config.refreshRate == 1e9f / 11'111'111) {
+ for (int i = 0; i < modes.size(); i++) {
+ const auto& mode = modes[i];
+ if (mode.refreshRate == 1e9f / 11'111'111) {
EXPECT_EQ(NO_ERROR,
- SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
- config.refreshRate,
- config.refreshRate,
- config.refreshRate,
- config.refreshRate));
- waitForDisplayTransaction();
- EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
+ SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false,
+ mode.refreshRate,
+ mode.refreshRate,
+ mode.refreshRate,
+ mode.refreshRate));
+ waitForDisplayTransaction(EXTERNAL_DISPLAY);
+ EXPECT_TRUE(waitForModeChangedEvent(EXTERNAL_DISPLAY, i));
break;
}
}
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
- EXPECT_EQ(ui::Size(800, 1600), config.resolution);
- EXPECT_EQ(1e9f / 11'111'111, config.refreshRate);
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+ EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
+ EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate);
mFakeComposerClient->clearFrames();
{
- const ui::Size& resolution = config.resolution;
+ const ui::Size& resolution = mode.resolution;
auto surfaceControl =
mComposerClient->createSurface(String8("Display Test Surface Foo"),
resolution.getWidth(), resolution.getHeight(),
@@ -583,7 +594,7 @@
mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
V2_1::IComposerCallback::Connection::DISCONNECTED);
- waitForDisplayTransaction();
+ waitForDisplayTransaction(EXTERNAL_DISPLAY);
mFakeComposerClient->clearFrames();
EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
}
@@ -616,20 +627,20 @@
mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
V2_1::IComposerCallback::Connection::CONNECTED);
- waitForDisplayTransaction();
+ waitForDisplayTransaction(EXTERNAL_DISPLAY);
EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
EXPECT_FALSE(display == nullptr);
- DisplayConfig config;
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
- EXPECT_EQ(ui::Size(800, 1600), config.resolution);
- EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
+ ui::DisplayMode mode;
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+ EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
+ EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate);
mFakeComposerClient->clearFrames();
{
- const ui::Size& resolution = config.resolution;
+ const ui::Size& resolution = mode.resolution;
auto surfaceControl =
mComposerClient->createSurface(String8("Display Test Surface Foo"),
resolution.getWidth(), resolution.getHeight(),
@@ -646,11 +657,12 @@
}
}
- Vector<DisplayConfig> configs;
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
- EXPECT_EQ(configs.size(), 4);
+ ui::DynamicDisplayInfo info;
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info));
+ const auto& modes = info.supportedDisplayModes;
+ EXPECT_EQ(modes.size(), 4);
- // change active config to 800x1600@90Hz
+ // change active mode to 800x1600@90Hz
if (mIs2_4Client) {
EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 3, _, _))
.WillOnce(Return(V2_4::Error::NONE));
@@ -659,28 +671,28 @@
.WillOnce(Return(V2_1::Error::NONE));
}
- for (int i = 0; i < configs.size(); i++) {
- const auto& config = configs[i];
- if (config.resolution.getWidth() == 800 && config.refreshRate == 1e9f / 11'111'111) {
+ for (size_t i = 0; i < modes.size(); i++) {
+ const auto& mode = modes[i];
+ if (mode.resolution.getWidth() == 800 && mode.refreshRate == 1e9f / 11'111'111) {
EXPECT_EQ(NO_ERROR,
- SurfaceComposerClient::
- setDesiredDisplayConfigSpecs(display, i, configs[i].refreshRate,
- configs[i].refreshRate,
- configs[i].refreshRate,
- configs[i].refreshRate));
- waitForDisplayTransaction();
- EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
+ SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false,
+ modes[i].refreshRate,
+ modes[i].refreshRate,
+ modes[i].refreshRate,
+ modes[i].refreshRate));
+ waitForDisplayTransaction(EXTERNAL_DISPLAY);
+ EXPECT_TRUE(waitForModeChangedEvent(EXTERNAL_DISPLAY, i));
break;
}
}
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
- EXPECT_EQ(ui::Size(800, 1600), config.resolution);
- EXPECT_EQ(1e9f / 11'111'111, config.refreshRate);
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+ EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
+ EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate);
mFakeComposerClient->clearFrames();
{
- const ui::Size& resolution = config.resolution;
+ const ui::Size& resolution = mode.resolution;
auto surfaceControl =
mComposerClient->createSurface(String8("Display Test Surface Foo"),
resolution.getWidth(), resolution.getHeight(),
@@ -697,7 +709,7 @@
}
}
- // change active config to 1600x3200@120Hz
+ // change active mode to 1600x3200@120Hz
if (mIs2_4Client) {
EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 4, _, _))
.WillOnce(Return(V2_4::Error::NONE));
@@ -706,28 +718,28 @@
.WillOnce(Return(V2_1::Error::NONE));
}
- for (int i = 0; i < configs.size(); i++) {
- const auto& config = configs[i];
- if (config.refreshRate == 1e9f / 8'333'333) {
+ for (int i = 0; i < modes.size(); i++) {
+ const auto& mode = modes[i];
+ if (mode.refreshRate == 1e9f / 8'333'333) {
EXPECT_EQ(NO_ERROR,
- SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
- config.refreshRate,
- config.refreshRate,
- config.refreshRate,
- config.refreshRate));
- waitForDisplayTransaction();
- EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
+ SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false,
+ mode.refreshRate,
+ mode.refreshRate,
+ mode.refreshRate,
+ mode.refreshRate));
+ waitForDisplayTransaction(EXTERNAL_DISPLAY);
+ EXPECT_TRUE(waitForModeChangedEvent(EXTERNAL_DISPLAY, i));
break;
}
}
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
- EXPECT_EQ(ui::Size(1600, 3200), config.resolution);
- EXPECT_EQ(1e9f / 8'333'333, config.refreshRate);
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+ EXPECT_EQ(ui::Size(1600, 3200), mode.resolution);
+ EXPECT_EQ(1e9f / 8'333'333, mode.refreshRate);
mFakeComposerClient->clearFrames();
{
- const ui::Size& resolution = config.resolution;
+ const ui::Size& resolution = mode.resolution;
auto surfaceControl =
mComposerClient->createSurface(String8("Display Test Surface Foo"),
resolution.getWidth(), resolution.getHeight(),
@@ -744,7 +756,7 @@
}
}
- // change active config to 1600x3200@90Hz
+ // change active mode to 1600x3200@90Hz
if (mIs2_4Client) {
EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 5, _, _))
.WillOnce(Return(V2_4::Error::NONE));
@@ -753,28 +765,28 @@
.WillOnce(Return(V2_1::Error::NONE));
}
- for (int i = 0; i < configs.size(); i++) {
- const auto& config = configs[i];
- if (config.resolution.getWidth() == 1600 && config.refreshRate == 1e9f / 11'111'111) {
+ for (int i = 0; i < modes.size(); i++) {
+ const auto& mode = modes[i];
+ if (mode.resolution.getWidth() == 1600 && mode.refreshRate == 1e9f / 11'111'111) {
EXPECT_EQ(NO_ERROR,
- SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
- config.refreshRate,
- config.refreshRate,
- config.refreshRate,
- config.refreshRate));
- waitForDisplayTransaction();
- EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
+ SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false,
+ mode.refreshRate,
+ mode.refreshRate,
+ mode.refreshRate,
+ mode.refreshRate));
+ waitForDisplayTransaction(EXTERNAL_DISPLAY);
+ EXPECT_TRUE(waitForModeChangedEvent(EXTERNAL_DISPLAY, i));
break;
}
}
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
- EXPECT_EQ(ui::Size(1600, 3200), config.resolution);
- EXPECT_EQ(1e9f / 11'111'111, config.refreshRate);
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+ EXPECT_EQ(ui::Size(1600, 3200), mode.resolution);
+ EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate);
mFakeComposerClient->clearFrames();
{
- const ui::Size& resolution = config.resolution;
+ const ui::Size& resolution = mode.resolution;
auto surfaceControl =
mComposerClient->createSurface(String8("Display Test Surface Foo"),
resolution.getWidth(), resolution.getHeight(),
@@ -793,7 +805,7 @@
mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
V2_1::IComposerCallback::Connection::DISCONNECTED);
- waitForDisplayTransaction();
+ waitForDisplayTransaction(EXTERNAL_DISPLAY);
mFakeComposerClient->clearFrames();
EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
}
@@ -804,15 +816,15 @@
mFakeComposerClient->hotplugDisplay(PRIMARY_DISPLAY,
V2_1::IComposerCallback::Connection::DISCONNECTED);
- waitForDisplayTransaction();
+ waitForDisplayTransaction(PRIMARY_DISPLAY);
EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, false));
{
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kPrimaryDisplayId);
EXPECT_TRUE(display == nullptr);
- DisplayConfig config;
- auto result = SurfaceComposerClient::getActiveDisplayConfig(display, &config);
+ ui::DisplayMode mode;
+ auto result = SurfaceComposerClient::getActiveDisplayMode(display, &mode);
EXPECT_NE(NO_ERROR, result);
}
@@ -829,19 +841,136 @@
mFakeComposerClient->hotplugDisplay(PRIMARY_DISPLAY,
V2_1::IComposerCallback::Connection::CONNECTED);
- waitForDisplayTransaction();
+ waitForDisplayTransaction(PRIMARY_DISPLAY);
EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, true));
{
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kPrimaryDisplayId);
EXPECT_FALSE(display == nullptr);
- DisplayConfig config;
- auto result = SurfaceComposerClient::getActiveDisplayConfig(display, &config);
+ ui::DisplayMode mode;
+ auto result = SurfaceComposerClient::getActiveDisplayMode(display, &mode);
EXPECT_EQ(NO_ERROR, result);
- ASSERT_EQ(ui::Size(400, 200), config.resolution);
- EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
+ ASSERT_EQ(ui::Size(400, 200), mode.resolution);
+ EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate);
+ }
+ }
+
+ void Test_SubsequentHotplugConnectUpdatesDisplay(Display hwcDisplayId) {
+ ALOGD("DisplayTest::Test_SubsequentHotplugConnectUpdatesDisplay");
+
+ // Send a hotplug connected event to set up the initial display modes.
+ // The primary display is already connected so this will update it.
+ // If we're running the test of an external display this will create it.
+ setExpectationsForConfigs(hwcDisplayId,
+ {{.id = 1,
+ .w = 800,
+ .h = 1600,
+ .vsyncPeriod = 11'111'111,
+ .group = 1}},
+ /* activeConfig */ 1, 11'111'111);
+
+ mFakeComposerClient->hotplugDisplay(hwcDisplayId,
+ V2_1::IComposerCallback::Connection::CONNECTED);
+ waitForDisplayTransaction(hwcDisplayId);
+ EXPECT_TRUE(waitForHotplugEvent(hwcDisplayId, true));
+
+ const auto displayId = physicalIdFromHwcDisplayId(hwcDisplayId);
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(displayId);
+ EXPECT_FALSE(display == nullptr);
+
+ // Verify that the active mode and the supported moded are updated
+ {
+ ui::DisplayMode mode;
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+ EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
+ EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate);
+
+ ui::DynamicDisplayInfo info;
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info));
+ const auto& modes = info.supportedDisplayModes;
+ EXPECT_EQ(modes.size(), 1);
+ }
+
+ // Send another hotplug connected event
+ setExpectationsForConfigs(hwcDisplayId,
+ {
+ {.id = 1,
+ .w = 800,
+ .h = 1600,
+ .vsyncPeriod = 16'666'666,
+ .group = 1},
+ {.id = 2,
+ .w = 800,
+ .h = 1600,
+ .vsyncPeriod = 11'111'111,
+ .group = 1},
+ {.id = 3,
+ .w = 800,
+ .h = 1600,
+ .vsyncPeriod = 8'333'333,
+ .group = 1},
+ },
+ /* activeConfig */ 1, 16'666'666);
+
+ mFakeComposerClient->hotplugDisplay(hwcDisplayId,
+ V2_1::IComposerCallback::Connection::CONNECTED);
+ waitForDisplayTransaction(hwcDisplayId);
+ EXPECT_TRUE(waitForHotplugEvent(hwcDisplayId, true));
+
+ // Verify that the active mode and the supported moded are updated
+ {
+ ui::DisplayMode mode;
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+ EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
+ EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate);
+ }
+
+ ui::DynamicDisplayInfo info;
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info));
+ const auto& modes = info.supportedDisplayModes;
+ EXPECT_EQ(modes.size(), 3);
+
+ EXPECT_EQ(ui::Size(800, 1600), modes[0].resolution);
+ EXPECT_EQ(1e9f / 16'666'666, modes[0].refreshRate);
+
+ EXPECT_EQ(ui::Size(800, 1600), modes[1].resolution);
+ EXPECT_EQ(1e9f / 11'111'111, modes[1].refreshRate);
+
+ EXPECT_EQ(ui::Size(800, 1600), modes[2].resolution);
+ EXPECT_EQ(1e9f / 8'333'333, modes[2].refreshRate);
+
+ // Verify that we are able to switch to any of the modes
+ for (int i = modes.size() - 1; i >= 0; i--) {
+ const auto hwcId = i + 1;
+ // Set up HWC expectations for the mode change
+ if (mIs2_4Client) {
+ EXPECT_CALL(*mMockComposer,
+ setActiveConfigWithConstraints(hwcDisplayId, hwcId, _, _))
+ .WillOnce(Return(V2_4::Error::NONE));
+ } else {
+ EXPECT_CALL(*mMockComposer, setActiveConfig(hwcDisplayId, hwcId))
+ .WillOnce(Return(V2_1::Error::NONE));
+ }
+
+ EXPECT_EQ(NO_ERROR,
+ SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false,
+ modes[i].refreshRate,
+ modes[i].refreshRate,
+ modes[i].refreshRate,
+ modes[i].refreshRate));
+ // We need to refresh twice - once to apply the pending mode change request,
+ // and once to process the change.
+ waitForDisplayTransaction(hwcDisplayId);
+ waitForDisplayTransaction(hwcDisplayId);
+ EXPECT_TRUE(waitForModeChangedEvent(hwcDisplayId, i))
+ << "Failure while switching to mode " << i;
+
+ ui::DisplayMode mode;
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+ EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
+ EXPECT_EQ(modes[i].refreshRate, mode.refreshRate);
}
}
@@ -861,6 +990,25 @@
using DisplayTest_2_1 = DisplayTest<FakeComposerService_2_1>;
+// Tests that VSYNC injection can be safely toggled while invalidating.
+TEST_F(DisplayTest_2_1, VsyncInjection) {
+ const auto flinger = ComposerService::getComposerService();
+ bool enable = true;
+
+ for (int i = 0; i < 100; i++) {
+ flinger->enableVSyncInjections(enable);
+ enable = !enable;
+
+ constexpr uint32_t kForceInvalidate = 1004;
+ android::Parcel data, reply;
+ data.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
+ EXPECT_EQ(NO_ERROR,
+ android::IInterface::asBinder(flinger)->transact(kForceInvalidate, data, &reply));
+
+ std::this_thread::sleep_for(5ms);
+ }
+}
+
TEST_F(DisplayTest_2_1, HotplugOneConfig) {
Test_HotplugOneConfig();
}
@@ -881,6 +1029,14 @@
Test_HotplugPrimaryDisplay();
}
+TEST_F(DisplayTest_2_1, SubsequentHotplugConnectUpdatesPrimaryDisplay) {
+ Test_SubsequentHotplugConnectUpdatesDisplay(PRIMARY_DISPLAY);
+}
+
+TEST_F(DisplayTest_2_1, SubsequentHotplugConnectUpdatesExternalDisplay) {
+ Test_SubsequentHotplugConnectUpdatesDisplay(EXTERNAL_DISPLAY);
+}
+
using DisplayTest_2_2 = DisplayTest<FakeComposerService_2_2>;
TEST_F(DisplayTest_2_2, HotplugOneConfig) {
@@ -903,6 +1059,14 @@
Test_HotplugPrimaryDisplay();
}
+TEST_F(DisplayTest_2_2, SubsequentHotplugConnectUpdatesPrimaryDisplay) {
+ Test_SubsequentHotplugConnectUpdatesDisplay(PRIMARY_DISPLAY);
+}
+
+TEST_F(DisplayTest_2_2, SubsequentHotplugConnectUpdatesExternalDisplay) {
+ Test_SubsequentHotplugConnectUpdatesDisplay(EXTERNAL_DISPLAY);
+}
+
using DisplayTest_2_3 = DisplayTest<FakeComposerService_2_3>;
TEST_F(DisplayTest_2_3, HotplugOneConfig) {
@@ -925,6 +1089,14 @@
Test_HotplugPrimaryDisplay();
}
+TEST_F(DisplayTest_2_3, SubsequentHotplugConnectUpdatesPrimaryDisplay) {
+ Test_SubsequentHotplugConnectUpdatesDisplay(PRIMARY_DISPLAY);
+}
+
+TEST_F(DisplayTest_2_3, SubsequentHotplugConnectUpdatesExternalDisplay) {
+ Test_SubsequentHotplugConnectUpdatesDisplay(EXTERNAL_DISPLAY);
+}
+
using DisplayTest_2_4 = DisplayTest<FakeComposerService_2_4>;
TEST_F(DisplayTest_2_4, HotplugOneConfig) {
@@ -947,6 +1119,14 @@
Test_HotplugPrimaryDisplay();
}
+TEST_F(DisplayTest_2_4, SubsequentHotplugConnectUpdatesPrimaryDisplay) {
+ Test_SubsequentHotplugConnectUpdatesDisplay(PRIMARY_DISPLAY);
+}
+
+TEST_F(DisplayTest_2_4, SubsequentHotplugConnectUpdatesExternalDisplay) {
+ Test_SubsequentHotplugConnectUpdatesDisplay(EXTERNAL_DISPLAY);
+}
+
////////////////////////////////////////////////
template <typename FakeComposerService>
@@ -988,13 +1168,13 @@
ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
ALOGI("TransactionTest::SetUp - display");
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kPrimaryDisplayId);
ASSERT_FALSE(display == nullptr);
- DisplayConfig config;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+ ui::DisplayMode mode;
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
- const ui::Size& resolution = config.resolution;
+ const ui::Size& resolution = mode.resolution;
mDisplayWidth = resolution.getWidth();
mDisplayHeight = resolution.getHeight();
@@ -1113,37 +1293,12 @@
EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2)));
}
- void Test_LayerResize() {
- ALOGD("TransactionTest::LayerResize");
- {
- TransactionScope ts(*sFakeComposer);
- ts.setSize(mFGSurfaceControl, 128, 128);
- }
-
- fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
- sFakeComposer->runVSyncAndWait();
-
- ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and
- // there's no extra frames.
-
- auto frame1Ref = mBaseFrame;
- // NOTE: The resize should not be visible for frame 1 as there's no buffer with new size
- // posted.
- EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1)));
-
- auto frame2Ref = frame1Ref;
- frame2Ref[FG_LAYER].mSwapCount++;
- frame2Ref[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 128, 64 + 128};
- frame2Ref[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 128.f, 128.f};
- EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2)));
- }
-
void Test_LayerCrop() {
// TODO: Add scaling to confirm that crop happens in buffer space?
{
TransactionScope ts(*sFakeComposer);
Rect cropRect(16, 16, 32, 32);
- ts.setCrop_legacy(mFGSurfaceControl, cropRect);
+ ts.setCrop(mFGSurfaceControl, cropRect);
}
ASSERT_EQ(2, sFakeComposer->getFrameCount());
@@ -1288,77 +1443,6 @@
}
}
- void Test_DeferredTransaction() {
- // Synchronization surface
- constexpr static int SYNC_LAYER = 2;
- auto syncSurfaceControl = mComposerClient->createSurface(String8("Sync Test Surface"), 1, 1,
- PIXEL_FORMAT_RGBA_8888, 0);
- ASSERT_TRUE(syncSurfaceControl != nullptr);
- ASSERT_TRUE(syncSurfaceControl->isValid());
-
- fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
-
- {
- TransactionScope ts(*sFakeComposer);
- ts.setLayer(syncSurfaceControl, INT32_MAX - 1);
- ts.setPosition(syncSurfaceControl, mDisplayWidth - 2, mDisplayHeight - 2);
- ts.show(syncSurfaceControl);
- }
- auto referenceFrame = mBaseFrame;
- referenceFrame.push_back(makeSimpleRect(mDisplayWidth - 2, mDisplayHeight - 2,
- mDisplayWidth - 1, mDisplayHeight - 1));
- referenceFrame[SYNC_LAYER].mSwapCount = 1;
- EXPECT_EQ(2, sFakeComposer->getFrameCount());
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
- // set up two deferred transactions on different frames - these should not yield composited
- // frames
- {
- TransactionScope ts(*sFakeComposer);
- ts.setAlpha(mFGSurfaceControl, 0.75);
- ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl->getHandle(),
- syncSurfaceControl->getSurface()->getNextFrameNumber());
- }
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
- {
- TransactionScope ts(*sFakeComposer);
- ts.setPosition(mFGSurfaceControl, 128, 128);
- ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl->getHandle(),
- syncSurfaceControl->getSurface()->getNextFrameNumber() +
- 1);
- }
- EXPECT_EQ(4, sFakeComposer->getFrameCount());
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
- // should trigger the first deferred transaction, but not the second one
- fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
- sFakeComposer->runVSyncAndWait();
- EXPECT_EQ(5, sFakeComposer->getFrameCount());
-
- referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f;
- referenceFrame[SYNC_LAYER].mSwapCount++;
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
- // should show up immediately since it's not deferred
- {
- TransactionScope ts(*sFakeComposer);
- ts.setAlpha(mFGSurfaceControl, 1.0);
- }
- referenceFrame[FG_LAYER].mPlaneAlpha = 1.f;
- EXPECT_EQ(6, sFakeComposer->getFrameCount());
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
- // trigger the second deferred transaction
- fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
- sFakeComposer->runVSyncAndWait();
- // TODO: Compute from layer size?
- referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{128, 128, 128 + 64, 128 + 64};
- referenceFrame[SYNC_LAYER].mSwapCount++;
- EXPECT_EQ(7, sFakeComposer->getFrameCount());
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
- }
-
void Test_SetRelativeLayer() {
constexpr int RELATIVE_LAYER = 2;
auto relativeSurfaceControl = mComposerClient->createSurface(String8("Test Surface"), 64,
@@ -1370,7 +1454,7 @@
TransactionScope ts(*sFakeComposer);
ts.setPosition(relativeSurfaceControl, 64, 64);
ts.show(relativeSurfaceControl);
- ts.setRelativeLayer(relativeSurfaceControl, mFGSurfaceControl->getHandle(), 1);
+ ts.setRelativeLayer(relativeSurfaceControl, mFGSurfaceControl, 1);
}
auto referenceFrame = mBaseFrame;
// NOTE: All three layers will be visible as the surfaces are
@@ -1408,10 +1492,6 @@
Test_LayerMove();
}
-TEST_F(TransactionTest_2_1, DISABLED_LayerResize) {
- Test_LayerResize();
-}
-
TEST_F(TransactionTest_2_1, DISABLED_LayerCrop) {
Test_LayerCrop();
}
@@ -1444,10 +1524,6 @@
Test_LayerSetMatrix();
}
-TEST_F(TransactionTest_2_1, DISABLED_DeferredTransaction) {
- Test_DeferredTransaction();
-}
-
TEST_F(TransactionTest_2_1, DISABLED_SetRelativeLayer) {
Test_SetRelativeLayer();
}
@@ -1463,7 +1539,7 @@
Base::SetUp();
mChild = Base::mComposerClient->createSurface(String8("Child surface"), 10, 10,
PIXEL_FORMAT_RGBA_8888, 0,
- Base::mFGSurfaceControl.get());
+ Base::mFGSurfaceControl->getHandle());
fillSurfaceRGBA8(mChild, LIGHT_GRAY);
Base::sFakeComposer->runVSyncAndWait();
@@ -1511,7 +1587,7 @@
ts.show(mChild);
ts.setPosition(mChild, 0, 0);
ts.setPosition(Base::mFGSurfaceControl, 0, 0);
- ts.setCrop_legacy(Base::mFGSurfaceControl, Rect(0, 0, 5, 5));
+ ts.setCrop(Base::mFGSurfaceControl, Rect(0, 0, 5, 5));
}
// NOTE: The foreground surface would be occluded by the child
// now, but is included in the stack because the child is
@@ -1585,201 +1661,6 @@
EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
}
- void Test_ReparentChildren() {
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.show(mChild);
- ts.setPosition(mChild, 10, 10);
- ts.setPosition(Base::mFGSurfaceControl, 64, 64);
- }
- auto referenceFrame = Base::mBaseFrame;
- referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
- referenceFrame[CHILD_LAYER].mDisplayFrame =
- hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
- EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
-
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.reparentChildren(Base::mFGSurfaceControl, Base::mBGSurfaceControl->getHandle());
- }
-
- auto referenceFrame2 = referenceFrame;
- referenceFrame2[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
- referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{10, 10, 10 + 10, 10 + 10};
- EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
- }
-
- void Test_DetachChildrenSameClient() {
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.show(mChild);
- ts.setPosition(mChild, 10, 10);
- ts.setPosition(Base::mFGSurfaceControl, 64, 64);
- }
-
- auto referenceFrame = Base::mBaseFrame;
- referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
- referenceFrame[CHILD_LAYER].mDisplayFrame =
- hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
- EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
-
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.setPosition(Base::mFGSurfaceControl, 0, 0);
- ts.detachChildren(Base::mFGSurfaceControl);
- }
-
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.setPosition(Base::mFGSurfaceControl, 64, 64);
- ts.hide(mChild);
- }
-
- std::vector<RenderState> refFrame(2);
- refFrame[Base::BG_LAYER] = Base::mBaseFrame[Base::BG_LAYER];
- refFrame[Base::FG_LAYER] = Base::mBaseFrame[Base::FG_LAYER];
-
- EXPECT_TRUE(framesAreSame(refFrame, Base::sFakeComposer->getLatestFrame()));
- }
-
- void Test_DetachChildrenDifferentClient() {
- sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
- sp<SurfaceControl> childNewClient =
- newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0,
- Base::mFGSurfaceControl.get());
- ASSERT_TRUE(childNewClient != nullptr);
- ASSERT_TRUE(childNewClient->isValid());
- fillSurfaceRGBA8(childNewClient, LIGHT_GRAY);
-
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.hide(mChild);
- ts.show(childNewClient);
- ts.setPosition(childNewClient, 10, 10);
- ts.setPosition(Base::mFGSurfaceControl, 64, 64);
- }
-
- auto referenceFrame = Base::mBaseFrame;
- referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
- referenceFrame[CHILD_LAYER].mDisplayFrame =
- hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
- EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
-
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.detachChildren(Base::mFGSurfaceControl);
- ts.setPosition(Base::mFGSurfaceControl, 0, 0);
- }
-
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.setPosition(Base::mFGSurfaceControl, 64, 64);
- ts.setPosition(childNewClient, 0, 0);
- ts.hide(childNewClient);
- }
-
- // Nothing should have changed. The child control becomes a no-op
- // zombie on detach. See comments for detachChildren in the
- // SurfaceControl.h file.
- EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
- }
-
- void Test_InheritNonTransformScalingFromParent() {
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.show(mChild);
- ts.setPosition(mChild, 0, 0);
- ts.setPosition(Base::mFGSurfaceControl, 0, 0);
- }
-
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.setOverrideScalingMode(Base::mFGSurfaceControl,
- NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
- // We cause scaling by 2.
- ts.setSize(Base::mFGSurfaceControl, 128, 128);
- }
-
- auto referenceFrame = Base::mBaseFrame;
- referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128};
- referenceFrame[Base::FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 64.f};
- referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20};
- referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 10.f, 10.f};
- EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
- }
-
- // Regression test for b/37673612
- void Test_ChildrenWithParentBufferTransform() {
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.show(mChild);
- ts.setPosition(mChild, 0, 0);
- ts.setPosition(Base::mFGSurfaceControl, 0, 0);
- }
-
- // We set things up as in b/37673612 so that there is a mismatch between the buffer size and
- // the WM specified state size.
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.setSize(Base::mFGSurfaceControl, 128, 64);
- }
-
- sp<Surface> s = Base::mFGSurfaceControl->getSurface();
- auto anw = static_cast<ANativeWindow*>(s.get());
- native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
- native_window_set_buffers_dimensions(anw, 64, 128);
- fillSurfaceRGBA8(Base::mFGSurfaceControl, RED);
- Base::sFakeComposer->runVSyncAndWait();
-
- // The child should still be in the same place and not have any strange scaling as in
- // b/37673612.
- auto referenceFrame = Base::mBaseFrame;
- referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 64};
- referenceFrame[Base::FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 128.f};
- referenceFrame[Base::FG_LAYER].mSwapCount++;
- referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
- EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
- }
-
- void Test_Bug36858924() {
- // Destroy the child layer
- mChild.clear();
-
- // Now recreate it as hidden
- mChild = Base::mComposerClient->createSurface(String8("Child surface"), 10, 10,
- PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eHidden,
- Base::mFGSurfaceControl.get());
-
- // Show the child layer in a deferred transaction
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.deferTransactionUntil_legacy(mChild, Base::mFGSurfaceControl->getHandle(),
- Base::mFGSurfaceControl->getSurface()
- ->getNextFrameNumber());
- ts.show(mChild);
- }
-
- // Render the foreground surface a few times
- //
- // Prior to the bugfix for b/36858924, this would usually hang while trying to fill the
- // third frame because SurfaceFlinger would never process the deferred transaction and would
- // therefore never acquire/release the first buffer
- ALOGI("Filling 1");
- fillSurfaceRGBA8(Base::mFGSurfaceControl, GREEN);
- Base::sFakeComposer->runVSyncAndWait();
- ALOGI("Filling 2");
- fillSurfaceRGBA8(Base::mFGSurfaceControl, BLUE);
- Base::sFakeComposer->runVSyncAndWait();
- ALOGI("Filling 3");
- fillSurfaceRGBA8(Base::mFGSurfaceControl, RED);
- Base::sFakeComposer->runVSyncAndWait();
- ALOGI("Filling 4");
- fillSurfaceRGBA8(Base::mFGSurfaceControl, GREEN);
- Base::sFakeComposer->runVSyncAndWait();
- }
-
sp<SurfaceControl> mChild;
};
@@ -1805,31 +1686,6 @@
Test_LayerAlpha();
}
-TEST_F(ChildLayerTest_2_1, DISABLED_ReparentChildren) {
- Test_ReparentChildren();
-}
-
-TEST_F(ChildLayerTest_2_1, DISABLED_DetachChildrenSameClient) {
- Test_DetachChildrenSameClient();
-}
-
-TEST_F(ChildLayerTest_2_1, DISABLED_DetachChildrenDifferentClient) {
- Test_DetachChildrenDifferentClient();
-}
-
-TEST_F(ChildLayerTest_2_1, DISABLED_InheritNonTransformScalingFromParent) {
- Test_InheritNonTransformScalingFromParent();
-}
-
-// Regression test for b/37673612
-TEST_F(ChildLayerTest_2_1, DISABLED_ChildrenWithParentBufferTransform) {
- Test_ChildrenWithParentBufferTransform();
-}
-
-TEST_F(ChildLayerTest_2_1, DISABLED_Bug36858924) {
- Test_Bug36858924();
-}
-
template <typename FakeComposerService>
class ChildColorLayerTest : public ChildLayerTest<FakeComposerService> {
using Base = ChildLayerTest<FakeComposerService>;
@@ -1841,12 +1697,12 @@
Base::mComposerClient->createSurface(String8("Child surface"), 0, 0,
PIXEL_FORMAT_RGBA_8888,
ISurfaceComposerClient::eFXSurfaceEffect,
- Base::mFGSurfaceControl.get());
+ Base::mFGSurfaceControl->getHandle());
{
TransactionScope ts(*Base::sFakeComposer);
ts.setColor(Base::mChild,
{LIGHT_GRAY.r / 255.0f, LIGHT_GRAY.g / 255.0f, LIGHT_GRAY.b / 255.0f});
- ts.setCrop_legacy(Base::mChild, Rect(0, 0, 10, 10));
+ ts.setCrop(Base::mChild, Rect(0, 0, 10, 10));
}
Base::sFakeComposer->runVSyncAndWait();
@@ -1919,91 +1775,6 @@
TEST_F(ChildColorLayerTest_2_1, DISABLED_LayerZeroAlpha) {
Test_LayerZeroAlpha();
}
-
-template <typename FakeComposerService>
-class LatchingTest : public TransactionTest<FakeComposerService> {
- using Base = TransactionTest<FakeComposerService>;
-
-protected:
- void lockAndFillFGBuffer() { fillSurfaceRGBA8(Base::mFGSurfaceControl, RED, false); }
-
- void unlockFGBuffer() {
- sp<Surface> s = Base::mFGSurfaceControl->getSurface();
- ASSERT_EQ(NO_ERROR, s->unlockAndPost());
- Base::sFakeComposer->runVSyncAndWait();
- }
-
- void completeFGResize() {
- fillSurfaceRGBA8(Base::mFGSurfaceControl, RED);
- Base::sFakeComposer->runVSyncAndWait();
- }
- void restoreInitialState() {
- TransactionScope ts(*Base::sFakeComposer);
- ts.setSize(Base::mFGSurfaceControl, 64, 64);
- ts.setPosition(Base::mFGSurfaceControl, 64, 64);
- ts.setCrop_legacy(Base::mFGSurfaceControl, Rect(0, 0, 64, 64));
- }
-
- void Test_SurfacePositionLatching() {
- // By default position can be updated even while
- // a resize is pending.
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.setSize(Base::mFGSurfaceControl, 32, 32);
- ts.setPosition(Base::mFGSurfaceControl, 100, 100);
- }
-
- // The size should not have updated as we have not provided a new buffer.
- auto referenceFrame1 = Base::mBaseFrame;
- referenceFrame1[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{100, 100, 100 + 64, 100 + 64};
- EXPECT_TRUE(framesAreSame(referenceFrame1, Base::sFakeComposer->getLatestFrame()));
-
- restoreInitialState();
-
- completeFGResize();
-
- auto referenceFrame2 = Base::mBaseFrame;
- referenceFrame2[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{100, 100, 100 + 32, 100 + 32};
- referenceFrame2[Base::FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 32.f, 32.f};
- referenceFrame2[Base::FG_LAYER].mSwapCount++;
- EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
- }
-
- void Test_CropLatching() {
- // Normally the crop applies immediately even while a resize is pending.
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.setSize(Base::mFGSurfaceControl, 128, 128);
- ts.setCrop_legacy(Base::mFGSurfaceControl, Rect(0, 0, 63, 63));
- }
-
- auto referenceFrame1 = Base::mBaseFrame;
- referenceFrame1[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 63, 64 + 63};
- referenceFrame1[Base::FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 63.f, 63.f};
- EXPECT_TRUE(framesAreSame(referenceFrame1, Base::sFakeComposer->getLatestFrame()));
-
- restoreInitialState();
-
- completeFGResize();
-
- auto referenceFrame2 = Base::mBaseFrame;
- referenceFrame2[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 63, 64 + 63};
- referenceFrame2[Base::FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 63.f, 63.f};
- referenceFrame2[Base::FG_LAYER].mSwapCount++;
- EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
- }
-};
-
-using LatchingTest_2_1 = LatchingTest<FakeComposerService_2_1>;
-
-TEST_F(LatchingTest_2_1, DISABLED_SurfacePositionLatching) {
- Test_SurfacePositionLatching();
-}
-
-TEST_F(LatchingTest_2_1, DISABLED_CropLatching) {
- Test_CropLatching();
-}
-
} // namespace
int main(int argc, char** argv) {
@@ -2016,4 +1787,4 @@
}
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index e00c3da..88fb811 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -23,7 +23,7 @@
cc_test {
name: "libsurfaceflinger_unittest",
- defaults: ["libsurfaceflinger_defaults"],
+ defaults: ["surfaceflinger_defaults"],
test_suites: ["device-tests"],
sanitize: {
// Using the address sanitizer not only helps uncover issues in the code
@@ -48,16 +48,31 @@
"CompositionTest.cpp",
"DispSyncSourceTest.cpp",
"DisplayIdentificationTest.cpp",
+ "DisplayIdGeneratorTest.cpp",
"DisplayTransactionTest.cpp",
- "EventControlThreadTest.cpp",
+ "DisplayDevice_GetBestColorModeTest.cpp",
+ "DisplayDevice_SetProjectionTest.cpp",
"EventThreadTest.cpp",
+ "FpsReporterTest.cpp",
+ "FpsTest.cpp",
+ "FramebufferSurfaceTest.cpp",
+ "FrameTimelineTest.cpp",
"HWComposerTest.cpp",
"OneShotTimerTest.cpp",
"LayerHistoryTest.cpp",
- "LayerHistoryTestV2.cpp",
+ "LayerInfoTest.cpp",
"LayerMetadataTest.cpp",
- "PhaseOffsetsTest.cpp",
- "PromiseTest.cpp",
+ "MessageQueueTest.cpp",
+ "SurfaceFlinger_CreateDisplayTest.cpp",
+ "SurfaceFlinger_DestroyDisplayTest.cpp",
+ "SurfaceFlinger_GetDisplayNativePrimariesTest.cpp",
+ "SurfaceFlinger_HandleTransactionLockedTest.cpp",
+ "SurfaceFlinger_NotifyPowerBoostTest.cpp",
+ "SurfaceFlinger_OnHotplugReceivedTest.cpp",
+ "SurfaceFlinger_OnInitializeDisplaysTest.cpp",
+ "SurfaceFlinger_SetDisplayStateTest.cpp",
+ "SurfaceFlinger_SetPowerModeInternalTest.cpp",
+ "SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp",
"SchedulerTest.cpp",
"SchedulerUtilsTest.cpp",
"SetFrameRateTest.cpp",
@@ -69,42 +84,85 @@
"FrameTracerTest.cpp",
"TimerTest.cpp",
"TransactionApplicationTest.cpp",
+ "TransactionFrameTracerTest.cpp",
+ "TransactionSurfaceFrameTest.cpp",
"StrongTypingTest.cpp",
"VSyncDispatchTimerQueueTest.cpp",
"VSyncDispatchRealtimeTest.cpp",
- "VSyncModulatorTest.cpp",
+ "VsyncModulatorTest.cpp",
"VSyncPredictorTest.cpp",
"VSyncReactorTest.cpp",
+ "VsyncConfigurationTest.cpp",
"mock/DisplayHardware/MockComposer.cpp",
- "mock/DisplayHardware/MockDisplay.cpp",
"mock/DisplayHardware/MockPowerAdvisor.cpp",
- "mock/MockDispSync.cpp",
- "mock/MockEventControlThread.cpp",
"mock/MockEventThread.cpp",
+ "mock/MockFrameTimeline.cpp",
+ "mock/MockFrameTracer.cpp",
"mock/MockMessageQueue.cpp",
"mock/MockNativeWindowSurface.cpp",
"mock/MockSurfaceInterceptor.cpp",
"mock/MockTimeStats.cpp",
- "mock/MockFrameTracer.cpp",
+ "mock/MockVsyncController.cpp",
+ "mock/MockVSyncTracker.cpp",
"mock/system/window/MockNativeWindow.cpp",
],
static_libs: [
- "libgmock",
- "libcompositionengine",
+ "android.hardware.graphics.composer@2.1",
+ "android.hardware.graphics.composer@2.2",
+ "android.hardware.graphics.composer@2.3",
+ "android.hardware.graphics.composer@2.4",
+ "android.hardware.power@1.0",
+ "android.hardware.power@1.1",
+ "android.hardware.power@1.2",
+ "android.hardware.power@1.3",
+ "android.hardware.power-V1-cpp",
"libcompositionengine_mocks",
+ "libcompositionengine",
+ "libframetimeline",
+ "libgmock",
"libgui_mocks",
+ "liblayers_proto",
"libperfetto_client_experimental",
"librenderengine_mocks",
+ "librenderengine",
+ "libserviceutils",
+ "libtimestats",
+ "libtimestats_atoms_proto",
+ "libtimestats_proto",
+ "libtrace_proto",
"perfetto_trace_protos",
],
shared_libs: [
- "libprotoutil",
- "libstatssocket",
- "libsurfaceflinger",
- "libtimestats",
- "libtimestats_proto",
+ "android.hardware.configstore-utils",
+ "android.hardware.configstore@1.0",
+ "android.hardware.configstore@1.1",
+ "android.hardware.graphics.allocator@2.0",
+ "android.hardware.graphics.allocator@3.0",
+ "android.hardware.graphics.common@1.2",
+ "libbase",
+ "libbinder",
+ "libcutils",
+ "libEGL",
+ "libfmq",
+ "libGLESv1_CM",
+ "libGLESv2",
+ "libgui",
+ "libhidlbase",
+ "libinput",
+ "liblog",
+ "libnativewindow",
+ "libprocessgroup",
+ "libprotobuf-cpp-lite",
+ "libSurfaceFlingerProp",
+ "libsync",
+ "libui",
+ "libutils",
],
header_libs: [
+ "android.hardware.graphics.composer@2.1-command-buffer",
+ "android.hardware.graphics.composer@2.2-command-buffer",
+ "android.hardware.graphics.composer@2.3-command-buffer",
+ "android.hardware.graphics.composer@2.4-command-buffer",
"libsurfaceflinger_headers",
],
}
diff --git a/services/surfaceflinger/tests/unittests/CachingTest.cpp b/services/surfaceflinger/tests/unittests/CachingTest.cpp
index 1b8c76d..6a7ec9b 100644
--- a/services/surfaceflinger/tests/unittests/CachingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CachingTest.cpp
@@ -17,6 +17,7 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
#undef LOG_TAG
#define LOG_TAG "CachingTest"
@@ -30,7 +31,8 @@
class SlotGenerationTest : public testing::Test {
protected:
- BufferStateLayer::HwcSlotGenerator mHwcSlotGenerator;
+ sp<BufferStateLayer::HwcSlotGenerator> mHwcSlotGenerator =
+ sp<BufferStateLayer::HwcSlotGenerator>::make();
sp<GraphicBuffer> mBuffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
sp<GraphicBuffer> mBuffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
sp<GraphicBuffer> mBuffer3{new GraphicBuffer(10, 10, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
@@ -40,7 +42,7 @@
sp<IBinder> binder = new BBinder();
// test getting invalid client_cache_id
client_cache_t id;
- uint32_t slot = mHwcSlotGenerator.getHwcCacheSlot(id);
+ uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(id);
EXPECT_EQ(BufferQueue::INVALID_BUFFER_SLOT, slot);
}
@@ -49,19 +51,19 @@
client_cache_t id;
id.token = binder;
id.id = 0;
- uint32_t slot = mHwcSlotGenerator.getHwcCacheSlot(id);
+ uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(id);
EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 1, slot);
client_cache_t idB;
idB.token = binder;
idB.id = 1;
- slot = mHwcSlotGenerator.getHwcCacheSlot(idB);
+ slot = mHwcSlotGenerator->getHwcCacheSlot(idB);
EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 2, slot);
- slot = mHwcSlotGenerator.getHwcCacheSlot(idB);
+ slot = mHwcSlotGenerator->getHwcCacheSlot(idB);
EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 2, slot);
- slot = mHwcSlotGenerator.getHwcCacheSlot(id);
+ slot = mHwcSlotGenerator->getHwcCacheSlot(id);
EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 1, slot);
}
@@ -76,12 +78,12 @@
id.id = cacheId;
ids.push_back(id);
- uint32_t slot = mHwcSlotGenerator.getHwcCacheSlot(id);
+ uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(id);
EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
cacheId++;
}
for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
- uint32_t slot = mHwcSlotGenerator.getHwcCacheSlot(ids[i]);
+ uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(ids[i]);
EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
}
@@ -89,7 +91,7 @@
client_cache_t id;
id.token = binder;
id.id = cacheId;
- uint32_t slot = mHwcSlotGenerator.getHwcCacheSlot(id);
+ uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(id);
EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
cacheId++;
}
@@ -97,4 +99,4 @@
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index faa619e..3042450 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -15,8 +15,10 @@
*/
// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#include "renderengine/ExternalTexture.h"
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
#undef LOG_TAG
#define LOG_TAG "CompositionTest"
@@ -35,16 +37,17 @@
#include <utils/String8.h>
#include "BufferQueueLayer.h"
+#include "ContainerLayer.h"
+#include "DisplayRenderArea.h"
#include "EffectLayer.h"
#include "Layer.h"
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/DisplayHardware/MockPowerAdvisor.h"
-#include "mock/MockDispSync.h"
-#include "mock/MockEventControlThread.h"
#include "mock/MockEventThread.h"
#include "mock/MockMessageQueue.h"
#include "mock/MockTimeStats.h"
+#include "mock/MockVsyncController.h"
#include "mock/system/window/MockNativeWindow.h"
namespace android {
@@ -60,15 +63,9 @@
using testing::_;
using testing::AtLeast;
-using testing::Between;
-using testing::ByMove;
using testing::DoAll;
-using testing::Field;
-using testing::Invoke;
using testing::IsNull;
using testing::Mock;
-using testing::NotNull;
-using testing::Ref;
using testing::Return;
using testing::ReturnRef;
using testing::SetArgPointee;
@@ -80,11 +77,10 @@
constexpr hal::HWLayerId HWC_LAYER = 5000;
constexpr Transform DEFAULT_TRANSFORM = static_cast<Transform>(0);
-constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42};
+constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID(42);
constexpr int DEFAULT_DISPLAY_WIDTH = 1920;
constexpr int DEFAULT_DISPLAY_HEIGHT = 1024;
-constexpr int DEFAULT_CONFIG_ID = 0;
constexpr int DEFAULT_TEXTURE_ID = 6000;
constexpr int DEFAULT_LAYER_STACK = 7000;
@@ -132,26 +128,30 @@
EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*eventThread, createEventConnection(_, _))
- .WillOnce(Return(
- new EventThreadConnection(eventThread.get(), ResyncCallback(),
- ISurfaceComposer::eConfigChangedSuppress)));
+ .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+ ResyncCallback())));
EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
- .WillOnce(Return(
- new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
- ISurfaceComposer::eConfigChangedSuppress)));
+ .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+ ResyncCallback())));
- auto primaryDispSync = std::make_unique<mock::DispSync>();
+ auto vsyncController = std::make_unique<mock::VsyncController>();
+ auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
- EXPECT_CALL(*primaryDispSync, computeNextRefresh(0, _)).WillRepeatedly(Return(0));
- EXPECT_CALL(*primaryDispSync, getPeriod())
- .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
- EXPECT_CALL(*primaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*vsyncTracker, currentPeriod())
+ .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
- mFlinger.setupScheduler(std::move(primaryDispSync),
- std::make_unique<mock::EventControlThread>(),
- std::move(eventThread), std::move(sfEventThread));
+ constexpr ISchedulerCallback* kCallback = nullptr;
+ constexpr bool kHasMultipleConfigs = true;
+ mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
+ std::move(eventThread), std::move(sfEventThread), kCallback,
+ kHasMultipleConfigs);
+
+ // Layer history should be created if there are multiple configs.
+ ASSERT_TRUE(mFlinger.scheduler()->hasLayerHistory());
}
void setupForceGeometryDirty() {
@@ -182,6 +182,7 @@
sp<compositionengine::mock::DisplaySurface> mDisplaySurface =
new compositionengine::mock::DisplaySurface();
mock::NativeWindow* mNativeWindow = new mock::NativeWindow();
+ std::vector<sp<Layer>> mAuxiliaryLayers;
sp<GraphicBuffer> mBuffer = new GraphicBuffer();
ANativeWindowBuffer* mNativeWindowBuffer = mBuffer->getNativeBuffer();
@@ -194,7 +195,7 @@
sp<Fence> mClientTargetAcquireFence = Fence::NO_FENCE;
- sp<GraphicBuffer> mCaptureScreenBuffer;
+ std::shared_ptr<renderengine::ExternalTexture> mCaptureScreenBuffer;
};
template <typename LayerCase>
@@ -229,32 +230,30 @@
LayerCase::setupForScreenCapture(this);
const Rect sourceCrop(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT);
- constexpr bool useIdentityTransform = true;
constexpr bool forSystem = true;
constexpr bool regionSampling = false;
- DisplayRenderArea renderArea(mDisplay, sourceCrop, DEFAULT_DISPLAY_WIDTH,
- DEFAULT_DISPLAY_HEIGHT, ui::Dataspace::V0_SRGB,
- ui::Transform::ROT_0);
+ auto renderArea = DisplayRenderArea::create(mDisplay, sourceCrop, sourceCrop.getSize(),
+ ui::Dataspace::V0_SRGB, ui::Transform::ROT_0);
auto traverseLayers = [this](const LayerVector::Visitor& visitor) {
- return mFlinger.traverseLayersInDisplay(mDisplay, visitor);
+ return mFlinger.traverseLayersInLayerStack(mDisplay->getLayerStack(),
+ CaptureArgs::UNSET_UID, visitor);
};
// TODO: Eliminate expensive/real allocation if possible.
const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
- mCaptureScreenBuffer = new GraphicBuffer(renderArea.getReqWidth(), renderArea.getReqHeight(),
- HAL_PIXEL_FORMAT_RGBA_8888, 1, usage, "screenshot");
+ mCaptureScreenBuffer = std::make_shared<
+ renderengine::ExternalTexture>(new GraphicBuffer(renderArea->getReqWidth(),
+ renderArea->getReqHeight(),
+ HAL_PIXEL_FORMAT_RGBA_8888, 1, usage,
+ "screenshot"),
+ *mRenderEngine, true);
- int fd = -1;
status_t result =
- mFlinger.captureScreenImplLocked(renderArea, traverseLayers, mCaptureScreenBuffer.get(),
- useIdentityTransform, forSystem, &fd, regionSampling);
- if (fd >= 0) {
- close(fd);
- }
-
+ mFlinger.renderScreenImplLocked(*renderArea, traverseLayers, mCaptureScreenBuffer,
+ forSystem, regionSampling);
EXPECT_EQ(NO_ERROR, result);
LayerCase::cleanup(this);
@@ -292,7 +291,7 @@
auto ceDisplayArgs =
compositionengine::DisplayCreationArgsBuilder()
- .setPhysical({DEFAULT_DISPLAY_ID, DisplayConnectionType::Internal})
+ .setPhysical({DEFAULT_DISPLAY_ID, ui::DisplayConnectionType::Internal})
.setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
.setIsSecure(Derived::IS_SECURE)
.setLayerStackId(DEFAULT_LAYER_STACK)
@@ -306,7 +305,7 @@
ceDisplayArgs);
test->mDisplay = FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay,
- DisplayConnectionType::Internal, HWC_DISPLAY,
+ ui::DisplayConnectionType::Internal, HWC_DISPLAY,
true /* isPrimary */)
.setDisplaySurface(test->mDisplaySurface)
.setNativeWindow(test->mNativeWindow)
@@ -334,8 +333,6 @@
EXPECT_CALL(*test->mComposer, presentDisplay(HWC_DISPLAY, _)).Times(1);
EXPECT_CALL(*test->mComposer, getReleaseFences(HWC_DISPLAY, _, _)).Times(1);
- EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true));
-
EXPECT_CALL(*test->mDisplaySurface, onFrameCommitted()).Times(1);
EXPECT_CALL(*test->mDisplaySurface, advanceFrame()).Times(1);
@@ -348,8 +345,8 @@
EXPECT_CALL(*test->mRenderEngine, drawLayers)
.WillRepeatedly([](const renderengine::DisplaySettings& displaySettings,
const std::vector<const renderengine::LayerSettings*>&,
- ANativeWindowBuffer*, const bool, base::unique_fd&&,
- base::unique_fd*) -> status_t {
+ const std::shared_ptr<renderengine::ExternalTexture>&,
+ const bool, base::unique_fd&&, base::unique_fd*) -> status_t {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
displaySettings.physicalDisplay);
@@ -397,8 +394,8 @@
EXPECT_CALL(*test->mRenderEngine, drawLayers)
.WillRepeatedly([](const renderengine::DisplaySettings& displaySettings,
const std::vector<const renderengine::LayerSettings*>&,
- ANativeWindowBuffer*, const bool, base::unique_fd&&,
- base::unique_fd*) -> status_t {
+ const std::shared_ptr<renderengine::ExternalTexture>&,
+ const bool, base::unique_fd&&, base::unique_fd*) -> status_t {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
displaySettings.physicalDisplay);
@@ -444,8 +441,6 @@
template <typename Case>
static void setupCommonCompositionCallExpectations(CompositionTest* test) {
- EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true));
-
// TODO: This seems like an unnecessary call if display is powered off.
EXPECT_CALL(*test->mComposer,
setColorTransform(HWC_DISPLAY, _, Hwc2::ColorTransform::IDENTITY))
@@ -460,8 +455,6 @@
static void setupHwcForcedClientCompositionCallExpectations(CompositionTest*) {}
static void setupRECompositionCallExpectations(CompositionTest* test) {
- EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true));
-
// TODO: This seems like an unnecessary call if display is powered off.
EXPECT_CALL(*test->mDisplaySurface, getClientTargetAcquireFence())
.WillRepeatedly(ReturnRef(test->mClientTargetAcquireFence));
@@ -544,7 +537,6 @@
enqueueBuffer(test, layer);
Mock::VerifyAndClearExpectations(test->mMessageQueue);
- EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true));
bool ignoredRecomputeVisibleRegions;
layer->latchBuffer(ignoredRecomputeVisibleRegions, 0, 0);
Mock::VerifyAndClear(test->mRenderEngine);
@@ -554,12 +546,6 @@
setupLatchedBuffer(test, layer);
}
- static void setupBufferLayerPostFrameCallExpectations(CompositionTest* test) {
- // BufferLayer::onPostComposition(), when there is no present fence
- EXPECT_CALL(*test->mComposer, getActiveConfig(HWC_DISPLAY, _))
- .WillOnce(DoAll(SetArgPointee<1>(DEFAULT_CONFIG_ID), Return(Error::NONE)));
- }
-
static void setupHwcSetGeometryCallExpectations(CompositionTest* test) {
if (!test->mDisplayOff) {
// TODO: Coverage of other values
@@ -638,16 +624,14 @@
.Times(1);
EXPECT_CALL(*test->mComposer, setLayerBuffer(HWC_DISPLAY, HWC_LAYER, _, _, _)).Times(1);
}
-
- setupBufferLayerPostFrameCallExpectations(test);
}
static void setupREBufferCompositionCommonCallExpectations(CompositionTest* test) {
EXPECT_CALL(*test->mRenderEngine, drawLayers)
.WillOnce([](const renderengine::DisplaySettings& displaySettings,
const std::vector<const renderengine::LayerSettings*>& layerSettings,
- ANativeWindowBuffer*, const bool, base::unique_fd&&,
- base::unique_fd*) -> status_t {
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&, base::unique_fd*) -> status_t {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
displaySettings.physicalDisplay);
@@ -695,8 +679,8 @@
EXPECT_CALL(*test->mRenderEngine, drawLayers)
.WillOnce([](const renderengine::DisplaySettings& displaySettings,
const std::vector<const renderengine::LayerSettings*>& layerSettings,
- ANativeWindowBuffer*, const bool, base::unique_fd&&,
- base::unique_fd*) -> status_t {
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&, base::unique_fd*) -> status_t {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
displaySettings.physicalDisplay);
@@ -764,17 +748,16 @@
static void setupREBufferCompositionCommonCallExpectations(CompositionTest* /*test*/) {}
};
-struct SecureLayerProperties : public BaseLayerProperties<SecureLayerProperties> {
- using Base = BaseLayerProperties<SecureLayerProperties>;
-
- static constexpr uint32_t LAYER_FLAGS = ISurfaceComposerClient::eSecure;
+template <typename LayerProperties>
+struct CommonSecureLayerProperties : public BaseLayerProperties<LayerProperties> {
+ using Base = BaseLayerProperties<LayerProperties>;
static void setupInsecureREBufferCompositionCommonCallExpectations(CompositionTest* test) {
EXPECT_CALL(*test->mRenderEngine, drawLayers)
.WillOnce([](const renderengine::DisplaySettings& displaySettings,
const std::vector<const renderengine::LayerSettings*>& layerSettings,
- ANativeWindowBuffer*, const bool, base::unique_fd&&,
- base::unique_fd*) -> status_t {
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&, base::unique_fd*) -> status_t {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
displaySettings.physicalDisplay);
@@ -800,7 +783,6 @@
static void setupInsecureREBufferCompositionCallExpectations(CompositionTest* test) {
setupInsecureREBufferCompositionCommonCallExpectations(test);
- Base::setupBufferLayerPostFrameCallExpectations(test);
}
static void setupInsecureREBufferScreenshotCompositionCallExpectations(CompositionTest* test) {
@@ -808,6 +790,13 @@
}
};
+struct ParentSecureLayerProperties
+ : public CommonSecureLayerProperties<ParentSecureLayerProperties> {};
+
+struct SecureLayerProperties : public CommonSecureLayerProperties<SecureLayerProperties> {
+ static constexpr uint32_t LAYER_FLAGS = ISurfaceComposerClient::eSecure;
+};
+
struct CursorLayerProperties : public BaseLayerProperties<CursorLayerProperties> {
using Base = BaseLayerProperties<CursorLayerProperties>;
@@ -843,15 +832,20 @@
Mock::VerifyAndClear(test->mRenderEngine);
Mock::VerifyAndClearExpectations(test->mMessageQueue);
+ initLayerDrawingStateAndComputeBounds(test, layer);
+
+ return layer;
+ }
+
+ template <typename L>
+ static void initLayerDrawingStateAndComputeBounds(CompositionTest* test, sp<L> layer) {
auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
layerDrawingState.layerStack = DEFAULT_LAYER_STACK;
- layerDrawingState.active.w = 100;
- layerDrawingState.active.h = 100;
+ layerDrawingState.width = 100;
+ layerDrawingState.height = 100;
layerDrawingState.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
LayerProperties::COLOR[2], LayerProperties::COLOR[3]);
layer->computeBounds(FloatRect(0, 0, 100, 100), ui::Transform(), 0.f /* shadowRadius */);
-
- return layer;
}
static void injectLayer(CompositionTest* test, sp<Layer> layer) {
@@ -889,13 +883,13 @@
static FlingerLayerType createLayer(CompositionTest* test) {
FlingerLayerType layer = Base::template createLayerWithFactory<EffectLayer>(test, [test]() {
return new EffectLayer(
- LayerCreationArgs(test->mFlinger.mFlinger.get(), sp<Client>(), "test-layer",
+ LayerCreationArgs(test->mFlinger.flinger(), sp<Client>(), "test-layer",
LayerProperties::WIDTH, LayerProperties::HEIGHT,
LayerProperties::LAYER_FLAGS, LayerMetadata()));
});
auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
- layerDrawingState.crop_legacy = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
+ layerDrawingState.crop = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
return layer;
}
@@ -928,8 +922,7 @@
FlingerLayerType layer =
Base::template createLayerWithFactory<BufferQueueLayer>(test, [test]() {
- sp<Client> client;
- LayerCreationArgs args(test->mFlinger.mFlinger.get(), client, "test-layer",
+ LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-layer",
LayerProperties::WIDTH, LayerProperties::HEIGHT,
LayerProperties::LAYER_FLAGS, LayerMetadata());
args.textureName = test->mFlinger.mutableTexturePool().back();
@@ -973,6 +966,49 @@
}
};
+template <typename LayerProperties>
+struct ContainerLayerVariant : public BaseLayerVariant<LayerProperties> {
+ using Base = BaseLayerVariant<LayerProperties>;
+ using FlingerLayerType = sp<ContainerLayer>;
+
+ static FlingerLayerType createLayer(CompositionTest* test) {
+ LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-container-layer",
+ LayerProperties::WIDTH, LayerProperties::HEIGHT,
+ LayerProperties::LAYER_FLAGS, LayerMetadata());
+ FlingerLayerType layer = new ContainerLayer(args);
+ Base::template initLayerDrawingStateAndComputeBounds(test, layer);
+ return layer;
+ }
+};
+
+template <typename LayerVariant, typename ParentLayerVariant>
+struct ChildLayerVariant : public LayerVariant {
+ using Base = LayerVariant;
+ using FlingerLayerType = typename LayerVariant::FlingerLayerType;
+ using ParentBase = ParentLayerVariant;
+
+ static FlingerLayerType createLayer(CompositionTest* test) {
+ // Need to create child layer first. Otherwise layer history size will be 2.
+ FlingerLayerType layer = Base::createLayer(test);
+
+ typename ParentBase::FlingerLayerType parentLayer = ParentBase::createLayer(test);
+ parentLayer->addChild(layer);
+ test->mFlinger.setLayerDrawingParent(layer, parentLayer);
+
+ test->mAuxiliaryLayers.push_back(parentLayer);
+
+ return layer;
+ }
+
+ static void cleanupInjectedLayers(CompositionTest* test) {
+ // Clear auxiliary layers first so that child layer can be successfully destroyed in the
+ // following call.
+ test->mAuxiliaryLayers.clear();
+
+ Base::cleanupInjectedLayers(test);
+ }
+};
+
/* ------------------------------------------------------------------------
* Variants to control how the composition type is changed
*/
@@ -1362,6 +1398,38 @@
}
/* ------------------------------------------------------------------------
+ * Layers with a parent layer with ISurfaceComposerClient::eSecure, on a non-secure display
+ */
+
+TEST_F(CompositionTest,
+ HWCComposedBufferLayerWithSecureParentLayerOnInsecureDisplayWithDirtyGeometry) {
+ displayRefreshCompositionDirtyGeometry<
+ CompositionCase<InsecureDisplaySetupVariant,
+ ChildLayerVariant<BufferLayerVariant<ParentSecureLayerProperties>,
+ ContainerLayerVariant<SecureLayerProperties>>,
+ KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
+ ForcedClientCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest,
+ HWCComposedBufferLayerWithSecureParentLayerOnInsecureDisplayWithDirtyFrame) {
+ displayRefreshCompositionDirtyFrame<
+ CompositionCase<InsecureDisplaySetupVariant,
+ ChildLayerVariant<BufferLayerVariant<ParentSecureLayerProperties>,
+ ContainerLayerVariant<SecureLayerProperties>>,
+ KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
+ ForcedClientCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, captureScreenBufferLayerWithSecureParentLayerOnInsecureDisplay) {
+ captureScreenComposition<
+ CompositionCase<InsecureDisplaySetupVariant,
+ ChildLayerVariant<BufferLayerVariant<ParentSecureLayerProperties>,
+ ContainerLayerVariant<SecureLayerProperties>>,
+ NoCompositionTypeVariant, REScreenshotResultVariant>>();
+}
+
+/* ------------------------------------------------------------------------
* Cursor layers
*/
@@ -1450,4 +1518,4 @@
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
index afebc40..54f4c7c 100644
--- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
@@ -27,13 +27,99 @@
#include "AsyncCallRecorder.h"
#include "Scheduler/DispSyncSource.h"
-#include "mock/MockDispSync.h"
+#include "Scheduler/VSyncDispatch.h"
namespace android {
namespace {
using namespace std::chrono_literals;
-using testing::Return;
+using namespace testing;
+
+class MockVSyncDispatch : public scheduler::VSyncDispatch {
+public:
+ MOCK_METHOD2(registerCallback,
+ CallbackToken(std::function<void(nsecs_t, nsecs_t, nsecs_t)> const&, std::string));
+ MOCK_METHOD1(unregisterCallback, void(CallbackToken));
+ MOCK_METHOD2(schedule, scheduler::ScheduleResult(CallbackToken, ScheduleTiming));
+ MOCK_METHOD1(cancel, scheduler::CancelResult(CallbackToken token));
+ MOCK_CONST_METHOD1(dump, void(std::string&));
+
+ MockVSyncDispatch() {
+ ON_CALL(*this, registerCallback)
+ .WillByDefault(
+ [this](std::function<void(nsecs_t, nsecs_t, nsecs_t)> const& callback,
+ std::string) {
+ CallbackToken token(mNextToken);
+ mNextToken++;
+
+ mCallbacks.emplace(token, CallbackData(callback));
+ ALOGD("registerCallback: %zu", token.value());
+ return token;
+ });
+
+ ON_CALL(*this, unregisterCallback).WillByDefault([this](CallbackToken token) {
+ ALOGD("unregisterCallback: %zu", token.value());
+ mCallbacks.erase(token);
+ });
+
+ ON_CALL(*this, schedule).WillByDefault([this](CallbackToken token, ScheduleTiming timing) {
+ ALOGD("schedule: %zu", token.value());
+ if (mCallbacks.count(token) == 0) {
+ ALOGD("schedule: callback %zu not registered", token.value());
+ return scheduler::ScheduleResult::Error;
+ }
+
+ auto& callback = mCallbacks.at(token);
+ callback.scheduled = true;
+ callback.vsyncTime = timing.earliestVsync;
+ callback.targetWakeupTime =
+ timing.earliestVsync - timing.workDuration - timing.readyDuration;
+ ALOGD("schedule: callback %zu scheduled", token.value());
+ return scheduler::ScheduleResult::Scheduled;
+ });
+
+ ON_CALL(*this, cancel).WillByDefault([this](CallbackToken token) {
+ ALOGD("cancel: %zu", token.value());
+ if (mCallbacks.count(token) == 0) {
+ ALOGD("cancel: callback %zu is not registered", token.value());
+ return scheduler::CancelResult::Error;
+ }
+
+ auto& callback = mCallbacks.at(token);
+ callback.scheduled = false;
+ ALOGD("cancel: callback %zu cancelled", token.value());
+ return scheduler::CancelResult::Cancelled;
+ });
+ }
+
+ void triggerCallbacks() {
+ ALOGD("triggerCallbacks");
+ for (auto& [token, callback] : mCallbacks) {
+ if (callback.scheduled) {
+ ALOGD("triggerCallbacks: callback %zu", token.value());
+ callback.scheduled = false;
+ callback.func(callback.vsyncTime, callback.targetWakeupTime, callback.readyTime);
+ } else {
+ ALOGD("triggerCallbacks: callback %zu is not scheduled", token.value());
+ }
+ }
+ }
+
+private:
+ struct CallbackData {
+ explicit CallbackData(std::function<void(nsecs_t, nsecs_t, nsecs_t)> func)
+ : func(std::move(func)) {}
+
+ std::function<void(nsecs_t, nsecs_t, nsecs_t)> func;
+ bool scheduled = false;
+ nsecs_t vsyncTime = 0;
+ nsecs_t targetWakeupTime = 0;
+ nsecs_t readyTime = 0;
+ };
+
+ std::unordered_map<CallbackToken, CallbackData> mCallbacks;
+ size_t mNextToken;
+};
class DispSyncSourceTest : public testing::Test, private VSyncSource::Callback {
protected:
@@ -43,15 +129,19 @@
void createDispSync();
void createDispSyncSource();
- void onVSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) override;
+ void onVSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp,
+ nsecs_t deadlineTimestamp) override;
- std::unique_ptr<mock::DispSync> mDispSync;
- std::unique_ptr<DispSyncSource> mDispSyncSource;
+ std::unique_ptr<MockVSyncDispatch> mVSyncDispatch;
+ std::unique_ptr<scheduler::DispSyncSource> mDispSyncSource;
- AsyncCallRecorder<void (*)(nsecs_t)> mVSyncEventCallRecorder;
+ AsyncCallRecorder<void (*)(nsecs_t, nsecs_t, nsecs_t)> mVSyncEventCallRecorder;
- static constexpr std::chrono::nanoseconds mPhaseOffset = 6ms;
+ static constexpr std::chrono::nanoseconds mWorkDuration = 20ms;
+ static constexpr std::chrono::nanoseconds mReadyDuration = 10ms;
static constexpr int mIterations = 100;
+ const scheduler::VSyncDispatch::CallbackToken mFakeToken{2398};
+ const std::string mName = "DispSyncSourceTest";
};
DispSyncSourceTest::DispSyncSourceTest() {
@@ -66,20 +156,21 @@
ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
}
-void DispSyncSourceTest::onVSyncEvent(nsecs_t when, nsecs_t /*expectedVSyncTimestamp*/) {
+void DispSyncSourceTest::onVSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp,
+ nsecs_t deadlineTimestamp) {
ALOGD("onVSyncEvent: %" PRId64, when);
- mVSyncEventCallRecorder.recordCall(when);
+ mVSyncEventCallRecorder.recordCall(when, expectedVSyncTimestamp, deadlineTimestamp);
}
void DispSyncSourceTest::createDispSync() {
- mDispSync = std::make_unique<mock::DispSync>();
+ mVSyncDispatch = std::make_unique<MockVSyncDispatch>();
}
void DispSyncSourceTest::createDispSyncSource() {
- createDispSync();
- mDispSyncSource = std::make_unique<DispSyncSource>(mDispSync.get(), mPhaseOffset.count(), true,
- "DispSyncSourceTest");
+ mDispSyncSource =
+ std::make_unique<scheduler::DispSyncSource>(*mVSyncDispatch, mWorkDuration,
+ mReadyDuration, true, mName.c_str());
mDispSyncSource->setCallback(this);
}
@@ -89,57 +180,119 @@
TEST_F(DispSyncSourceTest, createDispSync) {
createDispSync();
- EXPECT_TRUE(mDispSync);
+ EXPECT_TRUE(mVSyncDispatch);
}
TEST_F(DispSyncSourceTest, createDispSyncSource) {
+ createDispSync();
+
+ InSequence seq;
+ EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).WillOnce(Return(mFakeToken));
+ EXPECT_CALL(*mVSyncDispatch, cancel(mFakeToken))
+ .WillOnce(Return(scheduler::CancelResult::Cancelled));
+ EXPECT_CALL(*mVSyncDispatch, unregisterCallback(mFakeToken)).WillOnce(Return());
createDispSyncSource();
+
EXPECT_TRUE(mDispSyncSource);
}
TEST_F(DispSyncSourceTest, noCallbackAfterInit) {
+ createDispSync();
+
+ InSequence seq;
+ EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1);
+ EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1);
+ EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1);
createDispSyncSource();
+
EXPECT_TRUE(mDispSyncSource);
// DispSyncSource starts with Vsync disabled
- mDispSync->triggerCallback();
+ mVSyncDispatch->triggerCallbacks();
EXPECT_FALSE(mVSyncEventCallRecorder.waitForUnexpectedCall().has_value());
}
TEST_F(DispSyncSourceTest, waitForCallbacks) {
+ createDispSync();
+
+ InSequence seq;
+ EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1);
+ EXPECT_CALL(*mVSyncDispatch,
+ schedule(_, Truly([&](auto timings) {
+ return timings.workDuration == mWorkDuration.count() &&
+ timings.readyDuration == mReadyDuration.count();
+ })))
+ .Times(mIterations + 1);
+ EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1);
+ EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1);
createDispSyncSource();
+
EXPECT_TRUE(mDispSyncSource);
mDispSyncSource->setVSyncEnabled(true);
- EXPECT_EQ(mDispSync->getCallbackPhase(), mPhaseOffset.count());
-
for (int i = 0; i < mIterations; i++) {
- mDispSync->triggerCallback();
- EXPECT_TRUE(mVSyncEventCallRecorder.waitForCall().has_value());
+ mVSyncDispatch->triggerCallbacks();
+ const auto callbackData = mVSyncEventCallRecorder.waitForCall();
+ ASSERT_TRUE(callbackData.has_value());
+ const auto [when, expectedVSyncTimestamp, deadlineTimestamp] = callbackData.value();
+ EXPECT_EQ(when, expectedVSyncTimestamp - mWorkDuration.count() - mReadyDuration.count());
}
}
-TEST_F(DispSyncSourceTest, waitForCallbacksWithPhaseChange) {
+TEST_F(DispSyncSourceTest, waitForCallbacksWithDurationChange) {
+ createDispSync();
+
+ InSequence seq;
+ EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1);
+ EXPECT_CALL(*mVSyncDispatch,
+ schedule(_, Truly([&](auto timings) {
+ return timings.workDuration == mWorkDuration.count() &&
+ timings.readyDuration == mReadyDuration.count();
+ })))
+ .Times(1);
+
createDispSyncSource();
+
EXPECT_TRUE(mDispSyncSource);
mDispSyncSource->setVSyncEnabled(true);
- EXPECT_EQ(mDispSync->getCallbackPhase(), mPhaseOffset.count());
-
+ EXPECT_CALL(*mVSyncDispatch,
+ schedule(_, Truly([&](auto timings) {
+ return timings.workDuration == mWorkDuration.count() &&
+ timings.readyDuration == mReadyDuration.count();
+ })))
+ .Times(mIterations);
for (int i = 0; i < mIterations; i++) {
- mDispSync->triggerCallback();
- EXPECT_TRUE(mVSyncEventCallRecorder.waitForCall().has_value());
+ mVSyncDispatch->triggerCallbacks();
+ const auto callbackData = mVSyncEventCallRecorder.waitForCall();
+ ASSERT_TRUE(callbackData.has_value());
+ const auto [when, expectedVSyncTimestamp, deadlineTimestamp] = callbackData.value();
+ EXPECT_EQ(when, expectedVSyncTimestamp - mWorkDuration.count() - mReadyDuration.count());
}
- EXPECT_CALL(*mDispSync, getPeriod()).Times(1).WillOnce(Return(16666666));
- mDispSyncSource->setPhaseOffset((mPhaseOffset / 2).count());
+ const auto newDuration = mWorkDuration / 2;
+ EXPECT_CALL(*mVSyncDispatch, schedule(_, Truly([&](auto timings) {
+ return timings.workDuration == newDuration.count() &&
+ timings.readyDuration == 0;
+ })))
+ .Times(1);
+ mDispSyncSource->setDuration(newDuration, 0ns);
- EXPECT_EQ(mDispSync->getCallbackPhase(), (mPhaseOffset / 2).count());
-
+ EXPECT_CALL(*mVSyncDispatch, schedule(_, Truly([&](auto timings) {
+ return timings.workDuration == newDuration.count() &&
+ timings.readyDuration == 0;
+ })))
+ .Times(mIterations);
for (int i = 0; i < mIterations; i++) {
- mDispSync->triggerCallback();
- EXPECT_TRUE(mVSyncEventCallRecorder.waitForCall().has_value());
+ mVSyncDispatch->triggerCallbacks();
+ const auto callbackData = mVSyncEventCallRecorder.waitForCall();
+ ASSERT_TRUE(callbackData.has_value());
+ const auto [when, expectedVSyncTimestamp, deadlineTimestamp] = callbackData.value();
+ EXPECT_EQ(when, expectedVSyncTimestamp - newDuration.count());
}
+
+ EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1);
+ EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1);
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_GetBestColorModeTest.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_GetBestColorModeTest.cpp
new file mode 100644
index 0000000..2e53cd1
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_GetBestColorModeTest.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace {
+
+using hal::RenderIntent;
+
+using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+
+class GetBestColorModeTest : public DisplayTransactionTest {
+public:
+ void setHasWideColorGamut(bool hasWideColorGamut) { mHasWideColorGamut = hasWideColorGamut; }
+
+ void addHwcColorModesMapping(ui::ColorMode colorMode,
+ std::vector<ui::RenderIntent> renderIntents) {
+ mHwcColorModes[colorMode] = renderIntents;
+ }
+
+ void setInputDataspace(ui::Dataspace dataspace) { mInputDataspace = dataspace; }
+
+ void setInputRenderIntent(ui::RenderIntent renderIntent) { mInputRenderIntent = renderIntent; }
+
+ void getBestColorMode() {
+ auto displayDevice =
+ injectDefaultInternalDisplay([this](FakeDisplayDeviceInjector& injector) {
+ injector.setHwcColorModes(mHwcColorModes);
+ injector.setHasWideColorGamut(mHasWideColorGamut);
+ injector.setNativeWindow(mNativeWindow);
+ });
+
+ displayDevice->getCompositionDisplay()
+ ->getDisplayColorProfile()
+ ->getBestColorMode(mInputDataspace, mInputRenderIntent, &mOutDataspace,
+ &mOutColorMode, &mOutRenderIntent);
+ }
+
+ ui::Dataspace mOutDataspace;
+ ui::ColorMode mOutColorMode;
+ ui::RenderIntent mOutRenderIntent;
+
+private:
+ ui::Dataspace mInputDataspace;
+ ui::RenderIntent mInputRenderIntent;
+ bool mHasWideColorGamut = false;
+ std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> mHwcColorModes;
+};
+
+TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeSRGB) {
+ addHwcColorModesMapping(ui::ColorMode::SRGB,
+ std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+ setInputDataspace(ui::Dataspace::DISPLAY_P3);
+ setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
+ setHasWideColorGamut(true);
+
+ getBestColorMode();
+
+ ASSERT_EQ(ui::Dataspace::V0_SRGB, mOutDataspace);
+ ASSERT_EQ(ui::ColorMode::SRGB, mOutColorMode);
+ ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
+}
+
+TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeDisplayP3) {
+ addHwcColorModesMapping(ui::ColorMode::DISPLAY_P3,
+ std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+ addHwcColorModesMapping(ui::ColorMode::SRGB,
+ std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+ addHwcColorModesMapping(ui::ColorMode::DISPLAY_BT2020,
+ std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+ setInputDataspace(ui::Dataspace::DISPLAY_P3);
+ setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
+ setHasWideColorGamut(true);
+
+ getBestColorMode();
+
+ ASSERT_EQ(ui::Dataspace::DISPLAY_P3, mOutDataspace);
+ ASSERT_EQ(ui::ColorMode::DISPLAY_P3, mOutColorMode);
+ ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
+}
+
+TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeDISPLAY_BT2020) {
+ addHwcColorModesMapping(ui::ColorMode::DISPLAY_BT2020,
+ std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+ setInputDataspace(ui::Dataspace::DISPLAY_P3);
+ setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
+ setHasWideColorGamut(true);
+
+ getBestColorMode();
+
+ ASSERT_EQ(ui::Dataspace::DISPLAY_BT2020, mOutDataspace);
+ ASSERT_EQ(ui::ColorMode::DISPLAY_BT2020, mOutColorMode);
+ ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp
new file mode 100644
index 0000000..9fe30f8
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace {
+
+using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+
+class DisplayDeviceSetProjectionTest : public DisplayTransactionTest {
+public:
+ static constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1080; // arbitrary
+ static constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1920; // arbitrary
+
+ static constexpr int32_t TRANSFORM_FLAGS_ROT_0 = 0;
+ static constexpr int32_t TRANSFORM_FLAGS_ROT_90 = HAL_TRANSFORM_ROT_90;
+ static constexpr int32_t TRANSFORM_FLAGS_ROT_180 = HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_FLIP_V;
+ static constexpr int32_t TRANSFORM_FLAGS_ROT_270 =
+ HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_ROT_90;
+
+ DisplayDeviceSetProjectionTest(ui::Size flingerDisplaySize, ui::Size hardwareDisplaySize,
+ ui::Rotation physicalOrientation)
+ : mFlingerDisplaySize(flingerDisplaySize),
+ mHardwareDisplaySize(hardwareDisplaySize),
+ mPhysicalOrientation(physicalOrientation),
+ mDisplayDevice(createDisplayDevice()) {}
+
+ sp<DisplayDevice> createDisplayDevice() {
+ return injectDefaultInternalDisplay([this](FakeDisplayDeviceInjector& injector) {
+ injector.setPhysicalOrientation(mPhysicalOrientation);
+ });
+ }
+
+ ui::Size swapWH(const ui::Size size) const { return ui::Size(size.height, size.width); }
+
+ void setProjectionForRotation0() {
+ // A logical rotation of 0 uses the SurfaceFlinger display size
+ mDisplayDevice->setProjection(ui::ROTATION_0, Rect(mFlingerDisplaySize),
+ Rect(mFlingerDisplaySize));
+ }
+
+ void setProjectionForRotation90() {
+ // A logical rotation of 90 uses the SurfaceFlinger display size with
+ // the width/height swapped.
+ mDisplayDevice->setProjection(ui::ROTATION_90, Rect(swapWH(mFlingerDisplaySize)),
+ Rect(swapWH(mFlingerDisplaySize)));
+ }
+
+ void setProjectionForRotation180() {
+ // A logical rotation of 180 uses the SurfaceFlinger display size
+ mDisplayDevice->setProjection(ui::ROTATION_180, Rect(mFlingerDisplaySize),
+ Rect(mFlingerDisplaySize));
+ }
+
+ void setProjectionForRotation270() {
+ // A logical rotation of 270 uses the SurfaceFlinger display size with
+ // the width/height swapped.
+ mDisplayDevice->setProjection(ui::ROTATION_270, Rect(swapWH(mFlingerDisplaySize)),
+ Rect(swapWH(mFlingerDisplaySize)));
+ }
+
+ void expectStateForHardwareTransform0() {
+ const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
+ EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_0, mHardwareDisplaySize.width,
+ mHardwareDisplaySize.height),
+ compositionState.transform);
+ EXPECT_EQ(ui::ROTATION_0, compositionState.displaySpace.orientation);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content);
+ EXPECT_EQ(false, compositionState.needsFiltering);
+ }
+
+ void expectStateForHardwareTransform90() {
+ const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
+ EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_90, mHardwareDisplaySize.width,
+ mHardwareDisplaySize.height),
+ compositionState.transform);
+ EXPECT_EQ(ui::ROTATION_90, compositionState.displaySpace.orientation);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
+ // For 90, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display
+ // size width and height swapped
+ EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)),
+ compositionState.orientedDisplaySpace.content);
+ EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content);
+ EXPECT_EQ(false, compositionState.needsFiltering);
+ }
+
+ void expectStateForHardwareTransform180() {
+ const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
+ EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_180, mHardwareDisplaySize.width,
+ mHardwareDisplaySize.height),
+ compositionState.transform);
+ EXPECT_EQ(ui::ROTATION_180, compositionState.displaySpace.orientation);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content);
+ EXPECT_EQ(false, compositionState.needsFiltering);
+ }
+
+ void expectStateForHardwareTransform270() {
+ const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
+ EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_270, mHardwareDisplaySize.width,
+ mHardwareDisplaySize.height),
+ compositionState.transform);
+ EXPECT_EQ(ui::ROTATION_270, compositionState.displaySpace.orientation);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
+ // For 270, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display
+ // size width and height swapped
+ EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)),
+ compositionState.orientedDisplaySpace.content);
+ EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content);
+ EXPECT_EQ(false, compositionState.needsFiltering);
+ }
+
+ const ui::Size mFlingerDisplaySize;
+ const ui::Size mHardwareDisplaySize;
+ const ui::Rotation mPhysicalOrientation;
+ const sp<DisplayDevice> mDisplayDevice;
+};
+
+struct DisplayDeviceSetProjectionTest_Installed0 : public DisplayDeviceSetProjectionTest {
+ DisplayDeviceSetProjectionTest_Installed0()
+ : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ ui::ROTATION_0) {}
+};
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith0OutputRotation) {
+ setProjectionForRotation0();
+ expectStateForHardwareTransform0();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith90OutputRotation) {
+ setProjectionForRotation90();
+ expectStateForHardwareTransform90();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith180OutputRotation) {
+ setProjectionForRotation180();
+ expectStateForHardwareTransform180();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith270OutputRotation) {
+ setProjectionForRotation270();
+ expectStateForHardwareTransform270();
+}
+
+struct DisplayDeviceSetProjectionTest_Installed90 : public DisplayDeviceSetProjectionTest {
+ DisplayDeviceSetProjectionTest_Installed90()
+ : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH),
+ ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ ui::ROTATION_90) {}
+};
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith0OutputRotation) {
+ setProjectionForRotation0();
+ expectStateForHardwareTransform90();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith90OutputRotation) {
+ setProjectionForRotation90();
+ expectStateForHardwareTransform180();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith180OutputRotation) {
+ setProjectionForRotation180();
+ expectStateForHardwareTransform270();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith270OutputRotation) {
+ setProjectionForRotation270();
+ expectStateForHardwareTransform0();
+}
+
+struct DisplayDeviceSetProjectionTest_Installed180 : public DisplayDeviceSetProjectionTest {
+ DisplayDeviceSetProjectionTest_Installed180()
+ : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ ui::ROTATION_180) {}
+};
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith0OutputRotation) {
+ setProjectionForRotation0();
+ expectStateForHardwareTransform180();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith90OutputRotation) {
+ setProjectionForRotation90();
+ expectStateForHardwareTransform270();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith180OutputRotation) {
+ setProjectionForRotation180();
+ expectStateForHardwareTransform0();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith270OutputRotation) {
+ setProjectionForRotation270();
+ expectStateForHardwareTransform90();
+}
+
+struct DisplayDeviceSetProjectionTest_Installed270 : public DisplayDeviceSetProjectionTest {
+ DisplayDeviceSetProjectionTest_Installed270()
+ : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH),
+ ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ ui::ROTATION_270) {}
+};
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith0OutputRotation) {
+ setProjectionForRotation0();
+ expectStateForHardwareTransform270();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith90OutputRotation) {
+ setProjectionForRotation90();
+ expectStateForHardwareTransform0();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith180OutputRotation) {
+ setProjectionForRotation180();
+ expectStateForHardwareTransform90();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith270OutputRotation) {
+ setProjectionForRotation270();
+ expectStateForHardwareTransform180();
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayIdGeneratorTest.cpp b/services/surfaceflinger/tests/unittests/DisplayIdGeneratorTest.cpp
new file mode 100644
index 0000000..77a3e14
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/DisplayIdGeneratorTest.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2020 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wextra"
+
+#include <gtest/gtest.h>
+
+#include <vector>
+
+#include <ui/DisplayId.h>
+#include "DisplayIdGenerator.h"
+
+namespace android {
+
+template <typename T>
+void testNextId(DisplayIdGenerator<T>& generator) {
+ constexpr int kNumIds = 5;
+ std::vector<T> ids;
+ for (int i = 0; i < kNumIds; i++) {
+ const auto id = generator.nextId();
+ ASSERT_TRUE(id);
+ ids.push_back(*id);
+ }
+
+ // All IDs should be different.
+ for (size_t i = 0; i < kNumIds; i++) {
+ for (size_t j = i + 1; j < kNumIds; j++) {
+ EXPECT_NE(ids[i], ids[j]);
+ }
+ }
+}
+
+TEST(DisplayIdGeneratorTest, nextIdGpuVirtual) {
+ RandomDisplayIdGenerator<GpuVirtualDisplayId> generator;
+ testNextId(generator);
+}
+
+TEST(DisplayIdGeneratorTest, nextIdHalVirtual) {
+ RandomDisplayIdGenerator<HalVirtualDisplayId> generator;
+ testNextId(generator);
+}
+
+TEST(DisplayIdGeneratorTest, markUnused) {
+ constexpr size_t kMaxIdsCount = 5;
+ RandomDisplayIdGenerator<GpuVirtualDisplayId> generator(kMaxIdsCount);
+
+ const auto id = generator.nextId();
+ EXPECT_TRUE(id);
+
+ for (int i = 1; i < kMaxIdsCount; i++) {
+ EXPECT_TRUE(generator.nextId());
+ }
+
+ EXPECT_FALSE(generator.nextId());
+
+ generator.markUnused(*id);
+ EXPECT_TRUE(generator.nextId());
+}
+
+TEST(DisplayIdGeneratorTest, maxIdsCount) {
+ constexpr size_t kMaxIdsCount = 5;
+ RandomDisplayIdGenerator<GpuVirtualDisplayId> generator(kMaxIdsCount);
+
+ for (int i = 0; i < kMaxIdsCount; i++) {
+ EXPECT_TRUE(generator.nextId());
+ }
+
+ EXPECT_FALSE(generator.nextId());
+}
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
index 2a0e913..dc04b6d 100644
--- a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wextra"
+
#include <functional>
#include <string_view>
@@ -22,6 +26,8 @@
#include "DisplayHardware/DisplayIdentification.h"
+using ::testing::ElementsAre;
+
namespace android {
namespace {
@@ -312,93 +318,92 @@
using ManufactureYear = DeviceProductInfo::ManufactureYear;
using ManufactureWeekAndYear = DeviceProductInfo::ManufactureWeekAndYear;
using ModelYear = DeviceProductInfo::ModelYear;
- using RelativeAddress = DeviceProductInfo::RelativeAddress;
{
const auto displayIdInfo = parseDisplayIdentificationData(0, getInternalEdid());
ASSERT_TRUE(displayIdInfo);
ASSERT_TRUE(displayIdInfo->deviceProductInfo);
const auto& info = *displayIdInfo->deviceProductInfo;
- EXPECT_STREQ("", info.name.data());
+ EXPECT_EQ("", info.name);
EXPECT_STREQ("SEC", info.manufacturerPnpId.data());
- EXPECT_STREQ("12610", info.productId.data());
+ EXPECT_EQ("12610", info.productId);
ASSERT_TRUE(std::holds_alternative<ManufactureYear>(info.manufactureOrModelDate));
EXPECT_EQ(2011, std::get<ManufactureYear>(info.manufactureOrModelDate).year);
- EXPECT_EQ(DeviceProductInfo::NO_RELATIVE_ADDRESS, info.relativeAddress);
+ EXPECT_TRUE(info.relativeAddress.empty());
}
{
const auto displayIdInfo = parseDisplayIdentificationData(0, getExternalEdid());
ASSERT_TRUE(displayIdInfo);
ASSERT_TRUE(displayIdInfo->deviceProductInfo);
const auto& info = *displayIdInfo->deviceProductInfo;
- EXPECT_STREQ("HP ZR30w", info.name.data());
+ EXPECT_EQ("HP ZR30w", info.name);
EXPECT_STREQ("HWP", info.manufacturerPnpId.data());
- EXPECT_STREQ("10348", info.productId.data());
+ EXPECT_EQ("10348", info.productId);
ASSERT_TRUE(std::holds_alternative<ManufactureWeekAndYear>(info.manufactureOrModelDate));
const auto& date = std::get<ManufactureWeekAndYear>(info.manufactureOrModelDate);
EXPECT_EQ(2012, date.year);
EXPECT_EQ(2, date.week);
- EXPECT_EQ(DeviceProductInfo::NO_RELATIVE_ADDRESS, info.relativeAddress);
+ EXPECT_TRUE(info.relativeAddress.empty());
}
{
const auto displayIdInfo = parseDisplayIdentificationData(0, getExternalEedid());
ASSERT_TRUE(displayIdInfo);
ASSERT_TRUE(displayIdInfo->deviceProductInfo);
const auto& info = *displayIdInfo->deviceProductInfo;
- EXPECT_STREQ("SAMSUNG", info.name.data());
+ EXPECT_EQ("SAMSUNG", info.name);
EXPECT_STREQ("SAM", info.manufacturerPnpId.data());
- EXPECT_STREQ("2302", info.productId.data());
+ EXPECT_EQ("2302", info.productId);
ASSERT_TRUE(std::holds_alternative<ManufactureWeekAndYear>(info.manufactureOrModelDate));
const auto& date = std::get<ManufactureWeekAndYear>(info.manufactureOrModelDate);
EXPECT_EQ(2011, date.year);
EXPECT_EQ(41, date.week);
- EXPECT_EQ((RelativeAddress{{2, 0, 0, 0}}), info.relativeAddress);
+ EXPECT_THAT(info.relativeAddress, ElementsAre(2, 0, 0, 0));
}
{
const auto displayIdInfo = parseDisplayIdentificationData(0, getPanasonicTvEdid());
ASSERT_TRUE(displayIdInfo);
ASSERT_TRUE(displayIdInfo->deviceProductInfo);
const auto& info = *displayIdInfo->deviceProductInfo;
- EXPECT_STREQ("Panasonic-TV", info.name.data());
+ EXPECT_EQ("Panasonic-TV", info.name);
EXPECT_STREQ("MEI", info.manufacturerPnpId.data());
- EXPECT_STREQ("41622", info.productId.data());
+ EXPECT_EQ("41622", info.productId);
ASSERT_TRUE(std::holds_alternative<ManufactureYear>(info.manufactureOrModelDate));
const auto& date = std::get<ManufactureYear>(info.manufactureOrModelDate);
EXPECT_EQ(2019, date.year);
- EXPECT_EQ((RelativeAddress{{2, 0, 0, 0}}), info.relativeAddress);
+ EXPECT_THAT(info.relativeAddress, ElementsAre(2, 0, 0, 0));
}
{
const auto displayIdInfo = parseDisplayIdentificationData(0, getHisenseTvEdid());
ASSERT_TRUE(displayIdInfo);
ASSERT_TRUE(displayIdInfo->deviceProductInfo);
const auto& info = *displayIdInfo->deviceProductInfo;
- EXPECT_STREQ("Hisense", info.name.data());
+ EXPECT_EQ("Hisense", info.name);
EXPECT_STREQ("HEC", info.manufacturerPnpId.data());
- EXPECT_STREQ("0", info.productId.data());
+ EXPECT_EQ("0", info.productId);
ASSERT_TRUE(std::holds_alternative<ManufactureWeekAndYear>(info.manufactureOrModelDate));
const auto& date = std::get<ManufactureWeekAndYear>(info.manufactureOrModelDate);
EXPECT_EQ(2019, date.year);
EXPECT_EQ(18, date.week);
- EXPECT_EQ((RelativeAddress{{1, 2, 3, 4}}), info.relativeAddress);
+ EXPECT_THAT(info.relativeAddress, ElementsAre(1, 2, 3, 4));
}
{
const auto displayIdInfo = parseDisplayIdentificationData(0, getCtlDisplayEdid());
ASSERT_TRUE(displayIdInfo);
ASSERT_TRUE(displayIdInfo->deviceProductInfo);
const auto& info = *displayIdInfo->deviceProductInfo;
- EXPECT_STREQ("LP2361", info.name.data());
+ EXPECT_EQ("LP2361", info.name);
EXPECT_STREQ("CTL", info.manufacturerPnpId.data());
- EXPECT_STREQ("9373", info.productId.data());
+ EXPECT_EQ("9373", info.productId);
ASSERT_TRUE(std::holds_alternative<ModelYear>(info.manufactureOrModelDate));
EXPECT_EQ(2013, std::get<ModelYear>(info.manufactureOrModelDate).year);
- EXPECT_EQ(DeviceProductInfo::NO_RELATIVE_ADDRESS, info.relativeAddress);
+ EXPECT_TRUE(info.relativeAddress.empty());
}
}
-TEST(DisplayIdentificationTest, getFallbackDisplayId) {
+TEST(DisplayIdentificationTest, fromPort) {
// Manufacturer ID should be invalid.
- ASSERT_FALSE(getPnpId(getFallbackDisplayId(0)));
- ASSERT_FALSE(getPnpId(getFallbackDisplayId(0xffu)));
+ ASSERT_FALSE(getPnpId(PhysicalDisplayId::fromPort(0)));
+ ASSERT_FALSE(getPnpId(PhysicalDisplayId::fromPort(0xffu)));
}
TEST(DisplayIdentificationTest, getVirtualDisplayId) {
@@ -408,3 +413,6 @@
}
} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 0323778..a3e8108 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -14,154 +14,22 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#undef LOG_TAG
#define LOG_TAG "LibSurfaceFlingerUnittests"
-#include <type_traits>
-
-#include <compositionengine/Display.h>
-#include <compositionengine/DisplayColorProfile.h>
-#include <compositionengine/impl/Display.h>
-#include <compositionengine/impl/OutputCompositionState.h>
-#include <compositionengine/mock/Display.h>
-#include <compositionengine/mock/DisplayColorProfile.h>
-#include <compositionengine/mock/DisplaySurface.h>
-#include <compositionengine/mock/RenderSurface.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <gui/mock/GraphicBufferConsumer.h>
-#include <gui/mock/GraphicBufferProducer.h>
-#include <log/log.h>
-#include <renderengine/mock/RenderEngine.h>
-#include <ui/DebugUtils.h>
-
-#include "DisplayIdentificationTest.h"
-#include "TestableScheduler.h"
-#include "TestableSurfaceFlinger.h"
-#include "mock/DisplayHardware/MockComposer.h"
-#include "mock/DisplayHardware/MockPowerAdvisor.h"
-#include "mock/MockDispSync.h"
-#include "mock/MockEventControlThread.h"
-#include "mock/MockEventThread.h"
-#include "mock/MockMessageQueue.h"
-#include "mock/MockNativeWindowSurface.h"
-#include "mock/MockSurfaceInterceptor.h"
-#include "mock/system/window/MockNativeWindow.h"
+#include "DisplayTransactionTestHelpers.h"
namespace android {
-namespace {
-namespace hal = android::hardware::graphics::composer::hal;
-
-using testing::_;
using testing::AnyNumber;
using testing::DoAll;
using testing::Mock;
-using testing::ResultOf;
using testing::Return;
-using testing::ReturnRefOfCopy;
using testing::SetArgPointee;
-using testing::StrictMock;
-using hal::ColorMode;
-using hal::Connection;
-using hal::DisplayCapability;
-using hal::DisplayType;
-using hal::Error;
-using hal::Hdr;
-using hal::HWDisplayId;
-using hal::IComposer;
-using hal::IComposerClient;
-using hal::PerFrameMetadataKey;
-using hal::PowerMode;
-using hal::RenderIntent;
+using android::hardware::graphics::composer::hal::HWDisplayId;
using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
-using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
-using HotplugEvent = TestableSurfaceFlinger::HotplugEvent;
-using HWC2Display = TestableSurfaceFlinger::HWC2Display;
-
-constexpr int32_t DEFAULT_REFRESH_RATE = 16'666'667;
-constexpr int32_t DEFAULT_DPI = 320;
-constexpr int DEFAULT_VIRTUAL_DISPLAY_SURFACE_FORMAT = HAL_PIXEL_FORMAT_RGB_565;
-
-constexpr int POWER_MODE_LEET = 1337; // An out of range power mode value
-
-/* ------------------------------------------------------------------------
- * Boolean avoidance
- *
- * To make calls and template instantiations more readable, we define some
- * local enums along with an implicit bool conversion.
- */
-
-#define BOOL_SUBSTITUTE(TYPENAME) enum class TYPENAME : bool { FALSE = false, TRUE = true };
-
-BOOL_SUBSTITUTE(Async);
-BOOL_SUBSTITUTE(Critical);
-BOOL_SUBSTITUTE(Primary);
-BOOL_SUBSTITUTE(Secure);
-BOOL_SUBSTITUTE(Virtual);
-
-/* ------------------------------------------------------------------------
- *
- */
-
-class DisplayTransactionTest : public testing::Test {
-public:
- DisplayTransactionTest();
- ~DisplayTransactionTest() override;
-
- // --------------------------------------------------------------------
- // Mock/Fake injection
-
- void injectMockScheduler();
- void injectMockComposer(int virtualDisplayCount);
- void injectFakeBufferQueueFactory();
- void injectFakeNativeWindowSurfaceFactory();
- sp<DisplayDevice> injectDefaultInternalDisplay(std::function<void(FakeDisplayDeviceInjector&)>);
-
- // --------------------------------------------------------------------
- // Postcondition helpers
-
- bool hasPhysicalHwcDisplay(HWDisplayId hwcDisplayId);
- bool hasTransactionFlagSet(int flag);
- bool hasDisplayDevice(sp<IBinder> displayToken);
- sp<DisplayDevice> getDisplayDevice(sp<IBinder> displayToken);
- bool hasCurrentDisplayState(sp<IBinder> displayToken);
- const DisplayDeviceState& getCurrentDisplayState(sp<IBinder> displayToken);
- bool hasDrawingDisplayState(sp<IBinder> displayToken);
- const DisplayDeviceState& getDrawingDisplayState(sp<IBinder> displayToken);
-
- // --------------------------------------------------------------------
- // Test instances
-
- TestableSurfaceFlinger mFlinger;
- sp<mock::NativeWindow> mNativeWindow = new mock::NativeWindow();
- sp<GraphicBuffer> mBuffer = new GraphicBuffer();
- Hwc2::mock::PowerAdvisor mPowerAdvisor;
-
- // These mocks are created by the test, but are destroyed by SurfaceFlinger
- // by virtue of being stored into a std::unique_ptr. However we still need
- // to keep a reference to them for use in setting up call expectations.
- renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
- Hwc2::mock::Composer* mComposer = nullptr;
- mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
- mock::SurfaceInterceptor* mSurfaceInterceptor = new mock::SurfaceInterceptor();
-
- mock::DispSync* mPrimaryDispSync = new mock::DispSync;
- mock::EventControlThread* mEventControlThread = new mock::EventControlThread;
- mock::EventThread* mEventThread = new mock::EventThread;
- mock::EventThread* mSFEventThread = new mock::EventThread;
-
- // These mocks are created only when expected to be created via a factory.
- sp<mock::GraphicBufferConsumer> mConsumer;
- sp<mock::GraphicBufferProducer> mProducer;
- surfaceflinger::mock::NativeWindowSurface* mNativeWindowSurface = nullptr;
-};
DisplayTransactionTest::DisplayTransactionTest() {
const ::testing::TestInfo* const test_info =
@@ -188,7 +56,7 @@
injectMockScheduler();
mFlinger.mutableEventQueue().reset(mMessageQueue);
mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
- mFlinger.mutableInterceptor().reset(mSurfaceInterceptor);
+ mFlinger.mutableInterceptor() = mSurfaceInterceptor;
injectMockComposer(0);
}
@@ -202,18 +70,18 @@
void DisplayTransactionTest::injectMockScheduler() {
EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*mEventThread, createEventConnection(_, _))
- .WillOnce(Return(new EventThreadConnection(mEventThread, ResyncCallback(),
- ISurfaceComposer::eConfigChangedSuppress)));
+ .WillOnce(Return(
+ new EventThreadConnection(mEventThread, /*callingUid=*/0, ResyncCallback())));
EXPECT_CALL(*mSFEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*mSFEventThread, createEventConnection(_, _))
- .WillOnce(Return(new EventThreadConnection(mSFEventThread, ResyncCallback(),
- ISurfaceComposer::eConfigChangedSuppress)));
+ .WillOnce(Return(
+ new EventThreadConnection(mSFEventThread, /*callingUid=*/0, ResyncCallback())));
- mFlinger.setupScheduler(std::unique_ptr<DispSync>(mPrimaryDispSync),
- std::unique_ptr<EventControlThread>(mEventControlThread),
+ mFlinger.setupScheduler(std::unique_ptr<scheduler::VsyncController>(mVsyncController),
+ std::unique_ptr<scheduler::VSyncTracker>(mVSyncTracker),
std::unique_ptr<EventThread>(mEventThread),
- std::unique_ptr<EventThread>(mSFEventThread));
+ std::unique_ptr<EventThread>(mSFEventThread), &mSchedulerCallback);
}
void DisplayTransactionTest::injectMockComposer(int virtualDisplayCount) {
@@ -250,7 +118,7 @@
sp<DisplayDevice> DisplayTransactionTest::injectDefaultInternalDisplay(
std::function<void(FakeDisplayDeviceInjector&)> injectExtra) {
- constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{777};
+ constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID(777);
constexpr int DEFAULT_DISPLAY_WIDTH = 1080;
constexpr int DEFAULT_DISPLAY_HEIGHT = 1920;
constexpr HWDisplayId DEFAULT_DISPLAY_HWC_DISPLAY_ID = 0;
@@ -271,14 +139,14 @@
createDisplay(mFlinger.getCompositionEngine(),
compositionengine::DisplayCreationArgsBuilder()
.setPhysical(
- {DEFAULT_DISPLAY_ID, DisplayConnectionType::Internal})
+ {DEFAULT_DISPLAY_ID, ui::DisplayConnectionType::Internal})
.setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
.setPowerAdvisor(&mPowerAdvisor)
.build());
- auto injector =
- FakeDisplayDeviceInjector(mFlinger, compositionDisplay, DisplayConnectionType::Internal,
- DEFAULT_DISPLAY_HWC_DISPLAY_ID, true /* isPrimary */);
+ auto injector = FakeDisplayDeviceInjector(mFlinger, compositionDisplay,
+ ui::DisplayConnectionType::Internal,
+ DEFAULT_DISPLAY_HWC_DISPLAY_ID, true /* isPrimary */);
injector.setNativeWindow(mNativeWindow);
if (injectExtra) {
@@ -324,3238 +192,4 @@
return mFlinger.mutableDrawingState().displays.valueFor(displayToken);
}
-/* ------------------------------------------------------------------------
- *
- */
-
-template <typename PhysicalDisplay>
-struct PhysicalDisplayId {};
-
-template <DisplayId::Type displayId>
-using VirtualDisplayId = std::integral_constant<DisplayId::Type, displayId>;
-
-struct NoDisplayId {};
-
-template <typename>
-struct IsPhysicalDisplayId : std::bool_constant<false> {};
-
-template <typename PhysicalDisplay>
-struct IsPhysicalDisplayId<PhysicalDisplayId<PhysicalDisplay>> : std::bool_constant<true> {};
-
-template <typename>
-struct DisplayIdGetter;
-
-template <typename PhysicalDisplay>
-struct DisplayIdGetter<PhysicalDisplayId<PhysicalDisplay>> {
- static std::optional<DisplayId> get() {
- if (!PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
- return getFallbackDisplayId(static_cast<bool>(PhysicalDisplay::PRIMARY)
- ? LEGACY_DISPLAY_TYPE_PRIMARY
- : LEGACY_DISPLAY_TYPE_EXTERNAL);
- }
-
- const auto info =
- parseDisplayIdentificationData(PhysicalDisplay::PORT,
- PhysicalDisplay::GET_IDENTIFICATION_DATA());
- return info ? std::make_optional(info->id) : std::nullopt;
- }
-};
-
-template <DisplayId::Type displayId>
-struct DisplayIdGetter<VirtualDisplayId<displayId>> {
- static std::optional<DisplayId> get() { return DisplayId{displayId}; }
-};
-
-template <>
-struct DisplayIdGetter<NoDisplayId> {
- static std::optional<DisplayId> get() { return {}; }
-};
-
-template <typename>
-struct DisplayConnectionTypeGetter {
- static constexpr std::optional<DisplayConnectionType> value;
-};
-
-template <typename PhysicalDisplay>
-struct DisplayConnectionTypeGetter<PhysicalDisplayId<PhysicalDisplay>> {
- static constexpr std::optional<DisplayConnectionType> value = PhysicalDisplay::CONNECTION_TYPE;
-};
-
-template <typename>
-struct HwcDisplayIdGetter {
- static constexpr std::optional<HWDisplayId> value;
-};
-
-constexpr HWDisplayId HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID = 1010;
-
-template <DisplayId::Type displayId>
-struct HwcDisplayIdGetter<VirtualDisplayId<displayId>> {
- static constexpr std::optional<HWDisplayId> value = HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID;
-};
-
-template <typename PhysicalDisplay>
-struct HwcDisplayIdGetter<PhysicalDisplayId<PhysicalDisplay>> {
- static constexpr std::optional<HWDisplayId> value = PhysicalDisplay::HWC_DISPLAY_ID;
-};
-
-// DisplayIdType can be:
-// 1) PhysicalDisplayId<...> for generated ID of physical display backed by HWC.
-// 2) VirtualDisplayId<...> for hard-coded ID of virtual display backed by HWC.
-// 3) NoDisplayId for virtual display without HWC backing.
-template <typename DisplayIdType, int width, int height, Critical critical, Async async,
- Secure secure, Primary primary, int grallocUsage>
-struct DisplayVariant {
- using DISPLAY_ID = DisplayIdGetter<DisplayIdType>;
- using CONNECTION_TYPE = DisplayConnectionTypeGetter<DisplayIdType>;
- using HWC_DISPLAY_ID_OPT = HwcDisplayIdGetter<DisplayIdType>;
-
- // The display width and height
- static constexpr int WIDTH = width;
- static constexpr int HEIGHT = height;
-
- static constexpr int GRALLOC_USAGE = grallocUsage;
-
- // Whether the display is virtual or physical
- static constexpr Virtual VIRTUAL =
- IsPhysicalDisplayId<DisplayIdType>{} ? Virtual::FALSE : Virtual::TRUE;
-
- // When creating native window surfaces for the framebuffer, whether those should be critical
- static constexpr Critical CRITICAL = critical;
-
- // When creating native window surfaces for the framebuffer, whether those should be async
- static constexpr Async ASYNC = async;
-
- // Whether the display should be treated as secure
- static constexpr Secure SECURE = secure;
-
- // Whether the display is primary
- static constexpr Primary PRIMARY = primary;
-
- static auto makeFakeExistingDisplayInjector(DisplayTransactionTest* test) {
- auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder();
- if (auto displayId = DISPLAY_ID::get()) {
- ceDisplayArgs.setPhysical({*displayId, DisplayConnectionType::Internal});
- } else {
- ceDisplayArgs.setUseHwcVirtualDisplays(false);
- }
- ceDisplayArgs.setPixels({WIDTH, HEIGHT}).setPowerAdvisor(&test->mPowerAdvisor).build();
-
- auto compositionDisplay =
- compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
- ceDisplayArgs.build());
-
- auto injector = FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay,
- CONNECTION_TYPE::value, HWC_DISPLAY_ID_OPT::value,
- static_cast<bool>(PRIMARY));
-
- injector.setSecure(static_cast<bool>(SECURE));
- injector.setNativeWindow(test->mNativeWindow);
-
- // Creating a DisplayDevice requires getting default dimensions from the
- // native window along with some other initial setup.
- EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(WIDTH), Return(0)));
- EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(HEIGHT), Return(0)));
- EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT))
- .WillRepeatedly(Return(0));
- EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT))
- .WillRepeatedly(Return(0));
- EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64))
- .WillRepeatedly(Return(0));
- EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT))
- .WillRepeatedly(Return(0));
-
- return injector;
- }
-
- // Called by tests to set up any native window creation call expectations.
- static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mNativeWindowSurface, getNativeWindow())
- .WillOnce(Return(test->mNativeWindow));
-
- EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(WIDTH), Return(0)));
- EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(HEIGHT), Return(0)));
- EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT))
- .WillRepeatedly(Return(0));
- EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT))
- .WillRepeatedly(Return(0));
- EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64))
- .WillRepeatedly(Return(0));
- EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT))
- .WillRepeatedly(Return(0));
- }
-
- static void setupFramebufferConsumerBufferQueueCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mConsumer, consumerConnect(_, false)).WillOnce(Return(NO_ERROR));
- EXPECT_CALL(*test->mConsumer, setConsumerName(_)).WillRepeatedly(Return(NO_ERROR));
- EXPECT_CALL(*test->mConsumer, setConsumerUsageBits(GRALLOC_USAGE))
- .WillRepeatedly(Return(NO_ERROR));
- EXPECT_CALL(*test->mConsumer, setDefaultBufferSize(WIDTH, HEIGHT))
- .WillRepeatedly(Return(NO_ERROR));
- EXPECT_CALL(*test->mConsumer, setMaxAcquiredBufferCount(_))
- .WillRepeatedly(Return(NO_ERROR));
- }
-
- static void setupFramebufferProducerBufferQueueCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mProducer, allocateBuffers(0, 0, 0, 0)).WillRepeatedly(Return());
- }
-};
-
-template <HWDisplayId hwcDisplayId, DisplayType hwcDisplayType, typename DisplayVariant,
- typename PhysicalDisplay = void>
-struct HwcDisplayVariant {
- // The display id supplied by the HWC
- static constexpr HWDisplayId HWC_DISPLAY_ID = hwcDisplayId;
-
- // The HWC display type
- static constexpr DisplayType HWC_DISPLAY_TYPE = hwcDisplayType;
-
- // The HWC active configuration id
- static constexpr int HWC_ACTIVE_CONFIG_ID = 2001;
- static constexpr PowerMode INIT_POWER_MODE = PowerMode::ON;
-
- static void injectPendingHotplugEvent(DisplayTransactionTest* test, Connection connection) {
- test->mFlinger.mutablePendingHotplugEvents().emplace_back(
- HotplugEvent{HWC_DISPLAY_ID, connection});
- }
-
- // Called by tests to inject a HWC display setup
- static void injectHwcDisplayWithNoDefaultCapabilities(DisplayTransactionTest* test) {
- const auto displayId = DisplayVariant::DISPLAY_ID::get();
- ASSERT_TRUE(displayId);
- FakeHwcDisplayInjector(*displayId, HWC_DISPLAY_TYPE,
- static_cast<bool>(DisplayVariant::PRIMARY))
- .setHwcDisplayId(HWC_DISPLAY_ID)
- .setWidth(DisplayVariant::WIDTH)
- .setHeight(DisplayVariant::HEIGHT)
- .setActiveConfig(HWC_ACTIVE_CONFIG_ID)
- .setPowerMode(INIT_POWER_MODE)
- .inject(&test->mFlinger, test->mComposer);
- }
-
- // Called by tests to inject a HWC display setup
- static void injectHwcDisplay(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})),
- Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, INIT_POWER_MODE))
- .WillOnce(Return(Error::NONE));
- injectHwcDisplayWithNoDefaultCapabilities(test);
- }
-
- static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
- DisplayTransactionTest* test) {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
-
- auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
- .setPhysical({*DisplayVariant::DISPLAY_ID::get(),
- PhysicalDisplay::CONNECTION_TYPE})
- .setPixels({DisplayVariant::WIDTH, DisplayVariant::HEIGHT})
- .setIsSecure(static_cast<bool>(DisplayVariant::SECURE))
- .setPowerAdvisor(&test->mPowerAdvisor)
- .setName(std::string("Injected display for ") +
- test_info->test_case_name() + "." + test_info->name())
- .build();
-
- return compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
- ceDisplayArgs);
- }
-
- static void setupHwcHotplugCallExpectations(DisplayTransactionTest* test) {
- constexpr auto CONNECTION_TYPE =
- PhysicalDisplay::CONNECTION_TYPE == DisplayConnectionType::Internal
- ? IComposerClient::DisplayConnectionType::INTERNAL
- : IComposerClient::DisplayConnectionType::EXTERNAL;
-
- EXPECT_CALL(*test->mComposer, getDisplayConnectionType(HWC_DISPLAY_ID, _))
- .WillOnce(DoAll(SetArgPointee<1>(CONNECTION_TYPE), Return(hal::V2_4::Error::NONE)));
-
- EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_))
- .WillOnce(Return(hal::Error::NONE));
- EXPECT_CALL(*test->mComposer, getDisplayConfigs(HWC_DISPLAY_ID, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<unsigned>{HWC_ACTIVE_CONFIG_ID}),
- Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer,
- getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
- IComposerClient::Attribute::WIDTH, _))
- .WillOnce(DoAll(SetArgPointee<3>(DisplayVariant::WIDTH), Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer,
- getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
- IComposerClient::Attribute::HEIGHT, _))
- .WillOnce(DoAll(SetArgPointee<3>(DisplayVariant::HEIGHT), Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer,
- getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
- IComposerClient::Attribute::VSYNC_PERIOD, _))
- .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_REFRESH_RATE), Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer,
- getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
- IComposerClient::Attribute::DPI_X, _))
- .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer,
- getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
- IComposerClient::Attribute::DPI_Y, _))
- .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer,
- getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
- IComposerClient::Attribute::CONFIG_GROUP, _))
- .WillOnce(DoAll(SetArgPointee<3>(-1), Return(Error::NONE)));
-
- if (PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
- EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _))
- .WillOnce(DoAll(SetArgPointee<1>(PhysicalDisplay::PORT),
- SetArgPointee<2>(PhysicalDisplay::GET_IDENTIFICATION_DATA()),
- Return(Error::NONE)));
- } else {
- EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _))
- .WillOnce(Return(Error::UNSUPPORTED));
- }
- }
-
- // Called by tests to set up HWC call expectations
- static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getActiveConfig(HWC_DISPLAY_ID, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(HWC_ACTIVE_CONFIG_ID), Return(Error::NONE)));
- }
-};
-
-// Physical displays are expected to be synchronous, secure, and have a HWC display for output.
-constexpr uint32_t GRALLOC_USAGE_PHYSICAL_DISPLAY =
- GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_FB;
-
-template <typename PhysicalDisplay, int width, int height, Critical critical>
-struct PhysicalDisplayVariant
- : DisplayVariant<PhysicalDisplayId<PhysicalDisplay>, width, height, critical, Async::FALSE,
- Secure::TRUE, PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY>,
- HwcDisplayVariant<PhysicalDisplay::HWC_DISPLAY_ID, DisplayType::PHYSICAL,
- DisplayVariant<PhysicalDisplayId<PhysicalDisplay>, width, height,
- critical, Async::FALSE, Secure::TRUE,
- PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY>,
- PhysicalDisplay> {};
-
-template <bool hasIdentificationData>
-struct PrimaryDisplay {
- static constexpr auto CONNECTION_TYPE = DisplayConnectionType::Internal;
- static constexpr Primary PRIMARY = Primary::TRUE;
- static constexpr uint8_t PORT = 255;
- static constexpr HWDisplayId HWC_DISPLAY_ID = 1001;
- static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
- static constexpr auto GET_IDENTIFICATION_DATA = getInternalEdid;
-};
-
-template <bool hasIdentificationData>
-struct ExternalDisplay {
- static constexpr auto CONNECTION_TYPE = DisplayConnectionType::External;
- static constexpr Primary PRIMARY = Primary::FALSE;
- static constexpr uint8_t PORT = 254;
- static constexpr HWDisplayId HWC_DISPLAY_ID = 1002;
- static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
- static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
-};
-
-struct TertiaryDisplay {
- static constexpr Primary PRIMARY = Primary::FALSE;
- static constexpr uint8_t PORT = 253;
- static constexpr HWDisplayId HWC_DISPLAY_ID = 1003;
- static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
-};
-
-// A primary display is a physical display that is critical
-using PrimaryDisplayVariant =
- PhysicalDisplayVariant<PrimaryDisplay<false>, 3840, 2160, Critical::TRUE>;
-
-// An external display is physical display that is not critical.
-using ExternalDisplayVariant =
- PhysicalDisplayVariant<ExternalDisplay<false>, 1920, 1280, Critical::FALSE>;
-
-using TertiaryDisplayVariant = PhysicalDisplayVariant<TertiaryDisplay, 1600, 1200, Critical::FALSE>;
-
-// A virtual display not supported by the HWC.
-constexpr uint32_t GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY = 0;
-
-template <int width, int height, Secure secure>
-struct NonHwcVirtualDisplayVariant
- : DisplayVariant<NoDisplayId, width, height, Critical::FALSE, Async::TRUE, secure,
- Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY> {
- using Base = DisplayVariant<NoDisplayId, width, height, Critical::FALSE, Async::TRUE, secure,
- Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY>;
-
- static void injectHwcDisplay(DisplayTransactionTest*) {}
-
- static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
- DisplayTransactionTest* test) {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
-
- auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
- .setPixels({Base::WIDTH, Base::HEIGHT})
- .setIsSecure(static_cast<bool>(Base::SECURE))
- .setPowerAdvisor(&test->mPowerAdvisor)
- .setName(std::string("Injected display for ") +
- test_info->test_case_name() + "." + test_info->name())
- .build();
-
- return compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
- ceDisplayArgs);
- }
-
- static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getActiveConfig(_, _)).Times(0);
- }
-
- static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
- Base::setupNativeWindowSurfaceCreationCallExpectations(test);
- EXPECT_CALL(*test->mNativeWindow, setSwapInterval(0)).Times(1);
- }
-};
-
-// A virtual display supported by the HWC.
-constexpr uint32_t GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY = GRALLOC_USAGE_HW_COMPOSER;
-
-template <int width, int height, Secure secure>
-struct HwcVirtualDisplayVariant
- : DisplayVariant<VirtualDisplayId<42>, width, height, Critical::FALSE, Async::TRUE, secure,
- Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>,
- HwcDisplayVariant<
- HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID, DisplayType::VIRTUAL,
- DisplayVariant<VirtualDisplayId<42>, width, height, Critical::FALSE, Async::TRUE,
- secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>> {
- using Base = DisplayVariant<VirtualDisplayId<42>, width, height, Critical::FALSE, Async::TRUE,
- secure, Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER>;
- using Self = HwcVirtualDisplayVariant<width, height, secure>;
-
- static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
- DisplayTransactionTest* test) {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
-
- auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
- .setUseHwcVirtualDisplays(false)
- .setPixels({Base::WIDTH, Base::HEIGHT})
- .setIsSecure(static_cast<bool>(Base::SECURE))
- .setPowerAdvisor(&test->mPowerAdvisor)
- .setName(std::string("Injected display for ") +
- test_info->test_case_name() + "." + test_info->name())
- .build();
-
- auto compositionDisplay =
- compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
- ceDisplayArgs);
- compositionDisplay->setDisplayIdForTesting(Base::DISPLAY_ID::get());
-
- // Insert display data so that the HWC thinks it created the virtual display.
- if (const auto displayId = Base::DISPLAY_ID::get()) {
- test->mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
- }
-
- return compositionDisplay;
- }
-
- static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
- Base::setupNativeWindowSurfaceCreationCallExpectations(test);
- EXPECT_CALL(*test->mNativeWindow, setSwapInterval(0)).Times(1);
- }
-
- static void setupHwcVirtualDisplayCreationCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, createVirtualDisplay(Base::WIDTH, Base::HEIGHT, _, _))
- .WillOnce(DoAll(SetArgPointee<3>(Self::HWC_DISPLAY_ID), Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_)).WillOnce(Return(Error::NONE));
- }
-};
-
-// For this variant, SurfaceFlinger should not configure itself with wide
-// display support, so the display should not be configured for wide-color
-// support.
-struct WideColorSupportNotConfiguredVariant {
- static constexpr bool WIDE_COLOR_SUPPORTED = false;
-
- static void injectConfigChange(DisplayTransactionTest* test) {
- test->mFlinger.mutableHasWideColorDisplay() = false;
- test->mFlinger.mutableUseColorManagement() = false;
- test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
- }
-
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getColorModes(_, _)).Times(0);
- EXPECT_CALL(*test->mComposer, getRenderIntents(_, _, _)).Times(0);
- EXPECT_CALL(*test->mComposer, setColorMode(_, _, _)).Times(0);
- }
-};
-
-// For this variant, SurfaceFlinger should configure itself with wide display
-// support, and the display should respond with an non-empty list of supported
-// color modes. Wide-color support should be configured.
-template <typename Display>
-struct WideColorP3ColorimetricSupportedVariant {
- static constexpr bool WIDE_COLOR_SUPPORTED = true;
-
- static void injectConfigChange(DisplayTransactionTest* test) {
- test->mFlinger.mutableUseColorManagement() = true;
- test->mFlinger.mutableHasWideColorDisplay() = true;
- test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
- }
-
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_DATASPACE)).Times(1);
-
- EXPECT_CALL(*test->mComposer, getColorModes(Display::HWC_DISPLAY_ID, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<ColorMode>({ColorMode::DISPLAY_P3})),
- Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer,
- getRenderIntents(Display::HWC_DISPLAY_ID, ColorMode::DISPLAY_P3, _))
- .WillOnce(DoAll(SetArgPointee<2>(
- std::vector<RenderIntent>({RenderIntent::COLORIMETRIC})),
- Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer,
- setColorMode(Display::HWC_DISPLAY_ID, ColorMode::SRGB,
- RenderIntent::COLORIMETRIC))
- .WillOnce(Return(Error::NONE));
- }
-};
-
-// For this variant, SurfaceFlinger should configure itself with wide display
-// support, but the display should respond with an empty list of supported color
-// modes. Wide-color support for the display should not be configured.
-template <typename Display>
-struct WideColorNotSupportedVariant {
- static constexpr bool WIDE_COLOR_SUPPORTED = false;
-
- static void injectConfigChange(DisplayTransactionTest* test) {
- test->mFlinger.mutableUseColorManagement() = true;
- test->mFlinger.mutableHasWideColorDisplay() = true;
- }
-
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getColorModes(Display::HWC_DISPLAY_ID, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<ColorMode>()), Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer, setColorMode(_, _, _)).Times(0);
- }
-};
-
-// For this variant, the display is not a HWC display, so no HDR support should
-// be configured.
-struct NonHwcDisplayHdrSupportVariant {
- static constexpr bool HDR10_PLUS_SUPPORTED = false;
- static constexpr bool HDR10_SUPPORTED = false;
- static constexpr bool HDR_HLG_SUPPORTED = false;
- static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getHdrCapabilities(_, _, _, _, _)).Times(0);
- }
-};
-
-template <typename Display>
-struct Hdr10PlusSupportedVariant {
- static constexpr bool HDR10_PLUS_SUPPORTED = true;
- static constexpr bool HDR10_SUPPORTED = true;
- static constexpr bool HDR_HLG_SUPPORTED = false;
- static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getHdrCapabilities(_, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({
- Hdr::HDR10_PLUS,
- Hdr::HDR10,
- })),
- Return(Error::NONE)));
- }
-};
-
-// For this variant, the composer should respond with a non-empty list of HDR
-// modes containing HDR10, so HDR10 support should be configured.
-template <typename Display>
-struct Hdr10SupportedVariant {
- static constexpr bool HDR10_PLUS_SUPPORTED = false;
- static constexpr bool HDR10_SUPPORTED = true;
- static constexpr bool HDR_HLG_SUPPORTED = false;
- static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({Hdr::HDR10})),
- Return(Error::NONE)));
- }
-};
-
-// For this variant, the composer should respond with a non-empty list of HDR
-// modes containing HLG, so HLG support should be configured.
-template <typename Display>
-struct HdrHlgSupportedVariant {
- static constexpr bool HDR10_PLUS_SUPPORTED = false;
- static constexpr bool HDR10_SUPPORTED = false;
- static constexpr bool HDR_HLG_SUPPORTED = true;
- static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
- .WillOnce(
- DoAll(SetArgPointee<1>(std::vector<Hdr>({Hdr::HLG})), Return(Error::NONE)));
- }
-};
-
-// For this variant, the composer should respond with a non-empty list of HDR
-// modes containing DOLBY_VISION, so DOLBY_VISION support should be configured.
-template <typename Display>
-struct HdrDolbyVisionSupportedVariant {
- static constexpr bool HDR10_PLUS_SUPPORTED = false;
- static constexpr bool HDR10_SUPPORTED = false;
- static constexpr bool HDR_HLG_SUPPORTED = false;
- static constexpr bool HDR_DOLBY_VISION_SUPPORTED = true;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({Hdr::DOLBY_VISION})),
- Return(Error::NONE)));
- }
-};
-
-// For this variant, the composer should respond with am empty list of HDR
-// modes, so no HDR support should be configured.
-template <typename Display>
-struct HdrNotSupportedVariant {
- static constexpr bool HDR10_PLUS_SUPPORTED = false;
- static constexpr bool HDR10_SUPPORTED = false;
- static constexpr bool HDR_HLG_SUPPORTED = false;
- static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>()), Return(Error::NONE)));
- }
-};
-
-struct NonHwcPerFrameMetadataSupportVariant {
- static constexpr int PER_FRAME_METADATA_KEYS = 0;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(_)).Times(0);
- }
-};
-
-template <typename Display>
-struct NoPerFrameMetadataSupportVariant {
- static constexpr int PER_FRAME_METADATA_KEYS = 0;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
- .WillOnce(Return(std::vector<PerFrameMetadataKey>()));
- }
-};
-
-template <typename Display>
-struct Smpte2086PerFrameMetadataSupportVariant {
- static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::SMPTE2086;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
- .WillOnce(Return(std::vector<PerFrameMetadataKey>({
- PerFrameMetadataKey::DISPLAY_RED_PRIMARY_X,
- PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y,
- PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_X,
- PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_Y,
- PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_X,
- PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_Y,
- PerFrameMetadataKey::WHITE_POINT_X,
- PerFrameMetadataKey::WHITE_POINT_Y,
- PerFrameMetadataKey::MAX_LUMINANCE,
- PerFrameMetadataKey::MIN_LUMINANCE,
- })));
- }
-};
-
-template <typename Display>
-struct Cta861_3_PerFrameMetadataSupportVariant {
- static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::CTA861_3;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
- .WillOnce(Return(std::vector<PerFrameMetadataKey>({
- PerFrameMetadataKey::MAX_CONTENT_LIGHT_LEVEL,
- PerFrameMetadataKey::MAX_FRAME_AVERAGE_LIGHT_LEVEL,
- })));
- }
-};
-
-template <typename Display>
-struct Hdr10_Plus_PerFrameMetadataSupportVariant {
- static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::HDR10PLUS;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
- .WillOnce(Return(std::vector<PerFrameMetadataKey>({
- PerFrameMetadataKey::HDR10_PLUS_SEI,
- })));
- }
-};
-/* ------------------------------------------------------------------------
- * Typical display configurations to test
- */
-
-template <typename DisplayPolicy, typename WideColorSupportPolicy, typename HdrSupportPolicy,
- typename PerFrameMetadataSupportPolicy>
-struct Case {
- using Display = DisplayPolicy;
- using WideColorSupport = WideColorSupportPolicy;
- using HdrSupport = HdrSupportPolicy;
- using PerFrameMetadataSupport = PerFrameMetadataSupportPolicy;
-};
-
-using SimplePrimaryDisplayCase =
- Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
- HdrNotSupportedVariant<PrimaryDisplayVariant>,
- NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using SimpleExternalDisplayCase =
- Case<ExternalDisplayVariant, WideColorNotSupportedVariant<ExternalDisplayVariant>,
- HdrNotSupportedVariant<ExternalDisplayVariant>,
- NoPerFrameMetadataSupportVariant<ExternalDisplayVariant>>;
-using SimpleTertiaryDisplayCase =
- Case<TertiaryDisplayVariant, WideColorNotSupportedVariant<TertiaryDisplayVariant>,
- HdrNotSupportedVariant<TertiaryDisplayVariant>,
- NoPerFrameMetadataSupportVariant<TertiaryDisplayVariant>>;
-using NonHwcVirtualDisplayCase =
- Case<NonHwcVirtualDisplayVariant<1024, 768, Secure::FALSE>,
- WideColorSupportNotConfiguredVariant, NonHwcDisplayHdrSupportVariant,
- NonHwcPerFrameMetadataSupportVariant>;
-using SimpleHwcVirtualDisplayVariant = HwcVirtualDisplayVariant<1024, 768, Secure::TRUE>;
-using HwcVirtualDisplayCase =
- Case<SimpleHwcVirtualDisplayVariant, WideColorSupportNotConfiguredVariant,
- HdrNotSupportedVariant<SimpleHwcVirtualDisplayVariant>,
- NoPerFrameMetadataSupportVariant<SimpleHwcVirtualDisplayVariant>>;
-using WideColorP3ColorimetricDisplayCase =
- Case<PrimaryDisplayVariant, WideColorP3ColorimetricSupportedVariant<PrimaryDisplayVariant>,
- HdrNotSupportedVariant<PrimaryDisplayVariant>,
- NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using Hdr10PlusDisplayCase =
- Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
- Hdr10SupportedVariant<PrimaryDisplayVariant>,
- Hdr10_Plus_PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using Hdr10DisplayCase =
- Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
- Hdr10SupportedVariant<PrimaryDisplayVariant>,
- NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using HdrHlgDisplayCase =
- Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
- HdrHlgSupportedVariant<PrimaryDisplayVariant>,
- NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using HdrDolbyVisionDisplayCase =
- Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
- HdrDolbyVisionSupportedVariant<PrimaryDisplayVariant>,
- NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using HdrSmpte2086DisplayCase =
- Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
- HdrNotSupportedVariant<PrimaryDisplayVariant>,
- Smpte2086PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using HdrCta861_3_DisplayCase =
- Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
- HdrNotSupportedVariant<PrimaryDisplayVariant>,
- Cta861_3_PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-
-/* ------------------------------------------------------------------------
- *
- * SurfaceFlinger::onHotplugReceived
- */
-
-TEST_F(DisplayTransactionTest, hotplugEnqueuesEventsForDisplayTransaction) {
- constexpr int currentSequenceId = 123;
- constexpr HWDisplayId hwcDisplayId1 = 456;
- constexpr HWDisplayId hwcDisplayId2 = 654;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // Set the current sequence id for accepted events
- mFlinger.mutableComposerSequenceId() = currentSequenceId;
-
- // Set the main thread id so that the current thread does not appear to be
- // the main thread.
- mFlinger.mutableMainThreadId() = std::thread::id();
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- // We expect invalidate() to be invoked once to trigger display transaction
- // processing.
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-
- // --------------------------------------------------------------------
- // Invocation
-
- // Simulate two hotplug events (a connect and a disconnect)
- mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId1, Connection::CONNECTED);
- mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId2, Connection::DISCONNECTED);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The display transaction needed flag should be set.
- EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
-
- // All events should be in the pending event queue.
- const auto& pendingEvents = mFlinger.mutablePendingHotplugEvents();
- ASSERT_EQ(2u, pendingEvents.size());
- EXPECT_EQ(hwcDisplayId1, pendingEvents[0].hwcDisplayId);
- EXPECT_EQ(Connection::CONNECTED, pendingEvents[0].connection);
- EXPECT_EQ(hwcDisplayId2, pendingEvents[1].hwcDisplayId);
- EXPECT_EQ(Connection::DISCONNECTED, pendingEvents[1].connection);
-}
-
-TEST_F(DisplayTransactionTest, hotplugDiscardsUnexpectedEvents) {
- constexpr int currentSequenceId = 123;
- constexpr int otherSequenceId = 321;
- constexpr HWDisplayId displayId = 456;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // Set the current sequence id for accepted events
- mFlinger.mutableComposerSequenceId() = currentSequenceId;
-
- // Set the main thread id so that the current thread does not appear to be
- // the main thread.
- mFlinger.mutableMainThreadId() = std::thread::id();
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- // We do not expect any calls to invalidate().
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(0);
-
- // --------------------------------------------------------------------
- // Invocation
-
- // Call with an unexpected sequence id
- mFlinger.onHotplugReceived(otherSequenceId, displayId, Connection::INVALID);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The display transaction needed flag should not be set
- EXPECT_FALSE(hasTransactionFlagSet(eDisplayTransactionNeeded));
-
- // There should be no pending events
- EXPECT_TRUE(mFlinger.mutablePendingHotplugEvents().empty());
-}
-
-TEST_F(DisplayTransactionTest, hotplugProcessesEnqueuedEventsIfCalledOnMainThread) {
- constexpr int currentSequenceId = 123;
- constexpr HWDisplayId displayId1 = 456;
-
- // --------------------------------------------------------------------
- // Note:
- // --------------------------------------------------------------------
- // This test case is a bit tricky. We want to verify that
- // onHotplugReceived() calls processDisplayHotplugEventsLocked(), but we
- // don't really want to provide coverage for everything the later function
- // does as there are specific tests for it.
- // --------------------------------------------------------------------
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // Set the current sequence id for accepted events
- mFlinger.mutableComposerSequenceId() = currentSequenceId;
-
- // Set the main thread id so that the current thread does appear to be the
- // main thread.
- mFlinger.mutableMainThreadId() = std::this_thread::get_id();
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- // We expect invalidate() to be invoked once to trigger display transaction
- // processing.
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-
- // --------------------------------------------------------------------
- // Invocation
-
- // Simulate a disconnect on a display id that is not connected. This should
- // be enqueued by onHotplugReceived(), and dequeued by
- // processDisplayHotplugEventsLocked(), but then ignored as invalid.
- mFlinger.onHotplugReceived(currentSequenceId, displayId1, Connection::DISCONNECTED);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The display transaction needed flag should be set.
- EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
-
- // There should be no event queued on return, as it should have been
- // processed.
- EXPECT_TRUE(mFlinger.mutablePendingHotplugEvents().empty());
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::createDisplay
- */
-
-TEST_F(DisplayTransactionTest, createDisplaySetsCurrentStateForNonsecureDisplay) {
- const String8 name("virtual.test");
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- // The call should notify the interceptor that a display was created.
- EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
-
- // --------------------------------------------------------------------
- // Invocation
-
- sp<IBinder> displayToken = mFlinger.createDisplay(name, false);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The display should have been added to the current state
- ASSERT_TRUE(hasCurrentDisplayState(displayToken));
- const auto& display = getCurrentDisplayState(displayToken);
- EXPECT_TRUE(display.isVirtual());
- EXPECT_FALSE(display.isSecure);
- EXPECT_EQ(name.string(), display.displayName);
-
- // --------------------------------------------------------------------
- // Cleanup conditions
-
- // Destroying the display invalidates the display state.
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-}
-
-TEST_F(DisplayTransactionTest, createDisplaySetsCurrentStateForSecureDisplay) {
- const String8 name("virtual.test");
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- // The call should notify the interceptor that a display was created.
- EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
-
- // --------------------------------------------------------------------
- // Invocation
-
- sp<IBinder> displayToken = mFlinger.createDisplay(name, true);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The display should have been added to the current state
- ASSERT_TRUE(hasCurrentDisplayState(displayToken));
- const auto& display = getCurrentDisplayState(displayToken);
- EXPECT_TRUE(display.isVirtual());
- EXPECT_TRUE(display.isSecure);
- EXPECT_EQ(name.string(), display.displayName);
-
- // --------------------------------------------------------------------
- // Cleanup conditions
-
- // Destroying the display invalidates the display state.
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::destroyDisplay
- */
-
-TEST_F(DisplayTransactionTest, destroyDisplayClearsCurrentStateForDisplay) {
- using Case = NonHwcVirtualDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A virtual display exists
- auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
- existing.inject();
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- // The call should notify the interceptor that a display was created.
- EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
-
- // Destroying the display invalidates the display state.
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.destroyDisplay(existing.token());
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The display should have been removed from the current state
- EXPECT_FALSE(hasCurrentDisplayState(existing.token()));
-
- // Ths display should still exist in the drawing state
- EXPECT_TRUE(hasDrawingDisplayState(existing.token()));
-
- // The display transaction needed flasg should be set
- EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
-}
-
-TEST_F(DisplayTransactionTest, destroyDisplayHandlesUnknownDisplay) {
- // --------------------------------------------------------------------
- // Preconditions
-
- sp<BBinder> displayToken = new BBinder();
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.destroyDisplay(displayToken);
-}
-
-/* ------------------------------------------------------------------------
- * DisplayDevice::GetBestColorMode
- */
-class GetBestColorModeTest : public DisplayTransactionTest {
-public:
- void setHasWideColorGamut(bool hasWideColorGamut) { mHasWideColorGamut = hasWideColorGamut; }
-
- void addHwcColorModesMapping(ui::ColorMode colorMode,
- std::vector<ui::RenderIntent> renderIntents) {
- mHwcColorModes[colorMode] = renderIntents;
- }
-
- void setInputDataspace(ui::Dataspace dataspace) { mInputDataspace = dataspace; }
-
- void setInputRenderIntent(ui::RenderIntent renderIntent) { mInputRenderIntent = renderIntent; }
-
- void getBestColorMode() {
- auto displayDevice =
- injectDefaultInternalDisplay([this](FakeDisplayDeviceInjector& injector) {
- injector.setHwcColorModes(mHwcColorModes);
- injector.setHasWideColorGamut(mHasWideColorGamut);
- injector.setNativeWindow(mNativeWindow);
- });
-
- displayDevice->getCompositionDisplay()
- ->getDisplayColorProfile()
- ->getBestColorMode(mInputDataspace, mInputRenderIntent, &mOutDataspace,
- &mOutColorMode, &mOutRenderIntent);
- }
-
- ui::Dataspace mOutDataspace;
- ui::ColorMode mOutColorMode;
- ui::RenderIntent mOutRenderIntent;
-
-private:
- ui::Dataspace mInputDataspace;
- ui::RenderIntent mInputRenderIntent;
- bool mHasWideColorGamut = false;
- std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> mHwcColorModes;
-};
-
-TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeSRGB) {
- addHwcColorModesMapping(ui::ColorMode::SRGB,
- std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
- setInputDataspace(ui::Dataspace::DISPLAY_P3);
- setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
- setHasWideColorGamut(true);
-
- getBestColorMode();
-
- ASSERT_EQ(ui::Dataspace::V0_SRGB, mOutDataspace);
- ASSERT_EQ(ui::ColorMode::SRGB, mOutColorMode);
- ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
-}
-
-TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeDisplayP3) {
- addHwcColorModesMapping(ui::ColorMode::DISPLAY_P3,
- std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
- addHwcColorModesMapping(ui::ColorMode::SRGB,
- std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
- addHwcColorModesMapping(ui::ColorMode::DISPLAY_BT2020,
- std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
- setInputDataspace(ui::Dataspace::DISPLAY_P3);
- setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
- setHasWideColorGamut(true);
-
- getBestColorMode();
-
- ASSERT_EQ(ui::Dataspace::DISPLAY_P3, mOutDataspace);
- ASSERT_EQ(ui::ColorMode::DISPLAY_P3, mOutColorMode);
- ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
-}
-
-TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeDISPLAY_BT2020) {
- addHwcColorModesMapping(ui::ColorMode::DISPLAY_BT2020,
- std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
- setInputDataspace(ui::Dataspace::DISPLAY_P3);
- setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
- setHasWideColorGamut(true);
-
- getBestColorMode();
-
- ASSERT_EQ(ui::Dataspace::DISPLAY_BT2020, mOutDataspace);
- ASSERT_EQ(ui::ColorMode::DISPLAY_BT2020, mOutColorMode);
- ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
-}
-
-/* ------------------------------------------------------------------------
- * DisplayDevice::setProjection
- */
-
-class DisplayDeviceSetProjectionTest : public DisplayTransactionTest {
-public:
- static constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1080; // arbitrary
- static constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1920; // arbitrary
-
- static constexpr int32_t TRANSFORM_FLAGS_ROT_0 = 0;
- static constexpr int32_t TRANSFORM_FLAGS_ROT_90 = HAL_TRANSFORM_ROT_90;
- static constexpr int32_t TRANSFORM_FLAGS_ROT_180 = HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_FLIP_V;
- static constexpr int32_t TRANSFORM_FLAGS_ROT_270 =
- HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_ROT_90;
-
- DisplayDeviceSetProjectionTest(ui::Size flingerDisplaySize, ui::Size hardwareDisplaySize,
- ui::Rotation physicalOrientation)
- : mFlingerDisplaySize(flingerDisplaySize),
- mHardwareDisplaySize(hardwareDisplaySize),
- mPhysicalOrientation(physicalOrientation),
- mDisplayDevice(createDisplayDevice()) {}
-
- sp<DisplayDevice> createDisplayDevice() {
- return injectDefaultInternalDisplay([this](FakeDisplayDeviceInjector& injector) {
- injector.setPhysicalOrientation(mPhysicalOrientation);
- });
- }
-
- ui::Size SwapWH(const ui::Size size) const { return ui::Size(size.height, size.width); }
-
- void setProjectionForRotation0() {
- // A logical rotation of 0 uses the SurfaceFlinger display size
- mDisplayDevice->setProjection(ui::ROTATION_0, Rect(mFlingerDisplaySize),
- Rect(mFlingerDisplaySize));
- }
-
- void setProjectionForRotation90() {
- // A logical rotation of 90 uses the SurfaceFlinger display size with
- // the width/height swapped.
- mDisplayDevice->setProjection(ui::ROTATION_90, Rect(SwapWH(mFlingerDisplaySize)),
- Rect(SwapWH(mFlingerDisplaySize)));
- }
-
- void setProjectionForRotation180() {
- // A logical rotation of 180 uses the SurfaceFlinger display size
- mDisplayDevice->setProjection(ui::ROTATION_180, Rect(mFlingerDisplaySize),
- Rect(mFlingerDisplaySize));
- }
-
- void setProjectionForRotation270() {
- // A logical rotation of 270 uses the SurfaceFlinger display size with
- // the width/height swapped.
- mDisplayDevice->setProjection(ui::ROTATION_270, Rect(SwapWH(mFlingerDisplaySize)),
- Rect(SwapWH(mFlingerDisplaySize)));
- }
-
- void expectStateForHardwareTransform0() {
- const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
- EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_0, mHardwareDisplaySize.width,
- mHardwareDisplaySize.height),
- compositionState.transform);
- EXPECT_EQ(TRANSFORM_FLAGS_ROT_0, compositionState.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.sourceClip);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.frame);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.viewport);
- EXPECT_EQ(false, compositionState.needsFiltering);
- }
-
- void expectStateForHardwareTransform90() {
- const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
- EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_90, mHardwareDisplaySize.width,
- mHardwareDisplaySize.height),
- compositionState.transform);
- EXPECT_EQ(TRANSFORM_FLAGS_ROT_90, compositionState.orientation);
- EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.sourceClip);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
- // For 90, the frame and viewport have the hardware display size width and height swapped
- EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.frame);
- EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.viewport);
- EXPECT_EQ(false, compositionState.needsFiltering);
- }
-
- void expectStateForHardwareTransform180() {
- const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
- EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_180, mHardwareDisplaySize.width,
- mHardwareDisplaySize.height),
- compositionState.transform);
- EXPECT_EQ(TRANSFORM_FLAGS_ROT_180, compositionState.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.sourceClip);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.frame);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.viewport);
- EXPECT_EQ(false, compositionState.needsFiltering);
- }
-
- void expectStateForHardwareTransform270() {
- const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
- EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_270, mHardwareDisplaySize.width,
- mHardwareDisplaySize.height),
- compositionState.transform);
- EXPECT_EQ(TRANSFORM_FLAGS_ROT_270, compositionState.orientation);
- EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.sourceClip);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
- // For 270, the frame and viewport have the hardware display size width and height swapped
- EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.frame);
- EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.viewport);
- EXPECT_EQ(false, compositionState.needsFiltering);
- }
-
- const ui::Size mFlingerDisplaySize;
- const ui::Size mHardwareDisplaySize;
- const ui::Rotation mPhysicalOrientation;
- const sp<DisplayDevice> mDisplayDevice;
-};
-
-struct DisplayDeviceSetProjectionTest_Installed0 : public DisplayDeviceSetProjectionTest {
- DisplayDeviceSetProjectionTest_Installed0()
- : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
- ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
- ui::ROTATION_0) {}
-};
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith0OutputRotation) {
- setProjectionForRotation0();
- expectStateForHardwareTransform0();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith90OutputRotation) {
- setProjectionForRotation90();
- expectStateForHardwareTransform90();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith180OutputRotation) {
- setProjectionForRotation180();
- expectStateForHardwareTransform180();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith270OutputRotation) {
- setProjectionForRotation270();
- expectStateForHardwareTransform270();
-}
-
-struct DisplayDeviceSetProjectionTest_Installed90 : public DisplayDeviceSetProjectionTest {
- DisplayDeviceSetProjectionTest_Installed90()
- : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH),
- ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
- ui::ROTATION_90) {}
-};
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith0OutputRotation) {
- setProjectionForRotation0();
- expectStateForHardwareTransform90();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith90OutputRotation) {
- setProjectionForRotation90();
- expectStateForHardwareTransform180();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith180OutputRotation) {
- setProjectionForRotation180();
- expectStateForHardwareTransform270();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith270OutputRotation) {
- setProjectionForRotation270();
- expectStateForHardwareTransform0();
-}
-
-struct DisplayDeviceSetProjectionTest_Installed180 : public DisplayDeviceSetProjectionTest {
- DisplayDeviceSetProjectionTest_Installed180()
- : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
- ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
- ui::ROTATION_180) {}
-};
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith0OutputRotation) {
- setProjectionForRotation0();
- expectStateForHardwareTransform180();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith90OutputRotation) {
- setProjectionForRotation90();
- expectStateForHardwareTransform270();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith180OutputRotation) {
- setProjectionForRotation180();
- expectStateForHardwareTransform0();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith270OutputRotation) {
- setProjectionForRotation270();
- expectStateForHardwareTransform90();
-}
-
-struct DisplayDeviceSetProjectionTest_Installed270 : public DisplayDeviceSetProjectionTest {
- DisplayDeviceSetProjectionTest_Installed270()
- : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH),
- ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
- ui::ROTATION_270) {}
-};
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith0OutputRotation) {
- setProjectionForRotation0();
- expectStateForHardwareTransform270();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith90OutputRotation) {
- setProjectionForRotation90();
- expectStateForHardwareTransform0();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith180OutputRotation) {
- setProjectionForRotation180();
- expectStateForHardwareTransform90();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith270OutputRotation) {
- setProjectionForRotation270();
- expectStateForHardwareTransform180();
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::getDisplayNativePrimaries
- */
-
-class GetDisplayNativePrimaries : public DisplayTransactionTest {
-public:
- GetDisplayNativePrimaries();
- void populateDummyDisplayNativePrimaries(ui::DisplayPrimaries& primaries);
- void checkDummyDisplayNativePrimaries(const ui::DisplayPrimaries& primaries);
-
-private:
- static constexpr float mStartingTestValue = 1.0f;
-};
-
-GetDisplayNativePrimaries::GetDisplayNativePrimaries() {
- SimplePrimaryDisplayCase::Display::injectHwcDisplay(this);
- injectFakeNativeWindowSurfaceFactory();
-}
-
-void GetDisplayNativePrimaries::populateDummyDisplayNativePrimaries(
- ui::DisplayPrimaries& primaries) {
- float startingVal = mStartingTestValue;
- primaries.red.X = startingVal++;
- primaries.red.Y = startingVal++;
- primaries.red.Z = startingVal++;
- primaries.green.X = startingVal++;
- primaries.green.Y = startingVal++;
- primaries.green.Z = startingVal++;
- primaries.blue.X = startingVal++;
- primaries.blue.Y = startingVal++;
- primaries.blue.Z = startingVal++;
- primaries.white.X = startingVal++;
- primaries.white.Y = startingVal++;
- primaries.white.Z = startingVal++;
-}
-
-void GetDisplayNativePrimaries::checkDummyDisplayNativePrimaries(
- const ui::DisplayPrimaries& primaries) {
- float startingVal = mStartingTestValue;
- EXPECT_EQ(primaries.red.X, startingVal++);
- EXPECT_EQ(primaries.red.Y, startingVal++);
- EXPECT_EQ(primaries.red.Z, startingVal++);
- EXPECT_EQ(primaries.green.X, startingVal++);
- EXPECT_EQ(primaries.green.Y, startingVal++);
- EXPECT_EQ(primaries.green.Z, startingVal++);
- EXPECT_EQ(primaries.blue.X, startingVal++);
- EXPECT_EQ(primaries.blue.Y, startingVal++);
- EXPECT_EQ(primaries.blue.Z, startingVal++);
- EXPECT_EQ(primaries.white.X, startingVal++);
- EXPECT_EQ(primaries.white.Y, startingVal++);
- EXPECT_EQ(primaries.white.Z, startingVal++);
-}
-
-TEST_F(GetDisplayNativePrimaries, nullDisplayToken) {
- ui::DisplayPrimaries primaries;
- EXPECT_EQ(BAD_VALUE, mFlinger.getDisplayNativePrimaries(nullptr, primaries));
-}
-
-TEST_F(GetDisplayNativePrimaries, internalDisplayWithPrimariesData) {
- auto injector = SimplePrimaryDisplayCase::Display::makeFakeExistingDisplayInjector(this);
- injector.inject();
- auto internalDisplayToken = injector.token();
-
- ui::DisplayPrimaries expectedPrimaries;
- populateDummyDisplayNativePrimaries(expectedPrimaries);
- mFlinger.setInternalDisplayPrimaries(expectedPrimaries);
-
- ui::DisplayPrimaries primaries;
- EXPECT_EQ(NO_ERROR, mFlinger.getDisplayNativePrimaries(internalDisplayToken, primaries));
-
- checkDummyDisplayNativePrimaries(primaries);
-}
-
-TEST_F(GetDisplayNativePrimaries, notInternalDisplayToken) {
- sp<BBinder> notInternalDisplayToken = new BBinder();
-
- ui::DisplayPrimaries primaries;
- populateDummyDisplayNativePrimaries(primaries);
- EXPECT_EQ(NAME_NOT_FOUND,
- mFlinger.getDisplayNativePrimaries(notInternalDisplayToken, primaries));
-
- // Check primaries argument wasn't modified in case of failure
- checkDummyDisplayNativePrimaries(primaries);
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::setupNewDisplayDeviceInternal
- */
-
-class SetupNewDisplayDeviceInternalTest : public DisplayTransactionTest {
-public:
- template <typename T>
- void setupNewDisplayDeviceInternalTest();
-};
-
-template <typename Case>
-void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() {
- const sp<BBinder> displayToken = new BBinder();
- const sp<compositionengine::mock::DisplaySurface> displaySurface =
- new compositionengine::mock::DisplaySurface();
- const sp<mock::GraphicBufferProducer> producer = new mock::GraphicBufferProducer();
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // Wide color displays support is configured appropriately
- Case::WideColorSupport::injectConfigChange(this);
-
- // The display is setup with the HWC.
- Case::Display::injectHwcDisplay(this);
-
- // SurfaceFlinger will use a test-controlled factory for native window
- // surfaces.
- injectFakeNativeWindowSurfaceFactory();
-
- // A compositionengine::Display has already been created
- auto compositionDisplay = Case::Display::injectCompositionDisplay(this);
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- // Various native window calls will be made.
- Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
- Case::Display::setupHwcGetActiveConfigCallExpectations(this);
- Case::WideColorSupport::setupComposerCallExpectations(this);
- Case::HdrSupport::setupComposerCallExpectations(this);
- Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
-
- // --------------------------------------------------------------------
- // Invocation
-
- DisplayDeviceState state;
- if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) {
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(displayId);
- const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
- ASSERT_TRUE(hwcDisplayId);
- state.physical = {*displayId, *connectionType, *hwcDisplayId};
- }
-
- state.isSecure = static_cast<bool>(Case::Display::SECURE);
-
- auto device = mFlinger.setupNewDisplayDeviceInternal(displayToken, compositionDisplay, state,
- displaySurface, producer);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- ASSERT_TRUE(device != nullptr);
- EXPECT_EQ(Case::Display::DISPLAY_ID::get(), device->getId());
- EXPECT_EQ(Case::Display::CONNECTION_TYPE::value, device->getConnectionType());
- EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), device->isVirtual());
- EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
- EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary());
- EXPECT_EQ(Case::Display::WIDTH, device->getWidth());
- EXPECT_EQ(Case::Display::HEIGHT, device->getHeight());
- EXPECT_EQ(Case::WideColorSupport::WIDE_COLOR_SUPPORTED, device->hasWideColorGamut());
- EXPECT_EQ(Case::HdrSupport::HDR10_PLUS_SUPPORTED, device->hasHDR10PlusSupport());
- EXPECT_EQ(Case::HdrSupport::HDR10_SUPPORTED, device->hasHDR10Support());
- EXPECT_EQ(Case::HdrSupport::HDR_HLG_SUPPORTED, device->hasHLGSupport());
- EXPECT_EQ(Case::HdrSupport::HDR_DOLBY_VISION_SUPPORTED, device->hasDolbyVisionSupport());
- // Note: This is not Case::Display::HWC_ACTIVE_CONFIG_ID as the ids are
- // remapped, and the test only ever sets up one config. If there were an error
- // looking up the remapped index, device->getActiveConfig() would be -1 instead.
- EXPECT_EQ(0, device->getActiveConfig().value());
- EXPECT_EQ(Case::PerFrameMetadataSupport::PER_FRAME_METADATA_KEYS,
- device->getSupportedPerFrameMetadata());
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createSimplePrimaryDisplay) {
- setupNewDisplayDeviceInternalTest<SimplePrimaryDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createSimpleExternalDisplay) {
- setupNewDisplayDeviceInternalTest<SimpleExternalDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createNonHwcVirtualDisplay) {
- setupNewDisplayDeviceInternalTest<NonHwcVirtualDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createHwcVirtualDisplay) {
- setupNewDisplayDeviceInternalTest<HwcVirtualDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createWideColorP3Display) {
- setupNewDisplayDeviceInternalTest<WideColorP3ColorimetricDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createHdr10PlusDisplay) {
- setupNewDisplayDeviceInternalTest<Hdr10PlusDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createHdr10Display) {
- setupNewDisplayDeviceInternalTest<Hdr10DisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createHdrHlgDisplay) {
- setupNewDisplayDeviceInternalTest<HdrHlgDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createHdrDolbyVisionDisplay) {
- setupNewDisplayDeviceInternalTest<HdrDolbyVisionDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createHdrSmpte2086DisplayCase) {
- setupNewDisplayDeviceInternalTest<HdrSmpte2086DisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createHdrCta816_3_DisplayCase) {
- setupNewDisplayDeviceInternalTest<HdrCta861_3_DisplayCase>();
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::handleTransactionLocked(eDisplayTransactionNeeded)
- */
-
-class HandleTransactionLockedTest : public DisplayTransactionTest {
-public:
- template <typename Case>
- void setupCommonPreconditions();
-
- template <typename Case, bool connected>
- static void expectHotplugReceived(mock::EventThread*);
-
- template <typename Case>
- void setupCommonCallExpectationsForConnectProcessing();
-
- template <typename Case>
- void setupCommonCallExpectationsForDisconnectProcessing();
-
- template <typename Case>
- void processesHotplugConnectCommon();
-
- template <typename Case>
- void ignoresHotplugConnectCommon();
-
- template <typename Case>
- void processesHotplugDisconnectCommon();
-
- template <typename Case>
- void verifyDisplayIsConnected(const sp<IBinder>& displayToken);
-
- template <typename Case>
- void verifyPhysicalDisplayIsConnected();
-
- void verifyDisplayIsNotConnected(const sp<IBinder>& displayToken);
-};
-
-template <typename Case>
-void HandleTransactionLockedTest::setupCommonPreconditions() {
- // Wide color displays support is configured appropriately
- Case::WideColorSupport::injectConfigChange(this);
-
- // SurfaceFlinger will use a test-controlled factory for BufferQueues
- injectFakeBufferQueueFactory();
-
- // SurfaceFlinger will use a test-controlled factory for native window
- // surfaces.
- injectFakeNativeWindowSurfaceFactory();
-}
-
-template <typename Case, bool connected>
-void HandleTransactionLockedTest::expectHotplugReceived(mock::EventThread* eventThread) {
- const auto convert = [](auto physicalDisplayId) {
- return std::make_optional(DisplayId{physicalDisplayId});
- };
-
- EXPECT_CALL(*eventThread,
- onHotplugReceived(ResultOf(convert, Case::Display::DISPLAY_ID::get()), connected))
- .Times(1);
-}
-
-template <typename Case>
-void HandleTransactionLockedTest::setupCommonCallExpectationsForConnectProcessing() {
- Case::Display::setupHwcHotplugCallExpectations(this);
-
- Case::Display::setupFramebufferConsumerBufferQueueCallExpectations(this);
- Case::Display::setupFramebufferProducerBufferQueueCallExpectations(this);
- Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
- Case::Display::setupHwcGetActiveConfigCallExpectations(this);
-
- Case::WideColorSupport::setupComposerCallExpectations(this);
- Case::HdrSupport::setupComposerCallExpectations(this);
- Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
-
- EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
- expectHotplugReceived<Case, true>(mEventThread);
- expectHotplugReceived<Case, true>(mSFEventThread);
-}
-
-template <typename Case>
-void HandleTransactionLockedTest::setupCommonCallExpectationsForDisconnectProcessing() {
- EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
-
- expectHotplugReceived<Case, false>(mEventThread);
- expectHotplugReceived<Case, false>(mSFEventThread);
-}
-
-template <typename Case>
-void HandleTransactionLockedTest::verifyDisplayIsConnected(const sp<IBinder>& displayToken) {
- // The display device should have been set up in the list of displays.
- ASSERT_TRUE(hasDisplayDevice(displayToken));
- const auto& device = getDisplayDevice(displayToken);
- EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
- EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary());
-
- std::optional<DisplayDeviceState::Physical> expectedPhysical;
- if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) {
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(displayId);
- const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
- ASSERT_TRUE(hwcDisplayId);
- expectedPhysical = {*displayId, *connectionType, *hwcDisplayId};
- }
-
- // The display should have been set up in the current display state
- ASSERT_TRUE(hasCurrentDisplayState(displayToken));
- const auto& current = getCurrentDisplayState(displayToken);
- EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), current.isVirtual());
- EXPECT_EQ(expectedPhysical, current.physical);
-
- // The display should have been set up in the drawing display state
- ASSERT_TRUE(hasDrawingDisplayState(displayToken));
- const auto& draw = getDrawingDisplayState(displayToken);
- EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual());
- EXPECT_EQ(expectedPhysical, draw.physical);
-}
-
-template <typename Case>
-void HandleTransactionLockedTest::verifyPhysicalDisplayIsConnected() {
- // HWComposer should have an entry for the display
- EXPECT_TRUE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
-
- // SF should have a display token.
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(displayId);
- ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(*displayId) == 1);
- auto& displayToken = mFlinger.mutablePhysicalDisplayTokens()[*displayId];
-
- verifyDisplayIsConnected<Case>(displayToken);
-}
-
-void HandleTransactionLockedTest::verifyDisplayIsNotConnected(const sp<IBinder>& displayToken) {
- EXPECT_FALSE(hasDisplayDevice(displayToken));
- EXPECT_FALSE(hasCurrentDisplayState(displayToken));
- EXPECT_FALSE(hasDrawingDisplayState(displayToken));
-}
-
-template <typename Case>
-void HandleTransactionLockedTest::processesHotplugConnectCommon() {
- // --------------------------------------------------------------------
- // Preconditions
-
- setupCommonPreconditions<Case>();
-
- // A hotplug connect event is enqueued for a display
- Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- setupCommonCallExpectationsForConnectProcessing<Case>();
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- verifyPhysicalDisplayIsConnected<Case>();
-
- // --------------------------------------------------------------------
- // Cleanup conditions
-
- EXPECT_CALL(*mComposer,
- setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
- .WillOnce(Return(Error::NONE));
- EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
-}
-
-template <typename Case>
-void HandleTransactionLockedTest::ignoresHotplugConnectCommon() {
- // --------------------------------------------------------------------
- // Preconditions
-
- setupCommonPreconditions<Case>();
-
- // A hotplug connect event is enqueued for a display
- Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // HWComposer should not have an entry for the display
- EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
-}
-
-template <typename Case>
-void HandleTransactionLockedTest::processesHotplugDisconnectCommon() {
- // --------------------------------------------------------------------
- // Preconditions
-
- setupCommonPreconditions<Case>();
-
- // A hotplug disconnect event is enqueued for a display
- Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
-
- // The display is already completely set up.
- Case::Display::injectHwcDisplay(this);
- auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
- existing.inject();
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- EXPECT_CALL(*mComposer, getDisplayIdentificationData(Case::Display::HWC_DISPLAY_ID, _, _))
- .Times(0);
-
- setupCommonCallExpectationsForDisconnectProcessing<Case>();
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // HWComposer should not have an entry for the display
- EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
-
- // SF should not have a display token.
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(displayId);
- ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(*displayId) == 0);
-
- // The existing token should have been removed
- verifyDisplayIsNotConnected(existing.token());
-}
-
-TEST_F(HandleTransactionLockedTest, processesHotplugConnectPrimaryDisplay) {
- processesHotplugConnectCommon<SimplePrimaryDisplayCase>();
-}
-
-TEST_F(HandleTransactionLockedTest,
- processesHotplugConnectPrimaryDisplayWithExternalAlreadyConnected) {
- // Inject an external display.
- ExternalDisplayVariant::injectHwcDisplay(this);
-
- processesHotplugConnectCommon<SimplePrimaryDisplayCase>();
-}
-
-TEST_F(HandleTransactionLockedTest, processesHotplugConnectExternalDisplay) {
- // Inject a primary display.
- PrimaryDisplayVariant::injectHwcDisplay(this);
-
- processesHotplugConnectCommon<SimpleExternalDisplayCase>();
-}
-
-TEST_F(HandleTransactionLockedTest, ignoresHotplugConnectIfPrimaryAndExternalAlreadyConnected) {
- // Inject both a primary and external display.
- PrimaryDisplayVariant::injectHwcDisplay(this);
- ExternalDisplayVariant::injectHwcDisplay(this);
-
- // TODO: This is an unnecessary call.
- EXPECT_CALL(*mComposer,
- getDisplayIdentificationData(TertiaryDisplayVariant::HWC_DISPLAY_ID, _, _))
- .WillOnce(DoAll(SetArgPointee<1>(TertiaryDisplay::PORT),
- SetArgPointee<2>(TertiaryDisplay::GET_IDENTIFICATION_DATA()),
- Return(Error::NONE)));
-
- ignoresHotplugConnectCommon<SimpleTertiaryDisplayCase>();
-}
-
-TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectPrimaryDisplay) {
- processesHotplugDisconnectCommon<SimplePrimaryDisplayCase>();
-}
-
-TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectExternalDisplay) {
- processesHotplugDisconnectCommon<SimpleExternalDisplayCase>();
-}
-
-TEST_F(HandleTransactionLockedTest, processesHotplugConnectThenDisconnectPrimary) {
- using Case = SimplePrimaryDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- setupCommonPreconditions<Case>();
-
- // A hotplug connect event is enqueued for a display
- Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
- // A hotplug disconnect event is also enqueued for the same display
- Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- setupCommonCallExpectationsForConnectProcessing<Case>();
- setupCommonCallExpectationsForDisconnectProcessing<Case>();
-
- EXPECT_CALL(*mComposer,
- setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
- .WillOnce(Return(Error::NONE));
- EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // HWComposer should not have an entry for the display
- EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
-
- // SF should not have a display token.
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(displayId);
- ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(*displayId) == 0);
-}
-
-TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectThenConnectPrimary) {
- using Case = SimplePrimaryDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- setupCommonPreconditions<Case>();
-
- // The display is already completely set up.
- Case::Display::injectHwcDisplay(this);
- auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
- existing.inject();
-
- // A hotplug disconnect event is enqueued for a display
- Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
- // A hotplug connect event is also enqueued for the same display
- Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- setupCommonCallExpectationsForConnectProcessing<Case>();
- setupCommonCallExpectationsForDisconnectProcessing<Case>();
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The existing token should have been removed
- verifyDisplayIsNotConnected(existing.token());
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(displayId);
- ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(*displayId) == 1);
- EXPECT_NE(existing.token(), mFlinger.mutablePhysicalDisplayTokens()[*displayId]);
-
- // A new display should be connected in its place
-
- verifyPhysicalDisplayIsConnected<Case>();
-
- // --------------------------------------------------------------------
- // Cleanup conditions
-
- EXPECT_CALL(*mComposer,
- setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
- .WillOnce(Return(Error::NONE));
- EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
-}
-
-TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAdded) {
- using Case = HwcVirtualDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // The HWC supports at least one virtual display
- injectMockComposer(1);
-
- setupCommonPreconditions<Case>();
-
- // A virtual display was added to the current state, and it has a
- // surface(producer)
- sp<BBinder> displayToken = new BBinder();
-
- DisplayDeviceState state;
- state.isSecure = static_cast<bool>(Case::Display::SECURE);
-
- sp<mock::GraphicBufferProducer> surface{new mock::GraphicBufferProducer()};
- state.surface = surface;
- mFlinger.mutableCurrentState().displays.add(displayToken, state);
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- Case::Display::setupFramebufferConsumerBufferQueueCallExpectations(this);
- Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
-
- EXPECT_CALL(*surface, query(NATIVE_WINDOW_WIDTH, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(Case::Display::WIDTH), Return(NO_ERROR)));
- EXPECT_CALL(*surface, query(NATIVE_WINDOW_HEIGHT, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(Case::Display::HEIGHT), Return(NO_ERROR)));
- EXPECT_CALL(*surface, query(NATIVE_WINDOW_FORMAT, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_VIRTUAL_DISPLAY_SURFACE_FORMAT),
- Return(NO_ERROR)));
- EXPECT_CALL(*surface, query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR)));
-
- EXPECT_CALL(*surface, setAsyncMode(true)).Times(1);
-
- EXPECT_CALL(*mProducer, connect(_, NATIVE_WINDOW_API_EGL, false, _)).Times(1);
- EXPECT_CALL(*mProducer, disconnect(_, _)).Times(1);
-
- Case::Display::setupHwcVirtualDisplayCreationCallExpectations(this);
- Case::WideColorSupport::setupComposerCallExpectations(this);
- Case::HdrSupport::setupComposerCallExpectations(this);
- Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The display device should have been set up in the list of displays.
- verifyDisplayIsConnected<Case>(displayToken);
-
- // --------------------------------------------------------------------
- // Cleanup conditions
-
- EXPECT_CALL(*mComposer, destroyVirtualDisplay(Case::Display::HWC_DISPLAY_ID))
- .WillOnce(Return(Error::NONE));
- EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
-
- // Cleanup
- mFlinger.mutableCurrentState().displays.removeItem(displayToken);
- mFlinger.mutableDrawingState().displays.removeItem(displayToken);
-}
-
-TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAddedWithNoSurface) {
- using Case = HwcVirtualDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // The HWC supports at least one virtual display
- injectMockComposer(1);
-
- setupCommonPreconditions<Case>();
-
- // A virtual display was added to the current state, but it does not have a
- // surface.
- sp<BBinder> displayToken = new BBinder();
-
- DisplayDeviceState state;
- state.isSecure = static_cast<bool>(Case::Display::SECURE);
-
- mFlinger.mutableCurrentState().displays.add(displayToken, state);
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // There will not be a display device set up.
- EXPECT_FALSE(hasDisplayDevice(displayToken));
-
- // The drawing display state will be set from the current display state.
- ASSERT_TRUE(hasDrawingDisplayState(displayToken));
- const auto& draw = getDrawingDisplayState(displayToken);
- EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual());
-}
-
-TEST_F(HandleTransactionLockedTest, processesVirtualDisplayRemoval) {
- using Case = HwcVirtualDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A virtual display is set up but is removed from the current state.
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(displayId);
- mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
- Case::Display::injectHwcDisplay(this);
- auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
- existing.inject();
- mFlinger.mutableCurrentState().displays.removeItem(existing.token());
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The existing token should have been removed
- verifyDisplayIsNotConnected(existing.token());
-}
-
-TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackChanges) {
- using Case = NonHwcVirtualDisplayCase;
-
- constexpr uint32_t oldLayerStack = 0u;
- constexpr uint32_t newLayerStack = 123u;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // There is a change to the layerStack state
- display.mutableDrawingDisplayState().layerStack = oldLayerStack;
- display.mutableCurrentDisplayState().layerStack = newLayerStack;
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- EXPECT_EQ(newLayerStack, display.mutableDisplayDevice()->getLayerStack());
-}
-
-TEST_F(HandleTransactionLockedTest, processesDisplayTransformChanges) {
- using Case = NonHwcVirtualDisplayCase;
-
- constexpr ui::Rotation oldTransform = ui::ROTATION_0;
- constexpr ui::Rotation newTransform = ui::ROTATION_180;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // There is a change to the orientation state
- display.mutableDrawingDisplayState().orientation = oldTransform;
- display.mutableCurrentDisplayState().orientation = newTransform;
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- EXPECT_EQ(newTransform, display.mutableDisplayDevice()->getOrientation());
-}
-
-TEST_F(HandleTransactionLockedTest, processesDisplayViewportChanges) {
- using Case = NonHwcVirtualDisplayCase;
-
- const Rect oldViewport(0, 0, 0, 0);
- const Rect newViewport(0, 0, 123, 456);
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // There is a change to the viewport state
- display.mutableDrawingDisplayState().viewport = oldViewport;
- display.mutableCurrentDisplayState().viewport = newViewport;
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- EXPECT_EQ(newViewport, display.mutableDisplayDevice()->getViewport());
-}
-
-TEST_F(HandleTransactionLockedTest, processesDisplayFrameChanges) {
- using Case = NonHwcVirtualDisplayCase;
-
- const Rect oldFrame(0, 0, 0, 0);
- const Rect newFrame(0, 0, 123, 456);
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // There is a change to the viewport state
- display.mutableDrawingDisplayState().frame = oldFrame;
- display.mutableCurrentDisplayState().frame = newFrame;
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- EXPECT_EQ(newFrame, display.mutableDisplayDevice()->getFrame());
-}
-
-TEST_F(HandleTransactionLockedTest, processesDisplayWidthChanges) {
- using Case = NonHwcVirtualDisplayCase;
-
- constexpr int oldWidth = 0;
- constexpr int oldHeight = 10;
- constexpr int newWidth = 123;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto nativeWindow = new mock::NativeWindow();
- auto displaySurface = new compositionengine::mock::DisplaySurface();
- sp<GraphicBuffer> buf = new GraphicBuffer();
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.setNativeWindow(nativeWindow);
- display.setDisplaySurface(displaySurface);
- // Setup injection expections
- EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
- .WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0)));
- EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
- .WillOnce(DoAll(SetArgPointee<1>(oldHeight), Return(0)));
- EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
- EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
- EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
- EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1);
- display.inject();
-
- // There is a change to the viewport state
- display.mutableDrawingDisplayState().width = oldWidth;
- display.mutableDrawingDisplayState().height = oldHeight;
- display.mutableCurrentDisplayState().width = newWidth;
- display.mutableCurrentDisplayState().height = oldHeight;
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- EXPECT_CALL(*displaySurface, resizeBuffers(newWidth, oldHeight)).Times(1);
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-}
-
-TEST_F(HandleTransactionLockedTest, processesDisplayHeightChanges) {
- using Case = NonHwcVirtualDisplayCase;
-
- constexpr int oldWidth = 0;
- constexpr int oldHeight = 10;
- constexpr int newHeight = 123;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto nativeWindow = new mock::NativeWindow();
- auto displaySurface = new compositionengine::mock::DisplaySurface();
- sp<GraphicBuffer> buf = new GraphicBuffer();
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.setNativeWindow(nativeWindow);
- display.setDisplaySurface(displaySurface);
- // Setup injection expections
- EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
- .WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0)));
- EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
- .WillOnce(DoAll(SetArgPointee<1>(oldHeight), Return(0)));
- EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
- EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
- EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
- EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1);
- display.inject();
-
- // There is a change to the viewport state
- display.mutableDrawingDisplayState().width = oldWidth;
- display.mutableDrawingDisplayState().height = oldHeight;
- display.mutableCurrentDisplayState().width = oldWidth;
- display.mutableCurrentDisplayState().height = newHeight;
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- EXPECT_CALL(*displaySurface, resizeBuffers(oldWidth, newHeight)).Times(1);
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::setDisplayStateLocked
- */
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingWithUnknownDisplay) {
- // --------------------------------------------------------------------
- // Preconditions
-
- // We have an unknown display token not associated with a known display
- sp<BBinder> displayToken = new BBinder();
-
- // The requested display state references the unknown display.
- DisplayState state;
- state.what = DisplayState::eLayerStackChanged;
- state.token = displayToken;
- state.layerStack = 456;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags are empty
- EXPECT_EQ(0u, flags);
-
- // The display token still doesn't match anything known.
- EXPECT_FALSE(hasCurrentDisplayState(displayToken));
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingWhenNoChanges) {
- using Case = SimplePrimaryDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is already set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // No changes are made to the display
- DisplayState state;
- state.what = 0;
- state.token = display.token();
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags are empty
- EXPECT_EQ(0u, flags);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingIfSurfaceDidNotChange) {
- using Case = SimplePrimaryDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is already set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // There is a surface that can be set.
- sp<mock::GraphicBufferProducer> surface = new mock::GraphicBufferProducer();
-
- // The current display state has the surface set
- display.mutableCurrentDisplayState().surface = surface;
-
- // The incoming request sets the same surface
- DisplayState state;
- state.what = DisplayState::eSurfaceChanged;
- state.token = display.token();
- state.surface = surface;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags are empty
- EXPECT_EQ(0u, flags);
-
- // The current display state is unchanged.
- EXPECT_EQ(surface.get(), display.getCurrentDisplayState().surface.get());
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfSurfaceChanged) {
- using Case = SimplePrimaryDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is already set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // There is a surface that can be set.
- sp<mock::GraphicBufferProducer> surface = new mock::GraphicBufferProducer();
-
- // The current display state does not have a surface
- display.mutableCurrentDisplayState().surface = nullptr;
-
- // The incoming request sets a surface
- DisplayState state;
- state.what = DisplayState::eSurfaceChanged;
- state.token = display.token();
- state.surface = surface;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags indicate a transaction is needed
- EXPECT_EQ(eDisplayTransactionNeeded, flags);
-
- // The current display layer stack state is set to the new value
- EXPECT_EQ(surface.get(), display.getCurrentDisplayState().surface.get());
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingIfLayerStackDidNotChange) {
- using Case = SimplePrimaryDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is already set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The display has a layer stack set
- display.mutableCurrentDisplayState().layerStack = 456u;
-
- // The incoming request sets the same layer stack
- DisplayState state;
- state.what = DisplayState::eLayerStackChanged;
- state.token = display.token();
- state.layerStack = 456u;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags are empty
- EXPECT_EQ(0u, flags);
-
- // The current display state is unchanged
- EXPECT_EQ(456u, display.getCurrentDisplayState().layerStack);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfLayerStackChanged) {
- using Case = SimplePrimaryDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The display has a layer stack set
- display.mutableCurrentDisplayState().layerStack = 654u;
-
- // The incoming request sets a different layer stack
- DisplayState state;
- state.what = DisplayState::eLayerStackChanged;
- state.token = display.token();
- state.layerStack = 456u;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags indicate a transaction is needed
- EXPECT_EQ(eDisplayTransactionNeeded, flags);
-
- // The desired display state has been set to the new value.
- EXPECT_EQ(456u, display.getCurrentDisplayState().layerStack);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingIfProjectionDidNotChange) {
- using Case = SimplePrimaryDisplayCase;
- constexpr ui::Rotation initialOrientation = ui::ROTATION_180;
- const Rect initialFrame = {1, 2, 3, 4};
- const Rect initialViewport = {5, 6, 7, 8};
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The current display state projection state is all set
- display.mutableCurrentDisplayState().orientation = initialOrientation;
- display.mutableCurrentDisplayState().frame = initialFrame;
- display.mutableCurrentDisplayState().viewport = initialViewport;
-
- // The incoming request sets the same projection state
- DisplayState state;
- state.what = DisplayState::eDisplayProjectionChanged;
- state.token = display.token();
- state.orientation = initialOrientation;
- state.frame = initialFrame;
- state.viewport = initialViewport;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags are empty
- EXPECT_EQ(0u, flags);
-
- // The current display state is unchanged
- EXPECT_EQ(initialOrientation, display.getCurrentDisplayState().orientation);
-
- EXPECT_EQ(initialFrame, display.getCurrentDisplayState().frame);
- EXPECT_EQ(initialViewport, display.getCurrentDisplayState().viewport);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfOrientationChanged) {
- using Case = SimplePrimaryDisplayCase;
- constexpr ui::Rotation initialOrientation = ui::ROTATION_90;
- constexpr ui::Rotation desiredOrientation = ui::ROTATION_180;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The current display state has an orientation set
- display.mutableCurrentDisplayState().orientation = initialOrientation;
-
- // The incoming request sets a different orientation
- DisplayState state;
- state.what = DisplayState::eDisplayProjectionChanged;
- state.token = display.token();
- state.orientation = desiredOrientation;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags indicate a transaction is needed
- EXPECT_EQ(eDisplayTransactionNeeded, flags);
-
- // The current display state has the new value.
- EXPECT_EQ(desiredOrientation, display.getCurrentDisplayState().orientation);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfFrameChanged) {
- using Case = SimplePrimaryDisplayCase;
- const Rect initialFrame = {0, 0, 0, 0};
- const Rect desiredFrame = {5, 6, 7, 8};
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The current display state does not have a frame
- display.mutableCurrentDisplayState().frame = initialFrame;
-
- // The incoming request sets a frame
- DisplayState state;
- state.what = DisplayState::eDisplayProjectionChanged;
- state.token = display.token();
- state.frame = desiredFrame;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags indicate a transaction is needed
- EXPECT_EQ(eDisplayTransactionNeeded, flags);
-
- // The current display state has the new value.
- EXPECT_EQ(desiredFrame, display.getCurrentDisplayState().frame);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfViewportChanged) {
- using Case = SimplePrimaryDisplayCase;
- const Rect initialViewport = {0, 0, 0, 0};
- const Rect desiredViewport = {5, 6, 7, 8};
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The current display state does not have a viewport
- display.mutableCurrentDisplayState().viewport = initialViewport;
-
- // The incoming request sets a viewport
- DisplayState state;
- state.what = DisplayState::eDisplayProjectionChanged;
- state.token = display.token();
- state.viewport = desiredViewport;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags indicate a transaction is needed
- EXPECT_EQ(eDisplayTransactionNeeded, flags);
-
- // The current display state has the new value.
- EXPECT_EQ(desiredViewport, display.getCurrentDisplayState().viewport);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingIfSizeDidNotChange) {
- using Case = SimplePrimaryDisplayCase;
- constexpr uint32_t initialWidth = 1024;
- constexpr uint32_t initialHeight = 768;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The current display state has a size set
- display.mutableCurrentDisplayState().width = initialWidth;
- display.mutableCurrentDisplayState().height = initialHeight;
-
- // The incoming request sets the same display size
- DisplayState state;
- state.what = DisplayState::eDisplaySizeChanged;
- state.token = display.token();
- state.width = initialWidth;
- state.height = initialHeight;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags are empty
- EXPECT_EQ(0u, flags);
-
- // The current display state is unchanged
- EXPECT_EQ(initialWidth, display.getCurrentDisplayState().width);
- EXPECT_EQ(initialHeight, display.getCurrentDisplayState().height);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfWidthChanged) {
- using Case = SimplePrimaryDisplayCase;
- constexpr uint32_t initialWidth = 0;
- constexpr uint32_t desiredWidth = 1024;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The display does not yet have a width
- display.mutableCurrentDisplayState().width = initialWidth;
-
- // The incoming request sets a display width
- DisplayState state;
- state.what = DisplayState::eDisplaySizeChanged;
- state.token = display.token();
- state.width = desiredWidth;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags indicate a transaction is needed
- EXPECT_EQ(eDisplayTransactionNeeded, flags);
-
- // The current display state has the new value.
- EXPECT_EQ(desiredWidth, display.getCurrentDisplayState().width);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfHeightChanged) {
- using Case = SimplePrimaryDisplayCase;
- constexpr uint32_t initialHeight = 0;
- constexpr uint32_t desiredHeight = 768;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The display does not yet have a height
- display.mutableCurrentDisplayState().height = initialHeight;
-
- // The incoming request sets a display height
- DisplayState state;
- state.what = DisplayState::eDisplaySizeChanged;
- state.token = display.token();
- state.height = desiredHeight;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags indicate a transaction is needed
- EXPECT_EQ(eDisplayTransactionNeeded, flags);
-
- // The current display state has the new value.
- EXPECT_EQ(desiredHeight, display.getCurrentDisplayState().height);
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::onInitializeDisplays
- */
-
-TEST_F(DisplayTransactionTest, onInitializeDisplaysSetsUpPrimaryDisplay) {
- using Case = SimplePrimaryDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A primary display is set up
- Case::Display::injectHwcDisplay(this);
- auto primaryDisplay = Case::Display::makeFakeExistingDisplayInjector(this);
- primaryDisplay.inject();
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- // We expect the surface interceptor to possibly be used, but we treat it as
- // disabled since it is called as a side effect rather than directly by this
- // function.
- EXPECT_CALL(*mSurfaceInterceptor, isEnabled()).WillOnce(Return(false));
-
- // We expect a call to get the active display config.
- Case::Display::setupHwcGetActiveConfigCallExpectations(this);
-
- // We expect invalidate() to be invoked once to trigger display transaction
- // processing.
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-
- EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0));
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.onInitializeDisplays();
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The primary display should have a current state
- ASSERT_TRUE(hasCurrentDisplayState(primaryDisplay.token()));
- const auto& primaryDisplayState = getCurrentDisplayState(primaryDisplay.token());
- // The layer stack state should be set to zero
- EXPECT_EQ(0u, primaryDisplayState.layerStack);
- // The orientation state should be set to zero
- EXPECT_EQ(ui::ROTATION_0, primaryDisplayState.orientation);
-
- // The frame state should be set to INVALID
- EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.frame);
-
- // The viewport state should be set to INVALID
- EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.viewport);
-
- // The width and height should both be zero
- EXPECT_EQ(0u, primaryDisplayState.width);
- EXPECT_EQ(0u, primaryDisplayState.height);
-
- // The display should be set to PowerMode::ON
- ASSERT_TRUE(hasDisplayDevice(primaryDisplay.token()));
- auto displayDevice = primaryDisplay.mutableDisplayDevice();
- EXPECT_EQ(PowerMode::ON, displayDevice->getPowerMode());
-
- // The display refresh period should be set in the frame tracker.
- FrameStats stats;
- mFlinger.getAnimFrameTracker().getStats(&stats);
- EXPECT_EQ(DEFAULT_REFRESH_RATE, stats.refreshPeriodNano);
-
- // The display transaction needed flag should be set.
- EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
-
- // The compositor timing should be set to default values
- const auto& compositorTiming = mFlinger.getCompositorTiming();
- EXPECT_EQ(-DEFAULT_REFRESH_RATE, compositorTiming.deadline);
- EXPECT_EQ(DEFAULT_REFRESH_RATE, compositorTiming.interval);
- EXPECT_EQ(DEFAULT_REFRESH_RATE, compositorTiming.presentLatency);
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::setPowerModeInternal
- */
-
-// Used when we simulate a display that supports doze.
-template <typename Display>
-struct DozeIsSupportedVariant {
- static constexpr bool DOZE_SUPPORTED = true;
- static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE =
- IComposerClient::PowerMode::DOZE;
- static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND =
- IComposerClient::PowerMode::DOZE_SUSPEND;
-
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _))
- .WillOnce(DoAll(SetArgPointee<1>(
- std::vector<DisplayCapability>({DisplayCapability::DOZE})),
- Return(Error::NONE)));
- }
-};
-
-template <typename Display>
-// Used when we simulate a display that does not support doze.
-struct DozeNotSupportedVariant {
- static constexpr bool DOZE_SUPPORTED = false;
- static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE =
- IComposerClient::PowerMode::ON;
- static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND =
- IComposerClient::PowerMode::ON;
-
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})),
- Return(Error::NONE)));
- }
-};
-
-struct EventThreadBaseSupportedVariant {
- static void setupEventAndEventControlThreadNoCallExpectations(DisplayTransactionTest* test) {
- // The event control thread should not be notified.
- EXPECT_CALL(*test->mEventControlThread, setVsyncEnabled(_)).Times(0);
-
- // The event thread should not be notified.
- EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(0);
- EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(0);
- }
-};
-
-struct EventThreadNotSupportedVariant : public EventThreadBaseSupportedVariant {
- static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) {
- // These calls are only expected for the primary display.
-
- // Instead expect no calls.
- setupEventAndEventControlThreadNoCallExpectations(test);
- }
-
- static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) {
- // These calls are only expected for the primary display.
-
- // Instead expect no calls.
- setupEventAndEventControlThreadNoCallExpectations(test);
- }
-};
-
-struct EventThreadIsSupportedVariant : public EventThreadBaseSupportedVariant {
- static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) {
- // The event control thread should be notified to enable vsyncs
- EXPECT_CALL(*test->mEventControlThread, setVsyncEnabled(true)).Times(1);
-
- // The event thread should be notified that the screen was acquired.
- EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(1);
- }
-
- static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) {
- // There should be a call to setVsyncEnabled(false)
- EXPECT_CALL(*test->mEventControlThread, setVsyncEnabled(false)).Times(1);
-
- // The event thread should not be notified that the screen was released.
- EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(1);
- }
-};
-
-struct DispSyncIsSupportedVariant {
- static void setupBeginResyncCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mPrimaryDispSync, setPeriod(DEFAULT_REFRESH_RATE)).Times(1);
- EXPECT_CALL(*test->mPrimaryDispSync, beginResync()).Times(1);
- }
-
- static void setupEndResyncCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mPrimaryDispSync, endResync()).Times(1);
- }
-};
-
-struct DispSyncNotSupportedVariant {
- static void setupBeginResyncCallExpectations(DisplayTransactionTest* /* test */) {}
-
- static void setupEndResyncCallExpectations(DisplayTransactionTest* /* test */) {}
-};
-
-// --------------------------------------------------------------------
-// Note:
-//
-// There are a large number of transitions we could test, however we only test a
-// selected subset which provides complete test coverage of the implementation.
-// --------------------------------------------------------------------
-
-template <PowerMode initialPowerMode, PowerMode targetPowerMode>
-struct TransitionVariantCommon {
- static constexpr auto INITIAL_POWER_MODE = initialPowerMode;
- static constexpr auto TARGET_POWER_MODE = targetPowerMode;
-
- static void verifyPostconditions(DisplayTransactionTest*) {}
-};
-
-struct TransitionOffToOnVariant : public TransitionVariantCommon<PowerMode::OFF, PowerMode::ON> {
- template <typename Case>
- static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
- Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
- Case::DispSync::setupBeginResyncCallExpectations(test);
- Case::setupRepaintEverythingCallExpectations(test);
- }
-
- static void verifyPostconditions(DisplayTransactionTest* test) {
- EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
- EXPECT_TRUE(test->mFlinger.getHasPoweredOff());
- }
-};
-
-struct TransitionOffToDozeSuspendVariant
- : public TransitionVariantCommon<PowerMode::OFF, PowerMode::DOZE_SUSPEND> {
- template <typename Case>
- static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
- Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
- Case::setupRepaintEverythingCallExpectations(test);
- }
-
- static void verifyPostconditions(DisplayTransactionTest* test) {
- EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
- EXPECT_TRUE(test->mFlinger.getHasPoweredOff());
- }
-};
-
-struct TransitionOnToOffVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::OFF> {
- template <typename Case>
- static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
- Case::DispSync::setupEndResyncCallExpectations(test);
- Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
- }
-
- static void verifyPostconditions(DisplayTransactionTest* test) {
- EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
- }
-};
-
-struct TransitionDozeSuspendToOffVariant
- : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::OFF> {
- template <typename Case>
- static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
- Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
- }
-
- static void verifyPostconditions(DisplayTransactionTest* test) {
- EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
- }
-};
-
-struct TransitionOnToDozeVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE> {
- template <typename Case>
- static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
- Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
- }
-};
-
-struct TransitionDozeSuspendToDozeVariant
- : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::DOZE> {
- template <typename Case>
- static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
- Case::DispSync::setupBeginResyncCallExpectations(test);
- Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
- }
-};
-
-struct TransitionDozeToOnVariant : public TransitionVariantCommon<PowerMode::DOZE, PowerMode::ON> {
- template <typename Case>
- static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
- Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
- }
-};
-
-struct TransitionDozeSuspendToOnVariant
- : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::ON> {
- template <typename Case>
- static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
- Case::DispSync::setupBeginResyncCallExpectations(test);
- Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
- }
-};
-
-struct TransitionOnToDozeSuspendVariant
- : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE_SUSPEND> {
- template <typename Case>
- static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
- Case::DispSync::setupEndResyncCallExpectations(test);
- Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
- }
-};
-
-struct TransitionOnToUnknownVariant
- : public TransitionVariantCommon<PowerMode::ON, static_cast<PowerMode>(POWER_MODE_LEET)> {
- template <typename Case>
- static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
- Case::setupNoComposerPowerModeCallExpectations(test);
- }
-};
-
-// --------------------------------------------------------------------
-// Note:
-//
-// Rather than testing the cartesian product of of
-// DozeIsSupported/DozeNotSupported with all other options, we use one for one
-// display type, and the other for another display type.
-// --------------------------------------------------------------------
-
-template <typename DisplayVariant, typename DozeVariant, typename EventThreadVariant,
- typename DispSyncVariant, typename TransitionVariant>
-struct DisplayPowerCase {
- using Display = DisplayVariant;
- using Doze = DozeVariant;
- using EventThread = EventThreadVariant;
- using DispSync = DispSyncVariant;
- using Transition = TransitionVariant;
-
- static auto injectDisplayWithInitialPowerMode(DisplayTransactionTest* test, PowerMode mode) {
- Display::injectHwcDisplayWithNoDefaultCapabilities(test);
- auto display = Display::makeFakeExistingDisplayInjector(test);
- display.inject();
- display.mutableDisplayDevice()->setPowerMode(mode);
- return display;
- }
-
- static void setInitialPrimaryHWVsyncEnabled(DisplayTransactionTest* test, bool enabled) {
- test->mFlinger.scheduler()->mutablePrimaryHWVsyncEnabled() = enabled;
- }
-
- static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mMessageQueue, invalidate()).Times(1);
- }
-
- static void setupSurfaceInterceptorCallExpectations(DisplayTransactionTest* test,
- PowerMode mode) {
- EXPECT_CALL(*test->mSurfaceInterceptor, isEnabled()).WillOnce(Return(true));
- EXPECT_CALL(*test->mSurfaceInterceptor, savePowerModeUpdate(_, static_cast<int32_t>(mode)))
- .Times(1);
- }
-
- static void setupComposerCallExpectations(DisplayTransactionTest* test, PowerMode mode) {
- // Any calls to get the active config will return a default value.
- EXPECT_CALL(*test->mComposer, getActiveConfig(Display::HWC_DISPLAY_ID, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(Display::HWC_ACTIVE_CONFIG_ID),
- Return(Error::NONE)));
-
- // Any calls to get whether the display supports dozing will return the value set by the
- // policy variant.
- EXPECT_CALL(*test->mComposer, getDozeSupport(Display::HWC_DISPLAY_ID, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(Doze::DOZE_SUPPORTED), Return(Error::NONE)));
-
- EXPECT_CALL(*test->mComposer, setPowerMode(Display::HWC_DISPLAY_ID, mode)).Times(1);
- }
-
- static void setupNoComposerPowerModeCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, setPowerMode(Display::HWC_DISPLAY_ID, _)).Times(0);
- }
-};
-
-// A sample configuration for the primary display.
-// In addition to having event thread support, we emulate doze support.
-template <typename TransitionVariant>
-using PrimaryDisplayPowerCase =
- DisplayPowerCase<PrimaryDisplayVariant, DozeIsSupportedVariant<PrimaryDisplayVariant>,
- EventThreadIsSupportedVariant, DispSyncIsSupportedVariant,
- TransitionVariant>;
-
-// A sample configuration for the external display.
-// In addition to not having event thread support, we emulate not having doze
-// support.
-template <typename TransitionVariant>
-using ExternalDisplayPowerCase =
- DisplayPowerCase<ExternalDisplayVariant, DozeNotSupportedVariant<ExternalDisplayVariant>,
- EventThreadNotSupportedVariant, DispSyncNotSupportedVariant,
- TransitionVariant>;
-
-class SetPowerModeInternalTest : public DisplayTransactionTest {
-public:
- template <typename Case>
- void transitionDisplayCommon();
-};
-
-template <PowerMode PowerMode>
-struct PowerModeInitialVSyncEnabled : public std::false_type {};
-
-template <>
-struct PowerModeInitialVSyncEnabled<PowerMode::ON> : public std::true_type {};
-
-template <>
-struct PowerModeInitialVSyncEnabled<PowerMode::DOZE> : public std::true_type {};
-
-template <typename Case>
-void SetPowerModeInternalTest::transitionDisplayCommon() {
- // --------------------------------------------------------------------
- // Preconditions
-
- Case::Doze::setupComposerCallExpectations(this);
- auto display =
- Case::injectDisplayWithInitialPowerMode(this, Case::Transition::INITIAL_POWER_MODE);
- Case::setInitialPrimaryHWVsyncEnabled(this,
- PowerModeInitialVSyncEnabled<
- Case::Transition::INITIAL_POWER_MODE>::value);
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- Case::setupSurfaceInterceptorCallExpectations(this, Case::Transition::TARGET_POWER_MODE);
- Case::Transition::template setupCallExpectations<Case>(this);
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.setPowerModeInternal(display.mutableDisplayDevice(),
- Case::Transition::TARGET_POWER_MODE);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- Case::Transition::verifyPostconditions(this);
-}
-
-TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfNoChange) {
- using Case = SimplePrimaryDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A primary display device is set up
- Case::Display::injectHwcDisplay(this);
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The display is already set to PowerMode::ON
- display.mutableDisplayDevice()->setPowerMode(PowerMode::ON);
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), PowerMode::ON);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode());
-}
-
-TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfVirtualDisplay) {
- using Case = HwcVirtualDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // Insert display data so that the HWC thinks it created the virtual display.
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(displayId);
- mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
-
- // A virtual display device is set up
- Case::Display::injectHwcDisplay(this);
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The display is set to PowerMode::ON
- getDisplayDevice(display.token())->setPowerMode(PowerMode::ON);
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), PowerMode::OFF);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode());
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnPrimaryDisplay) {
- transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToOnVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendPrimaryDisplay) {
- transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToDozeSuspendVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffPrimaryDisplay) {
- transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToOffVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffPrimaryDisplay) {
- transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOffVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozePrimaryDisplay) {
- transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozePrimaryDisplay) {
- transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnPrimaryDisplay) {
- transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeToOnVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnPrimaryDisplay) {
- transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOnVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendPrimaryDisplay) {
- transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeSuspendVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownPrimaryDisplay) {
- transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToUnknownVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnExternalDisplay) {
- transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToOnVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendExternalDisplay) {
- transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToDozeSuspendVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffExternalDisplay) {
- transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToOffVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffExternalDisplay) {
- transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOffVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeExternalDisplay) {
- transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozeExternalDisplay) {
- transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnExternalDisplay) {
- transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeToOnVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnExternalDisplay) {
- transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOnVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendExternalDisplay) {
- transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeSuspendVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownExternalDisplay) {
- transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToUnknownVariant>>();
-}
-
-} // namespace
} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
new file mode 100644
index 0000000..d68fff6
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -0,0 +1,777 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
+
+#include <type_traits>
+#include "DisplayIdentificationTest.h"
+
+#include <binder/IPCThreadState.h>
+#include <compositionengine/Display.h>
+#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/impl/Display.h>
+#include <compositionengine/impl/OutputCompositionState.h>
+#include <compositionengine/mock/Display.h>
+#include <compositionengine/mock/DisplayColorProfile.h>
+#include <compositionengine/mock/DisplaySurface.h>
+#include <compositionengine/mock/RenderSurface.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/mock/GraphicBufferConsumer.h>
+#include <gui/mock/GraphicBufferProducer.h>
+#include <log/log.h>
+#include <private/android_filesystem_config.h>
+#include <renderengine/mock/RenderEngine.h>
+#include <ui/DebugUtils.h>
+
+#include "TestableScheduler.h"
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/DisplayHardware/MockPowerAdvisor.h"
+#include "mock/MockEventThread.h"
+#include "mock/MockMessageQueue.h"
+#include "mock/MockNativeWindowSurface.h"
+#include "mock/MockSchedulerCallback.h"
+#include "mock/MockSurfaceInterceptor.h"
+#include "mock/MockVsyncController.h"
+#include "mock/system/window/MockNativeWindow.h"
+
+namespace android {
+
+// TODO: Do not polute the android namespace
+namespace hal = android::hardware::graphics::composer::hal;
+
+using testing::_;
+using testing::AnyNumber;
+using testing::DoAll;
+using testing::Mock;
+using testing::ResultOf;
+using testing::Return;
+using testing::SetArgPointee;
+
+using hal::ColorMode;
+using hal::Connection;
+using hal::DisplayCapability;
+using hal::DisplayType;
+using hal::Error;
+using hal::Hdr;
+using hal::HWDisplayId;
+using hal::IComposer;
+using hal::IComposerClient;
+using hal::PerFrameMetadataKey;
+using hal::PowerMode;
+
+class DisplayTransactionTest : public testing::Test {
+public:
+ ~DisplayTransactionTest() override;
+
+ // --------------------------------------------------------------------
+ // Mock/Fake injection
+
+ void injectMockScheduler();
+ void injectMockComposer(int virtualDisplayCount);
+ void injectFakeBufferQueueFactory();
+ void injectFakeNativeWindowSurfaceFactory();
+ sp<DisplayDevice> injectDefaultInternalDisplay(
+ std::function<void(TestableSurfaceFlinger::FakeDisplayDeviceInjector&)>);
+
+ // --------------------------------------------------------------------
+ // Postcondition helpers
+
+ bool hasPhysicalHwcDisplay(hal::HWDisplayId hwcDisplayId);
+ bool hasTransactionFlagSet(int flag);
+ bool hasDisplayDevice(sp<IBinder> displayToken);
+ sp<DisplayDevice> getDisplayDevice(sp<IBinder> displayToken);
+ bool hasCurrentDisplayState(sp<IBinder> displayToken);
+ const DisplayDeviceState& getCurrentDisplayState(sp<IBinder> displayToken);
+ bool hasDrawingDisplayState(sp<IBinder> displayToken);
+ const DisplayDeviceState& getDrawingDisplayState(sp<IBinder> displayToken);
+
+ // --------------------------------------------------------------------
+ // Test instances
+
+ TestableSurfaceFlinger mFlinger;
+ sp<mock::NativeWindow> mNativeWindow = new mock::NativeWindow();
+ sp<GraphicBuffer> mBuffer = new GraphicBuffer();
+ Hwc2::mock::PowerAdvisor mPowerAdvisor;
+
+ // These mocks are created by the test, but are destroyed by SurfaceFlinger
+ // by virtue of being stored into a std::unique_ptr. However we still need
+ // to keep a reference to them for use in setting up call expectations.
+ renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
+ Hwc2::mock::Composer* mComposer = nullptr;
+ mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
+ sp<mock::SurfaceInterceptor> mSurfaceInterceptor = new mock::SurfaceInterceptor;
+
+ mock::VsyncController* mVsyncController = new mock::VsyncController;
+ mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker;
+ mock::SchedulerCallback mSchedulerCallback;
+ mock::EventThread* mEventThread = new mock::EventThread;
+ mock::EventThread* mSFEventThread = new mock::EventThread;
+
+ // These mocks are created only when expected to be created via a factory.
+ sp<mock::GraphicBufferConsumer> mConsumer;
+ sp<mock::GraphicBufferProducer> mProducer;
+ surfaceflinger::mock::NativeWindowSurface* mNativeWindowSurface = nullptr;
+
+protected:
+ DisplayTransactionTest();
+};
+
+constexpr int32_t DEFAULT_VSYNC_PERIOD = 16'666'667;
+constexpr int32_t DEFAULT_DPI = 320;
+constexpr int DEFAULT_VIRTUAL_DISPLAY_SURFACE_FORMAT = HAL_PIXEL_FORMAT_RGB_565;
+
+constexpr int POWER_MODE_LEET = 1337; // An out of range power mode value
+
+/* ------------------------------------------------------------------------
+ * Boolean avoidance
+ *
+ * To make calls and template instantiations more readable, we define some
+ * local enums along with an implicit bool conversion.
+ */
+
+#define BOOL_SUBSTITUTE(TYPENAME) enum class TYPENAME : bool { FALSE = false, TRUE = true };
+
+BOOL_SUBSTITUTE(Async);
+BOOL_SUBSTITUTE(Critical);
+BOOL_SUBSTITUTE(Primary);
+BOOL_SUBSTITUTE(Secure);
+BOOL_SUBSTITUTE(Virtual);
+
+template <typename PhysicalDisplay>
+struct PhysicalDisplayIdType {};
+
+template <uint64_t displayId>
+using HalVirtualDisplayIdType = std::integral_constant<uint64_t, displayId>;
+
+struct GpuVirtualDisplayIdType {};
+
+template <typename>
+struct IsPhysicalDisplayId : std::bool_constant<false> {};
+
+template <typename PhysicalDisplay>
+struct IsPhysicalDisplayId<PhysicalDisplayIdType<PhysicalDisplay>> : std::bool_constant<true> {};
+
+template <typename>
+struct DisplayIdGetter;
+
+template <typename PhysicalDisplay>
+struct DisplayIdGetter<PhysicalDisplayIdType<PhysicalDisplay>> {
+ static PhysicalDisplayId get() {
+ if (!PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
+ return PhysicalDisplayId::fromPort(static_cast<bool>(PhysicalDisplay::PRIMARY)
+ ? LEGACY_DISPLAY_TYPE_PRIMARY
+ : LEGACY_DISPLAY_TYPE_EXTERNAL);
+ }
+
+ const auto info =
+ parseDisplayIdentificationData(PhysicalDisplay::PORT,
+ PhysicalDisplay::GET_IDENTIFICATION_DATA());
+ return info ? info->id : PhysicalDisplayId::fromPort(PhysicalDisplay::PORT);
+ }
+};
+
+template <uint64_t displayId>
+struct DisplayIdGetter<HalVirtualDisplayIdType<displayId>> {
+ static HalVirtualDisplayId get() { return HalVirtualDisplayId(displayId); }
+};
+
+template <>
+struct DisplayIdGetter<GpuVirtualDisplayIdType> {
+ static GpuVirtualDisplayId get() { return GpuVirtualDisplayId(0); }
+};
+
+template <typename>
+struct DisplayConnectionTypeGetter {
+ static constexpr std::optional<ui::DisplayConnectionType> value;
+};
+
+template <typename PhysicalDisplay>
+struct DisplayConnectionTypeGetter<PhysicalDisplayIdType<PhysicalDisplay>> {
+ static constexpr std::optional<ui::DisplayConnectionType> value =
+ PhysicalDisplay::CONNECTION_TYPE;
+};
+
+template <typename>
+struct HwcDisplayIdGetter {
+ static constexpr std::optional<HWDisplayId> value;
+};
+
+constexpr HWDisplayId HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID = 1010;
+
+template <uint64_t displayId>
+struct HwcDisplayIdGetter<HalVirtualDisplayIdType<displayId>> {
+ static constexpr std::optional<HWDisplayId> value = HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID;
+};
+
+template <typename PhysicalDisplay>
+struct HwcDisplayIdGetter<PhysicalDisplayIdType<PhysicalDisplay>> {
+ static constexpr std::optional<HWDisplayId> value = PhysicalDisplay::HWC_DISPLAY_ID;
+};
+
+// DisplayIdType can be:
+// 1) PhysicalDisplayIdType<...> for generated ID of physical display backed by HWC.
+// 2) HalVirtualDisplayIdType<...> for hard-coded ID of virtual display backed by HWC.
+// 3) GpuVirtualDisplayIdType for virtual display without HWC backing.
+template <typename DisplayIdType, int width, int height, Critical critical, Async async,
+ Secure secure, Primary primary, int grallocUsage>
+struct DisplayVariant {
+ using DISPLAY_ID = DisplayIdGetter<DisplayIdType>;
+ using CONNECTION_TYPE = DisplayConnectionTypeGetter<DisplayIdType>;
+ using HWC_DISPLAY_ID_OPT = HwcDisplayIdGetter<DisplayIdType>;
+
+ // The display width and height
+ static constexpr int WIDTH = width;
+ static constexpr int HEIGHT = height;
+
+ static constexpr int GRALLOC_USAGE = grallocUsage;
+
+ // Whether the display is virtual or physical
+ static constexpr Virtual VIRTUAL =
+ IsPhysicalDisplayId<DisplayIdType>{} ? Virtual::FALSE : Virtual::TRUE;
+
+ // When creating native window surfaces for the framebuffer, whether those should be critical
+ static constexpr Critical CRITICAL = critical;
+
+ // When creating native window surfaces for the framebuffer, whether those should be async
+ static constexpr Async ASYNC = async;
+
+ // Whether the display should be treated as secure
+ static constexpr Secure SECURE = secure;
+
+ // Whether the display is primary
+ static constexpr Primary PRIMARY = primary;
+
+ static auto makeFakeExistingDisplayInjector(DisplayTransactionTest* test) {
+ auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder();
+ if (auto displayId = PhysicalDisplayId::tryCast(DISPLAY_ID::get())) {
+ ceDisplayArgs.setPhysical({*displayId, ui::DisplayConnectionType::Internal});
+ } else {
+ // We turn off the use of HwcVirtualDisplays, to prevent Composition Engine
+ // from calling into HWComposer. This way all virtual displays will get
+ // a GpuVirtualDisplayId, even if we are in the HwcVirtualDisplayVariant.
+ // In this case we later override it by calling display.setDisplayIdForTesting().
+ ceDisplayArgs.setUseHwcVirtualDisplays(false);
+
+ GpuVirtualDisplayId desiredDisplayId = GpuVirtualDisplayId::tryCast(DISPLAY_ID::get())
+ .value_or(GpuVirtualDisplayId(0));
+
+ ON_CALL(test->mFlinger.gpuVirtualDisplayIdGenerator(), nextId())
+ .WillByDefault(Return(desiredDisplayId));
+
+ auto& generator = test->mFlinger.gpuVirtualDisplayIdGenerator();
+ ceDisplayArgs.setGpuVirtualDisplayIdGenerator(generator);
+ }
+ ceDisplayArgs.setPixels({WIDTH, HEIGHT}).setPowerAdvisor(&test->mPowerAdvisor);
+
+ auto compositionDisplay =
+ compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+ ceDisplayArgs.build());
+
+ if (HalVirtualDisplayId::tryCast(DISPLAY_ID::get())) {
+ // CompositionEngine has assigned a placeholder GpuVirtualDisplayId and we need to
+ // override it with the correct HalVirtualDisplayId.
+ compositionDisplay->setDisplayIdForTesting(DISPLAY_ID::get());
+ }
+
+ auto injector =
+ TestableSurfaceFlinger::FakeDisplayDeviceInjector(test->mFlinger,
+ compositionDisplay,
+ CONNECTION_TYPE::value,
+ HWC_DISPLAY_ID_OPT::value,
+ static_cast<bool>(PRIMARY));
+
+ injector.setSecure(static_cast<bool>(SECURE));
+ injector.setNativeWindow(test->mNativeWindow);
+
+ // Creating a DisplayDevice requires getting default dimensions from the
+ // native window along with some other initial setup.
+ EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(WIDTH), Return(0)));
+ EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(HEIGHT), Return(0)));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT))
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT))
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64))
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT))
+ .WillRepeatedly(Return(0));
+
+ return injector;
+ }
+
+ // Called by tests to set up any native window creation call expectations.
+ static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mNativeWindowSurface, getNativeWindow())
+ .WillOnce(Return(test->mNativeWindow));
+
+ EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(WIDTH), Return(0)));
+ EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(HEIGHT), Return(0)));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT))
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT))
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64))
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT))
+ .WillRepeatedly(Return(0));
+ }
+
+ static void setupFramebufferConsumerBufferQueueCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mConsumer, consumerConnect(_, false)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(*test->mConsumer, setConsumerName(_)).WillRepeatedly(Return(NO_ERROR));
+ EXPECT_CALL(*test->mConsumer, setConsumerUsageBits(GRALLOC_USAGE))
+ .WillRepeatedly(Return(NO_ERROR));
+ EXPECT_CALL(*test->mConsumer, setDefaultBufferSize(WIDTH, HEIGHT))
+ .WillRepeatedly(Return(NO_ERROR));
+ EXPECT_CALL(*test->mConsumer, setMaxAcquiredBufferCount(_))
+ .WillRepeatedly(Return(NO_ERROR));
+ }
+
+ static void setupFramebufferProducerBufferQueueCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mProducer, allocateBuffers(0, 0, 0, 0)).WillRepeatedly(Return());
+ }
+};
+
+template <HWDisplayId hwcDisplayId, DisplayType hwcDisplayType, typename DisplayVariant,
+ typename PhysicalDisplay = void>
+struct HwcDisplayVariant {
+ // The display id supplied by the HWC
+ static constexpr HWDisplayId HWC_DISPLAY_ID = hwcDisplayId;
+
+ // The HWC display type
+ static constexpr DisplayType HWC_DISPLAY_TYPE = hwcDisplayType;
+
+ // The HWC active configuration id
+ static constexpr hal::HWConfigId HWC_ACTIVE_CONFIG_ID = 2001;
+ static constexpr PowerMode INIT_POWER_MODE = hal::PowerMode::ON;
+
+ static void injectPendingHotplugEvent(DisplayTransactionTest* test, Connection connection) {
+ test->mFlinger.mutablePendingHotplugEvents().emplace_back(
+ TestableSurfaceFlinger::HotplugEvent{HWC_DISPLAY_ID, connection});
+ }
+
+ // Called by tests to inject a HWC display setup
+ static void injectHwcDisplayWithNoDefaultCapabilities(DisplayTransactionTest* test) {
+ const auto displayId = DisplayVariant::DISPLAY_ID::get();
+ ASSERT_FALSE(GpuVirtualDisplayId::tryCast(displayId));
+ TestableSurfaceFlinger::FakeHwcDisplayInjector(displayId, HWC_DISPLAY_TYPE,
+ static_cast<bool>(DisplayVariant::PRIMARY))
+ .setHwcDisplayId(HWC_DISPLAY_ID)
+ .setWidth(DisplayVariant::WIDTH)
+ .setHeight(DisplayVariant::HEIGHT)
+ .setActiveConfig(HWC_ACTIVE_CONFIG_ID)
+ .setPowerMode(INIT_POWER_MODE)
+ .inject(&test->mFlinger, test->mComposer);
+ }
+
+ // Called by tests to inject a HWC display setup
+ static void injectHwcDisplay(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})),
+ Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, INIT_POWER_MODE))
+ .WillOnce(Return(Error::NONE));
+ injectHwcDisplayWithNoDefaultCapabilities(test);
+ }
+
+ static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
+ DisplayTransactionTest* test) {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+
+ auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
+ .setPhysical({DisplayVariant::DISPLAY_ID::get(),
+ PhysicalDisplay::CONNECTION_TYPE})
+ .setPixels({DisplayVariant::WIDTH, DisplayVariant::HEIGHT})
+ .setIsSecure(static_cast<bool>(DisplayVariant::SECURE))
+ .setPowerAdvisor(&test->mPowerAdvisor)
+ .setName(std::string("Injected display for ") +
+ test_info->test_case_name() + "." + test_info->name())
+ .build();
+
+ return compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+ ceDisplayArgs);
+ }
+
+ static void setupHwcGetConfigsCallExpectations(DisplayTransactionTest* test) {
+ if (HWC_DISPLAY_TYPE == DisplayType::PHYSICAL) {
+ EXPECT_CALL(*test->mComposer, getDisplayConfigs(HWC_DISPLAY_ID, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(std::vector<hal::HWConfigId>{
+ HWC_ACTIVE_CONFIG_ID}),
+ Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer,
+ getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+ IComposerClient::Attribute::WIDTH, _))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<3>(DisplayVariant::WIDTH), Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer,
+ getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+ IComposerClient::Attribute::HEIGHT, _))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<3>(DisplayVariant::HEIGHT), Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer,
+ getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+ IComposerClient::Attribute::VSYNC_PERIOD, _))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<3>(DEFAULT_VSYNC_PERIOD), Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer,
+ getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+ IComposerClient::Attribute::DPI_X, _))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer,
+ getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+ IComposerClient::Attribute::DPI_Y, _))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer,
+ getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+ IComposerClient::Attribute::CONFIG_GROUP, _))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(-1), Return(Error::NONE)));
+ } else {
+ EXPECT_CALL(*test->mComposer, getDisplayConfigs(_, _)).Times(0);
+ EXPECT_CALL(*test->mComposer, getDisplayAttribute(_, _, _, _)).Times(0);
+ }
+ }
+
+ static void setupHwcHotplugCallExpectations(DisplayTransactionTest* test) {
+ constexpr auto CONNECTION_TYPE =
+ PhysicalDisplay::CONNECTION_TYPE == ui::DisplayConnectionType::Internal
+ ? IComposerClient::DisplayConnectionType::INTERNAL
+ : IComposerClient::DisplayConnectionType::EXTERNAL;
+
+ EXPECT_CALL(*test->mComposer, getDisplayConnectionType(HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(CONNECTION_TYPE), Return(hal::V2_4::Error::NONE)));
+
+ EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_))
+ .WillOnce(Return(hal::Error::NONE));
+
+ setupHwcGetConfigsCallExpectations(test);
+
+ if (PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
+ EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(PhysicalDisplay::PORT),
+ SetArgPointee<2>(PhysicalDisplay::GET_IDENTIFICATION_DATA()),
+ Return(Error::NONE)));
+ } else {
+ EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _))
+ .WillOnce(Return(Error::UNSUPPORTED));
+ }
+ }
+
+ // Called by tests to set up HWC call expectations
+ static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getActiveConfig(HWC_DISPLAY_ID, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(HWC_ACTIVE_CONFIG_ID), Return(Error::NONE)));
+ }
+};
+
+// Physical displays are expected to be synchronous, secure, and have a HWC display for output.
+constexpr uint32_t GRALLOC_USAGE_PHYSICAL_DISPLAY =
+ GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_FB;
+
+template <typename PhysicalDisplay, int width, int height, Critical critical>
+struct PhysicalDisplayVariant
+ : DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, critical,
+ Async::FALSE, Secure::TRUE, PhysicalDisplay::PRIMARY,
+ GRALLOC_USAGE_PHYSICAL_DISPLAY>,
+ HwcDisplayVariant<PhysicalDisplay::HWC_DISPLAY_ID, DisplayType::PHYSICAL,
+ DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height,
+ critical, Async::FALSE, Secure::TRUE,
+ PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY>,
+ PhysicalDisplay> {};
+
+template <bool hasIdentificationData>
+struct PrimaryDisplay {
+ static constexpr auto CONNECTION_TYPE = ui::DisplayConnectionType::Internal;
+ static constexpr Primary PRIMARY = Primary::TRUE;
+ static constexpr uint8_t PORT = 255;
+ static constexpr HWDisplayId HWC_DISPLAY_ID = 1001;
+ static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
+ static constexpr auto GET_IDENTIFICATION_DATA = getInternalEdid;
+};
+
+template <bool hasIdentificationData>
+struct ExternalDisplay {
+ static constexpr auto CONNECTION_TYPE = ui::DisplayConnectionType::External;
+ static constexpr Primary PRIMARY = Primary::FALSE;
+ static constexpr uint8_t PORT = 254;
+ static constexpr HWDisplayId HWC_DISPLAY_ID = 1002;
+ static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
+ static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
+};
+
+struct TertiaryDisplay {
+ static constexpr Primary PRIMARY = Primary::FALSE;
+ static constexpr uint8_t PORT = 253;
+ static constexpr HWDisplayId HWC_DISPLAY_ID = 1003;
+ static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
+};
+
+// A primary display is a physical display that is critical
+using PrimaryDisplayVariant =
+ PhysicalDisplayVariant<PrimaryDisplay<false>, 3840, 2160, Critical::TRUE>;
+
+// An external display is physical display that is not critical.
+using ExternalDisplayVariant =
+ PhysicalDisplayVariant<ExternalDisplay<false>, 1920, 1280, Critical::FALSE>;
+
+using TertiaryDisplayVariant = PhysicalDisplayVariant<TertiaryDisplay, 1600, 1200, Critical::FALSE>;
+
+// A virtual display not supported by the HWC.
+constexpr uint32_t GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY = 0;
+
+template <int width, int height, Secure secure>
+struct NonHwcVirtualDisplayVariant
+ : DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE, Async::TRUE, secure,
+ Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY> {
+ using Base =
+ DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE, Async::TRUE,
+ secure, Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY>;
+
+ static void injectHwcDisplay(DisplayTransactionTest*) {}
+
+ static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
+ DisplayTransactionTest* test) {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+
+ ON_CALL(test->mFlinger.gpuVirtualDisplayIdGenerator(), nextId())
+ .WillByDefault(Return(Base::DISPLAY_ID::get()));
+
+ auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
+ .setPixels({Base::WIDTH, Base::HEIGHT})
+ .setIsSecure(static_cast<bool>(Base::SECURE))
+ .setPowerAdvisor(&test->mPowerAdvisor)
+ .setName(std::string("Injected display for ") +
+ test_info->test_case_name() + "." + test_info->name())
+ .setGpuVirtualDisplayIdGenerator(
+ test->mFlinger.gpuVirtualDisplayIdGenerator())
+ .build();
+
+ return compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+ ceDisplayArgs);
+ }
+
+ static void setupHwcGetConfigsCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getDisplayConfigs(_, _)).Times(0);
+ EXPECT_CALL(*test->mComposer, getDisplayAttribute(_, _, _, _)).Times(0);
+ }
+
+ static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getActiveConfig(_, _)).Times(0);
+ }
+
+ static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
+ Base::setupNativeWindowSurfaceCreationCallExpectations(test);
+ EXPECT_CALL(*test->mNativeWindow, setSwapInterval(0)).Times(1);
+ }
+};
+
+// A virtual display supported by the HWC.
+constexpr uint32_t GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY = GRALLOC_USAGE_HW_COMPOSER;
+
+template <int width, int height, Secure secure>
+struct HwcVirtualDisplayVariant
+ : DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE, Async::TRUE,
+ secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>,
+ HwcDisplayVariant<HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID, DisplayType::VIRTUAL,
+ DisplayVariant<HalVirtualDisplayIdType<42>, width, height,
+ Critical::FALSE, Async::TRUE, secure, Primary::FALSE,
+ GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>> {
+ using Base = DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE,
+ Async::TRUE, secure, Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER>;
+ using Self = HwcVirtualDisplayVariant<width, height, secure>;
+
+ static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
+ DisplayTransactionTest* test) {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+
+ // In order to prevent compostition engine calling into HWComposer, we
+ // 1. turn off the use of HWC virtual displays,
+ // 2. provide a GpuVirtualDisplayIdGenerator which always returns some fake ID
+ // 3. override the ID by calling setDisplayIdForTesting()
+
+ ON_CALL(test->mFlinger.gpuVirtualDisplayIdGenerator(), nextId())
+ .WillByDefault(Return(GpuVirtualDisplayId(0)));
+
+ auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
+ .setUseHwcVirtualDisplays(false)
+ .setPixels({Base::WIDTH, Base::HEIGHT})
+ .setIsSecure(static_cast<bool>(Base::SECURE))
+ .setPowerAdvisor(&test->mPowerAdvisor)
+ .setName(std::string("Injected display for ") +
+ test_info->test_case_name() + "." + test_info->name())
+ .setGpuVirtualDisplayIdGenerator(
+ test->mFlinger.gpuVirtualDisplayIdGenerator())
+ .build();
+
+ auto compositionDisplay =
+ compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+ ceDisplayArgs);
+ compositionDisplay->setDisplayIdForTesting(Base::DISPLAY_ID::get());
+
+ // Insert display data so that the HWC thinks it created the virtual display.
+ if (const auto displayId = Base::DISPLAY_ID::get();
+ HalVirtualDisplayId::tryCast(displayId)) {
+ test->mFlinger.mutableHwcDisplayData().try_emplace(displayId);
+ }
+
+ return compositionDisplay;
+ }
+
+ static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
+ Base::setupNativeWindowSurfaceCreationCallExpectations(test);
+ EXPECT_CALL(*test->mNativeWindow, setSwapInterval(0)).Times(1);
+ }
+
+ static void setupHwcVirtualDisplayCreationCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, createVirtualDisplay(Base::WIDTH, Base::HEIGHT, _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(Self::HWC_DISPLAY_ID), Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_)).WillOnce(Return(Error::NONE));
+ }
+};
+
+// For this variant, the display is not a HWC display, so no HDR support should
+// be configured.
+struct NonHwcDisplayHdrSupportVariant {
+ static constexpr bool HDR10_PLUS_SUPPORTED = false;
+ static constexpr bool HDR10_SUPPORTED = false;
+ static constexpr bool HDR_HLG_SUPPORTED = false;
+ static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getHdrCapabilities(_, _, _, _, _)).Times(0);
+ }
+};
+
+// For this variant, the composer should respond with am empty list of HDR
+// modes, so no HDR support should be configured.
+template <typename Display>
+struct HdrNotSupportedVariant {
+ static constexpr bool HDR10_PLUS_SUPPORTED = false;
+ static constexpr bool HDR10_SUPPORTED = false;
+ static constexpr bool HDR_HLG_SUPPORTED = false;
+ static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>()), Return(Error::NONE)));
+ }
+};
+
+struct NonHwcPerFrameMetadataSupportVariant {
+ static constexpr int PER_FRAME_METADATA_KEYS = 0;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(_)).Times(0);
+ }
+};
+
+template <typename Display>
+struct NoPerFrameMetadataSupportVariant {
+ static constexpr int PER_FRAME_METADATA_KEYS = 0;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
+ .WillOnce(Return(std::vector<PerFrameMetadataKey>()));
+ }
+};
+
+// For this variant, SurfaceFlinger should configure itself with wide display
+// support, but the display should respond with an empty list of supported color
+// modes. Wide-color support for the display should not be configured.
+template <typename Display>
+struct WideColorNotSupportedVariant {
+ static constexpr bool WIDE_COLOR_SUPPORTED = false;
+
+ static void injectConfigChange(DisplayTransactionTest* test) {
+ test->mFlinger.mutableUseColorManagement() = true;
+ test->mFlinger.mutableHasWideColorDisplay() = true;
+ }
+
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getColorModes(Display::HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<ColorMode>()), Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer, setColorMode(_, _, _)).Times(0);
+ }
+};
+
+// For this variant, SurfaceFlinger should not configure itself with wide
+// display support, so the display should not be configured for wide-color
+// support.
+struct WideColorSupportNotConfiguredVariant {
+ static constexpr bool WIDE_COLOR_SUPPORTED = false;
+
+ static void injectConfigChange(DisplayTransactionTest* test) {
+ test->mFlinger.mutableHasWideColorDisplay() = false;
+ test->mFlinger.mutableUseColorManagement() = false;
+ test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
+ }
+
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getColorModes(_, _)).Times(0);
+ EXPECT_CALL(*test->mComposer, getRenderIntents(_, _, _)).Times(0);
+ EXPECT_CALL(*test->mComposer, setColorMode(_, _, _)).Times(0);
+ }
+};
+
+/* ------------------------------------------------------------------------
+ * Typical display configurations to test
+ */
+
+template <typename DisplayPolicy, typename WideColorSupportPolicy, typename HdrSupportPolicy,
+ typename PerFrameMetadataSupportPolicy>
+struct Case {
+ using Display = DisplayPolicy;
+ using WideColorSupport = WideColorSupportPolicy;
+ using HdrSupport = HdrSupportPolicy;
+ using PerFrameMetadataSupport = PerFrameMetadataSupportPolicy;
+};
+
+using SimplePrimaryDisplayCase =
+ Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+ HdrNotSupportedVariant<PrimaryDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using SimpleExternalDisplayCase =
+ Case<ExternalDisplayVariant, WideColorNotSupportedVariant<ExternalDisplayVariant>,
+ HdrNotSupportedVariant<ExternalDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<ExternalDisplayVariant>>;
+using SimpleTertiaryDisplayCase =
+ Case<TertiaryDisplayVariant, WideColorNotSupportedVariant<TertiaryDisplayVariant>,
+ HdrNotSupportedVariant<TertiaryDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<TertiaryDisplayVariant>>;
+
+using NonHwcVirtualDisplayCase =
+ Case<NonHwcVirtualDisplayVariant<1024, 768, Secure::FALSE>,
+ WideColorSupportNotConfiguredVariant, NonHwcDisplayHdrSupportVariant,
+ NonHwcPerFrameMetadataSupportVariant>;
+using SimpleHwcVirtualDisplayVariant = HwcVirtualDisplayVariant<1024, 768, Secure::TRUE>;
+using HwcVirtualDisplayCase =
+ Case<SimpleHwcVirtualDisplayVariant, WideColorSupportNotConfiguredVariant,
+ HdrNotSupportedVariant<SimpleHwcVirtualDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<SimpleHwcVirtualDisplayVariant>>;
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp
deleted file mode 100644
index 9dc4193..0000000
--- a/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "LibSurfaceFlingerUnittests"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <log/log.h>
-
-#include "AsyncCallRecorder.h"
-#include "Scheduler/EventControlThread.h"
-
-namespace android {
-namespace {
-
-using namespace std::chrono_literals;
-using testing::_;
-
-class EventControlThreadTest : public testing::Test {
-protected:
- EventControlThreadTest();
- ~EventControlThreadTest() override;
-
- void createThread();
-
- void expectVSyncEnableCallbackCalled(bool enable);
-
- AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder;
-
- std::unique_ptr<EventControlThread> mThread;
-};
-
-EventControlThreadTest::EventControlThreadTest() {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
-}
-
-EventControlThreadTest::~EventControlThreadTest() {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
-}
-
-void EventControlThreadTest::createThread() {
- mThread = std::make_unique<android::impl::EventControlThread>(
- mVSyncSetEnabledCallRecorder.getInvocable());
-}
-
-void EventControlThreadTest::expectVSyncEnableCallbackCalled(bool expectedEnabled) {
- auto args = mVSyncSetEnabledCallRecorder.waitForCall();
- ASSERT_TRUE(args.has_value());
- EXPECT_EQ(std::get<0>(args.value()), expectedEnabled);
-}
-
-/* ------------------------------------------------------------------------
- * Test cases
- */
-
-TEST_F(EventControlThreadTest, signalsVSyncDisabledOnStartup) {
- createThread();
-
- // On thread start, there should be an automatic explicit call to disable
- // vsyncs
- expectVSyncEnableCallbackCalled(false);
-}
-
-TEST_F(EventControlThreadTest, signalsVSyncDisabledOnce) {
- createThread();
- expectVSyncEnableCallbackCalled(false);
-
- mThread->setVsyncEnabled(false);
-
- EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
-}
-
-TEST_F(EventControlThreadTest, signalsVSyncEnabledThenDisabled) {
- createThread();
- expectVSyncEnableCallbackCalled(false);
-
- mThread->setVsyncEnabled(true);
-
- expectVSyncEnableCallbackCalled(true);
-
- mThread->setVsyncEnabled(false);
-
- expectVSyncEnableCallbackCalled(false);
-}
-
-TEST_F(EventControlThreadTest, signalsVSyncEnabledOnce) {
- createThread();
- expectVSyncEnableCallbackCalled(false);
-
- mThread->setVsyncEnabled(true);
-
- expectVSyncEnableCallbackCalled(true);
-
- mThread->setVsyncEnabled(true);
-
- EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
-}
-
-} // namespace
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index b90b566..b4a1481 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wextra"
+
#undef LOG_TAG
#define LOG_TAG "LibSurfaceFlingerUnittests"
@@ -23,12 +27,13 @@
#include <utils/Errors.h>
#include "AsyncCallRecorder.h"
+#include "DisplayHardware/DisplayMode.h"
#include "Scheduler/EventThread.h"
-#include "Scheduler/HwcStrongTypes.h"
using namespace std::chrono_literals;
using namespace std::placeholders;
+using namespace android::flag_operators;
using testing::_;
using testing::Invoke;
@@ -36,17 +41,19 @@
namespace {
-constexpr PhysicalDisplayId INTERNAL_DISPLAY_ID = 111;
-constexpr PhysicalDisplayId EXTERNAL_DISPLAY_ID = 222;
-constexpr PhysicalDisplayId DISPLAY_ID_64BIT = 0xabcd12349876fedcULL;
-
+constexpr PhysicalDisplayId INTERNAL_DISPLAY_ID(111);
+constexpr PhysicalDisplayId EXTERNAL_DISPLAY_ID(222);
+constexpr PhysicalDisplayId DISPLAY_ID_64BIT(0xabcd12349876fedcULL);
+constexpr std::chrono::duration VSYNC_PERIOD(16ms);
class MockVSyncSource : public VSyncSource {
public:
const char* getName() const override { return "test"; }
MOCK_METHOD1(setVSyncEnabled, void(bool));
MOCK_METHOD1(setCallback, void(VSyncSource::Callback*));
- MOCK_METHOD1(setPhaseOffset, void(nsecs_t));
+ MOCK_METHOD2(setDuration,
+ void(std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration));
MOCK_METHOD1(pauseVsyncCallback, void(bool));
MOCK_CONST_METHOD1(dump, void(std::string&));
};
@@ -57,9 +64,11 @@
protected:
class MockEventThreadConnection : public EventThreadConnection {
public:
- MockEventThreadConnection(impl::EventThread* eventThread, ResyncCallback&& resyncCallback,
- ISurfaceComposer::ConfigChanged configChanged)
- : EventThreadConnection(eventThread, std::move(resyncCallback), configChanged) {}
+ MockEventThreadConnection(impl::EventThread* eventThread, uid_t callingUid,
+ ResyncCallback&& resyncCallback,
+ ISurfaceComposer::EventRegistrationFlags eventRegistration)
+ : EventThreadConnection(eventThread, callingUid, std::move(resyncCallback),
+ eventRegistration) {}
MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event));
};
@@ -70,11 +79,14 @@
~EventThreadTest() override;
void createThread(std::unique_ptr<VSyncSource>);
- sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder,
- ISurfaceComposer::ConfigChanged configChanged);
+ sp<MockEventThreadConnection> createConnection(
+ ConnectionEventRecorder& recorder,
+ ISurfaceComposer::EventRegistrationFlags eventRegistration = {},
+ uid_t ownerUid = mConnectionUid);
void expectVSyncSetEnabledCallReceived(bool expectedState);
- void expectVSyncSetPhaseOffsetCallReceived(nsecs_t expectedPhaseOffset);
+ void expectVSyncSetDurationCallReceived(std::chrono::nanoseconds expectedDuration,
+ std::chrono::nanoseconds expectedReadyDuration);
VSyncSource::Callback* expectVSyncSetCallbackCallReceived();
void expectInterceptCallReceived(nsecs_t expectedTimestamp);
void expectVsyncEventReceivedByConnection(const char* name,
@@ -86,18 +98,28 @@
void expectConfigChangedEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
int32_t expectedConfigId,
nsecs_t expectedVsyncPeriod);
+ void expectThrottleVsyncReceived(nsecs_t expectedTimestamp, uid_t);
+ void expectUidFrameRateMappingEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
+ std::vector<FrameRateOverride>);
AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder;
AsyncCallRecorder<void (*)(VSyncSource::Callback*)> mVSyncSetCallbackCallRecorder;
- AsyncCallRecorder<void (*)(nsecs_t)> mVSyncSetPhaseOffsetCallRecorder;
+ AsyncCallRecorder<void (*)(std::chrono::nanoseconds, std::chrono::nanoseconds)>
+ mVSyncSetDurationCallRecorder;
AsyncCallRecorder<void (*)()> mResyncCallRecorder;
AsyncCallRecorder<void (*)(nsecs_t)> mInterceptVSyncCallRecorder;
+ AsyncCallRecorder<void (*)(nsecs_t, uid_t)> mThrottleVsyncCallRecorder;
ConnectionEventRecorder mConnectionEventCallRecorder{0};
+ ConnectionEventRecorder mThrottledConnectionEventCallRecorder{0};
MockVSyncSource* mVSyncSource;
VSyncSource::Callback* mCallback = nullptr;
std::unique_ptr<impl::EventThread> mThread;
sp<MockEventThreadConnection> mConnection;
+ sp<MockEventThreadConnection> mThrottledConnection;
+
+ static constexpr uid_t mConnectionUid = 443;
+ static constexpr uid_t mThrottledConnectionUid = 177;
};
EventThreadTest::EventThreadTest() {
@@ -114,12 +136,16 @@
EXPECT_CALL(*mVSyncSource, setCallback(_))
.WillRepeatedly(Invoke(mVSyncSetCallbackCallRecorder.getInvocable()));
- EXPECT_CALL(*mVSyncSource, setPhaseOffset(_))
- .WillRepeatedly(Invoke(mVSyncSetPhaseOffsetCallRecorder.getInvocable()));
+ EXPECT_CALL(*mVSyncSource, setDuration(_, _))
+ .WillRepeatedly(Invoke(mVSyncSetDurationCallRecorder.getInvocable()));
createThread(std::move(vsyncSource));
mConnection = createConnection(mConnectionEventCallRecorder,
- ISurfaceComposer::eConfigChangedDispatch);
+ ISurfaceComposer::EventRegistration::modeChanged |
+ ISurfaceComposer::EventRegistration::frameRateOverride);
+ mThrottledConnection = createConnection(mThrottledConnectionEventCallRecorder,
+ ISurfaceComposer::EventRegistration::modeChanged,
+ mThrottledConnectionUid);
// A display must be connected for VSYNC events to be delivered.
mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, true);
@@ -136,8 +162,18 @@
}
void EventThreadTest::createThread(std::unique_ptr<VSyncSource> source) {
+ const auto throttleVsync = [&](nsecs_t expectedVsyncTimestamp, uid_t uid) {
+ mThrottleVsyncCallRecorder.getInvocable()(expectedVsyncTimestamp, uid);
+ return (uid == mThrottledConnectionUid);
+ };
+ const auto getVsyncPeriod = [](uid_t uid) {
+ return VSYNC_PERIOD.count();
+ };
+
mThread = std::make_unique<impl::EventThread>(std::move(source),
- mInterceptVSyncCallRecorder.getInvocable());
+ /*tokenManager=*/nullptr,
+ mInterceptVSyncCallRecorder.getInvocable(),
+ throttleVsync, getVsyncPeriod);
// EventThread should register itself as VSyncSource callback.
mCallback = expectVSyncSetCallbackCallReceived();
@@ -145,10 +181,11 @@
}
sp<EventThreadTest::MockEventThreadConnection> EventThreadTest::createConnection(
- ConnectionEventRecorder& recorder, ISurfaceComposer::ConfigChanged configChanged) {
+ ConnectionEventRecorder& recorder,
+ ISurfaceComposer::EventRegistrationFlags eventRegistration, uid_t ownerUid) {
sp<MockEventThreadConnection> connection =
- new MockEventThreadConnection(mThread.get(), mResyncCallRecorder.getInvocable(),
- configChanged);
+ new MockEventThreadConnection(mThread.get(), ownerUid,
+ mResyncCallRecorder.getInvocable(), eventRegistration);
EXPECT_CALL(*connection, postEvent(_)).WillRepeatedly(Invoke(recorder.getInvocable()));
return connection;
}
@@ -159,10 +196,12 @@
EXPECT_EQ(expectedState, std::get<0>(args.value()));
}
-void EventThreadTest::expectVSyncSetPhaseOffsetCallReceived(nsecs_t expectedPhaseOffset) {
- auto args = mVSyncSetPhaseOffsetCallRecorder.waitForCall();
+void EventThreadTest::expectVSyncSetDurationCallReceived(
+ std::chrono::nanoseconds expectedDuration, std::chrono::nanoseconds expectedReadyDuration) {
+ auto args = mVSyncSetDurationCallRecorder.waitForCall();
ASSERT_TRUE(args.has_value());
- EXPECT_EQ(expectedPhaseOffset, std::get<0>(args.value()));
+ EXPECT_EQ(expectedDuration, std::get<0>(args.value()));
+ EXPECT_EQ(expectedReadyDuration, std::get<1>(args.value()));
}
VSyncSource::Callback* EventThreadTest::expectVSyncSetCallbackCallReceived() {
@@ -176,6 +215,13 @@
EXPECT_EQ(expectedTimestamp, std::get<0>(args.value()));
}
+void EventThreadTest::expectThrottleVsyncReceived(nsecs_t expectedTimestamp, uid_t uid) {
+ auto args = mThrottleVsyncCallRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value());
+ EXPECT_EQ(expectedTimestamp, std::get<0>(args.value()));
+ EXPECT_EQ(uid, std::get<1>(args.value()));
+}
+
void EventThreadTest::expectVsyncEventReceivedByConnection(
const char* name, ConnectionEventRecorder& connectionEventRecorder,
nsecs_t expectedTimestamp, unsigned expectedCount) {
@@ -214,10 +260,29 @@
auto args = mConnectionEventCallRecorder.waitForCall();
ASSERT_TRUE(args.has_value());
const auto& event = std::get<0>(args.value());
- EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED, event.header.type);
+ EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE, event.header.type);
EXPECT_EQ(expectedDisplayId, event.header.displayId);
- EXPECT_EQ(expectedConfigId, event.config.configId);
- EXPECT_EQ(expectedVsyncPeriod, event.config.vsyncPeriod);
+ EXPECT_EQ(expectedConfigId, event.modeChange.modeId);
+ EXPECT_EQ(expectedVsyncPeriod, event.modeChange.vsyncPeriod);
+}
+
+void EventThreadTest::expectUidFrameRateMappingEventReceivedByConnection(
+ PhysicalDisplayId expectedDisplayId, std::vector<FrameRateOverride> expectedOverrides) {
+ for (const auto [uid, frameRateHz] : expectedOverrides) {
+ auto args = mConnectionEventCallRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value());
+ const auto& event = std::get<0>(args.value());
+ EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE, event.header.type);
+ EXPECT_EQ(expectedDisplayId, event.header.displayId);
+ EXPECT_EQ(uid, event.frameRateOverride.uid);
+ EXPECT_EQ(frameRateHz, event.frameRateOverride.frameRateHz);
+ }
+
+ auto args = mConnectionEventCallRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value());
+ const auto& event = std::get<0>(args.value());
+ EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH, event.header.type);
+ EXPECT_EQ(expectedDisplayId, event.header.displayId);
}
namespace {
@@ -229,7 +294,7 @@
TEST_F(EventThreadTest, canCreateAndDestroyThreadWithNoEventsSent) {
EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
EXPECT_FALSE(mVSyncSetCallbackCallRecorder.waitForCall(0us).has_value());
- EXPECT_FALSE(mVSyncSetPhaseOffsetCallRecorder.waitForCall(0us).has_value());
+ EXPECT_FALSE(mVSyncSetDurationCallRecorder.waitForCall(0us).has_value());
EXPECT_FALSE(mResyncCallRecorder.waitForCall(0us).has_value());
EXPECT_FALSE(mInterceptVSyncCallRecorder.waitForCall(0us).has_value());
EXPECT_FALSE(mConnectionEventCallRecorder.waitForCall(0us).has_value());
@@ -258,15 +323,17 @@
// Use the received callback to signal a first vsync event.
// The interceptor should receive the event, as well as the connection.
- mCallback->onVSyncEvent(123, 456);
+ mCallback->onVSyncEvent(123, 456, 789);
expectInterceptCallReceived(123);
+ expectThrottleVsyncReceived(456, mConnectionUid);
expectVsyncEventReceivedByConnection(123, 1u);
// Use the received callback to signal a second vsync event.
- // The interceptor should receive the event, but the the connection should
+ // The interceptor should receive the event, but the connection should
// not as it was only interested in the first.
- mCallback->onVSyncEvent(456, 123);
+ mCallback->onVSyncEvent(456, 123, 0);
expectInterceptCallReceived(456);
+ EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
// EventThread should also detect that at this point that it does not need
@@ -277,9 +344,7 @@
TEST_F(EventThreadTest, setVsyncRateZeroPostsNoVSyncEventsToThatConnection) {
// Create a first connection, register it, and request a vsync rate of zero.
ConnectionEventRecorder firstConnectionEventRecorder{0};
- sp<MockEventThreadConnection> firstConnection =
- createConnection(firstConnectionEventRecorder,
- ISurfaceComposer::eConfigChangedSuppress);
+ sp<MockEventThreadConnection> firstConnection = createConnection(firstConnectionEventRecorder);
mThread->setVsyncRate(0, firstConnection);
// By itself, this should not enable vsync events
@@ -289,8 +354,7 @@
// However if there is another connection which wants events at a nonzero rate.....
ConnectionEventRecorder secondConnectionEventRecorder{0};
sp<MockEventThreadConnection> secondConnection =
- createConnection(secondConnectionEventRecorder,
- ISurfaceComposer::eConfigChangedSuppress);
+ createConnection(secondConnectionEventRecorder);
mThread->setVsyncRate(1, secondConnection);
// EventThread should enable vsync callbacks.
@@ -299,7 +363,7 @@
// Send a vsync event. EventThread should then make a call to the
// interceptor, and the second connection. The first connection should not
// get the event.
- mCallback->onVSyncEvent(123, 456);
+ mCallback->onVSyncEvent(123, 456, 0);
expectInterceptCallReceived(123);
EXPECT_FALSE(firstConnectionEventRecorder.waitForUnexpectedCall().has_value());
expectVsyncEventReceivedByConnection("secondConnection", secondConnectionEventRecorder, 123,
@@ -314,18 +378,21 @@
// Send a vsync event. EventThread should then make a call to the
// interceptor, and the connection.
- mCallback->onVSyncEvent(123, 456);
+ mCallback->onVSyncEvent(123, 456, 789);
expectInterceptCallReceived(123);
+ expectThrottleVsyncReceived(456, mConnectionUid);
expectVsyncEventReceivedByConnection(123, 1u);
// A second event should go to the same places.
- mCallback->onVSyncEvent(456, 123);
+ mCallback->onVSyncEvent(456, 123, 0);
expectInterceptCallReceived(456);
+ expectThrottleVsyncReceived(123, mConnectionUid);
expectVsyncEventReceivedByConnection(456, 2u);
// A third event should go to the same places.
- mCallback->onVSyncEvent(789, 777);
+ mCallback->onVSyncEvent(789, 777, 111);
expectInterceptCallReceived(789);
+ expectThrottleVsyncReceived(777, mConnectionUid);
expectVsyncEventReceivedByConnection(789, 3u);
}
@@ -336,22 +403,25 @@
expectVSyncSetEnabledCallReceived(true);
// The first event will be seen by the interceptor, and not the connection.
- mCallback->onVSyncEvent(123, 456);
+ mCallback->onVSyncEvent(123, 456, 789);
expectInterceptCallReceived(123);
EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
// The second event will be seen by the interceptor and the connection.
- mCallback->onVSyncEvent(456, 123);
+ mCallback->onVSyncEvent(456, 123, 0);
expectInterceptCallReceived(456);
expectVsyncEventReceivedByConnection(456, 2u);
+ EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
// The third event will be seen by the interceptor, and not the connection.
- mCallback->onVSyncEvent(789, 777);
+ mCallback->onVSyncEvent(789, 777, 744);
expectInterceptCallReceived(789);
EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
// The fourth event will be seen by the interceptor and the connection.
- mCallback->onVSyncEvent(101112, 7847);
+ mCallback->onVSyncEvent(101112, 7847, 86);
expectInterceptCallReceived(101112);
expectVsyncEventReceivedByConnection(101112, 4u);
}
@@ -366,7 +436,7 @@
mConnection = nullptr;
// The first event will be seen by the interceptor, and not the connection.
- mCallback->onVSyncEvent(123, 456);
+ mCallback->onVSyncEvent(123, 456, 789);
expectInterceptCallReceived(123);
EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
@@ -376,9 +446,7 @@
TEST_F(EventThreadTest, connectionsRemovedIfEventDeliveryError) {
ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY};
- sp<MockEventThreadConnection> errorConnection =
- createConnection(errorConnectionEventRecorder,
- ISurfaceComposer::eConfigChangedSuppress);
+ sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
mThread->setVsyncRate(1, errorConnection);
// EventThread should enable vsync callbacks.
@@ -386,13 +454,13 @@
// The first event will be seen by the interceptor, and by the connection,
// which then returns an error.
- mCallback->onVSyncEvent(123, 456);
+ mCallback->onVSyncEvent(123, 456, 789);
expectInterceptCallReceived(123);
expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
// A subsequent event will be seen by the interceptor and not by the
// connection.
- mCallback->onVSyncEvent(456, 123);
+ mCallback->onVSyncEvent(456, 123, 0);
expectInterceptCallReceived(456);
EXPECT_FALSE(errorConnectionEventRecorder.waitForUnexpectedCall().has_value());
@@ -401,38 +469,33 @@
}
TEST_F(EventThreadTest, tracksEventConnections) {
- EXPECT_EQ(1, mThread->getEventThreadConnectionCount());
- ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY};
- sp<MockEventThreadConnection> errorConnection =
- createConnection(errorConnectionEventRecorder,
- ISurfaceComposer::eConfigChangedSuppress);
- mThread->setVsyncRate(1, errorConnection);
EXPECT_EQ(2, mThread->getEventThreadConnectionCount());
+ ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY};
+ sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
+ mThread->setVsyncRate(1, errorConnection);
+ EXPECT_EQ(3, mThread->getEventThreadConnectionCount());
ConnectionEventRecorder secondConnectionEventRecorder{0};
sp<MockEventThreadConnection> secondConnection =
- createConnection(secondConnectionEventRecorder,
- ISurfaceComposer::eConfigChangedSuppress);
+ createConnection(secondConnectionEventRecorder);
mThread->setVsyncRate(1, secondConnection);
- EXPECT_EQ(3, mThread->getEventThreadConnectionCount());
+ EXPECT_EQ(4, mThread->getEventThreadConnectionCount());
// EventThread should enable vsync callbacks.
expectVSyncSetEnabledCallReceived(true);
// The first event will be seen by the interceptor, and by the connection,
// which then returns an error.
- mCallback->onVSyncEvent(123, 456);
+ mCallback->onVSyncEvent(123, 456, 789);
expectInterceptCallReceived(123);
expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
expectVsyncEventReceivedByConnection("successConnection", secondConnectionEventRecorder, 123,
1u);
- EXPECT_EQ(2, mThread->getEventThreadConnectionCount());
+ EXPECT_EQ(3, mThread->getEventThreadConnectionCount());
}
TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) {
ConnectionEventRecorder errorConnectionEventRecorder{WOULD_BLOCK};
- sp<MockEventThreadConnection> errorConnection =
- createConnection(errorConnectionEventRecorder,
- ISurfaceComposer::eConfigChangedSuppress);
+ sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
mThread->setVsyncRate(1, errorConnection);
// EventThread should enable vsync callbacks.
@@ -440,13 +503,13 @@
// The first event will be seen by the interceptor, and by the connection,
// which then returns an non-fatal error.
- mCallback->onVSyncEvent(123, 456);
+ mCallback->onVSyncEvent(123, 456, 789);
expectInterceptCallReceived(123);
expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
// A subsequent event will be seen by the interceptor, and by the connection,
// which still then returns an non-fatal error.
- mCallback->onVSyncEvent(456, 123);
+ mCallback->onVSyncEvent(456, 123, 0);
expectInterceptCallReceived(456);
expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 456, 2u);
@@ -455,8 +518,8 @@
}
TEST_F(EventThreadTest, setPhaseOffsetForwardsToVSyncSource) {
- mThread->setPhaseOffset(321);
- expectVSyncSetPhaseOffsetCallReceived(321);
+ mThread->setDuration(321ns, 456ns);
+ expectVSyncSetDurationCallReceived(321ns, 456ns);
}
TEST_F(EventThreadTest, postHotplugInternalDisconnect) {
@@ -480,32 +543,93 @@
}
TEST_F(EventThreadTest, postConfigChangedPrimary) {
- mThread->onConfigChanged(INTERNAL_DISPLAY_ID, HwcConfigIndexType(7), 16666666);
+ mThread->onModeChanged(INTERNAL_DISPLAY_ID, DisplayModeId(7), 16666666);
expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7, 16666666);
}
TEST_F(EventThreadTest, postConfigChangedExternal) {
- mThread->onConfigChanged(EXTERNAL_DISPLAY_ID, HwcConfigIndexType(5), 16666666);
+ mThread->onModeChanged(EXTERNAL_DISPLAY_ID, DisplayModeId(5), 16666666);
expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5, 16666666);
}
TEST_F(EventThreadTest, postConfigChangedPrimary64bit) {
- mThread->onConfigChanged(DISPLAY_ID_64BIT, HwcConfigIndexType(7), 16666666);
+ mThread->onModeChanged(DISPLAY_ID_64BIT, DisplayModeId(7), 16666666);
expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7, 16666666);
}
TEST_F(EventThreadTest, suppressConfigChanged) {
ConnectionEventRecorder suppressConnectionEventRecorder{0};
sp<MockEventThreadConnection> suppressConnection =
- createConnection(suppressConnectionEventRecorder,
- ISurfaceComposer::eConfigChangedSuppress);
+ createConnection(suppressConnectionEventRecorder);
- mThread->onConfigChanged(INTERNAL_DISPLAY_ID, HwcConfigIndexType(9), 16666666);
+ mThread->onModeChanged(INTERNAL_DISPLAY_ID, DisplayModeId(9), 16666666);
expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9, 16666666);
auto args = suppressConnectionEventRecorder.waitForCall();
ASSERT_FALSE(args.has_value());
}
+TEST_F(EventThreadTest, postUidFrameRateMapping) {
+ const std::vector<FrameRateOverride> overrides = {
+ {.uid = 1, .frameRateHz = 20},
+ {.uid = 3, .frameRateHz = 40},
+ {.uid = 5, .frameRateHz = 60},
+ };
+
+ mThread->onFrameRateOverridesChanged(INTERNAL_DISPLAY_ID, overrides);
+ expectUidFrameRateMappingEventReceivedByConnection(INTERNAL_DISPLAY_ID, overrides);
+}
+
+TEST_F(EventThreadTest, suppressUidFrameRateMapping) {
+ const std::vector<FrameRateOverride> overrides = {
+ {.uid = 1, .frameRateHz = 20},
+ {.uid = 3, .frameRateHz = 40},
+ {.uid = 5, .frameRateHz = 60},
+ };
+
+ ConnectionEventRecorder suppressConnectionEventRecorder{0};
+ sp<MockEventThreadConnection> suppressConnection =
+ createConnection(suppressConnectionEventRecorder);
+
+ mThread->onFrameRateOverridesChanged(INTERNAL_DISPLAY_ID, overrides);
+ expectUidFrameRateMappingEventReceivedByConnection(INTERNAL_DISPLAY_ID, overrides);
+
+ auto args = suppressConnectionEventRecorder.waitForCall();
+ ASSERT_FALSE(args.has_value());
+}
+
+TEST_F(EventThreadTest, requestNextVsyncWithThrottleVsyncDoesntPostVSync) {
+ // Signal that we want the next vsync event to be posted to the throttled connection
+ mThread->requestNextVsync(mThrottledConnection);
+
+ // EventThread should immediately request a resync.
+ EXPECT_TRUE(mResyncCallRecorder.waitForCall().has_value());
+
+ // EventThread should enable vsync callbacks.
+ expectVSyncSetEnabledCallReceived(true);
+
+ // Use the received callback to signal a first vsync event.
+ // The interceptor should receive the event, but not the connection.
+ mCallback->onVSyncEvent(123, 456, 789);
+ expectInterceptCallReceived(123);
+ expectThrottleVsyncReceived(456, mThrottledConnectionUid);
+ mThrottledConnectionEventCallRecorder.waitForUnexpectedCall();
+
+ // Use the received callback to signal a second vsync event.
+ // The interceptor should receive the event, but the connection should
+ // not as it was only interested in the first.
+ mCallback->onVSyncEvent(456, 123, 0);
+ expectInterceptCallReceived(456);
+ expectThrottleVsyncReceived(123, mThrottledConnectionUid);
+ EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
+
+ // EventThread should not change the vsync state as it didn't send the event
+ // yet
+ EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
+}
+
} // namespace
} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wextra"
diff --git a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
deleted file mode 100644
index b50ddf5..0000000
--- a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2019 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.
- */
-
-#pragma once
-
-#include <gmock/gmock.h>
-
-#include "Scheduler/PhaseOffsets.h"
-
-namespace android::scheduler {
-
-struct FakePhaseOffsets : PhaseConfiguration {
- static constexpr nsecs_t FAKE_PHASE_OFFSET_NS = 0;
-
- Offsets getOffsetsForRefreshRate(float) const override { return getCurrentOffsets(); }
-
- Offsets getCurrentOffsets() const override {
- return {{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
- {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
- {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}};
- }
-
- void setRefreshRateFps(float) override {}
- void dump(std::string&) const override {}
-};
-
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/FakeVsyncConfiguration.h b/services/surfaceflinger/tests/unittests/FakeVsyncConfiguration.h
new file mode 100644
index 0000000..e890a62
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FakeVsyncConfiguration.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2019 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.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "Scheduler/VsyncConfiguration.h"
+
+namespace android::scheduler {
+
+struct FakePhaseOffsets : VsyncConfiguration {
+ static constexpr nsecs_t FAKE_PHASE_OFFSET_NS = 0;
+ static constexpr auto FAKE_DURATION_OFFSET_NS = std::chrono::nanoseconds(0);
+
+ VsyncConfigSet getConfigsForRefreshRate(Fps) const override { return getCurrentConfigs(); }
+
+ VsyncConfigSet getCurrentConfigs() const override {
+ return {{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS,
+ FAKE_DURATION_OFFSET_NS},
+ {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS,
+ FAKE_DURATION_OFFSET_NS},
+ {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS,
+ FAKE_DURATION_OFFSET_NS}};
+ }
+
+ void reset() override {}
+ void setRefreshRateFps(Fps) override {}
+ void dump(std::string&) const override {}
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
new file mode 100644
index 0000000..dec0ff5
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "FpsReporterTest"
+
+#include <android/gui/BnFpsListener.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/LayerMetadata.h>
+
+#include "BufferQueueLayer.h"
+#include "BufferStateLayer.h"
+#include "EffectLayer.h"
+#include "FpsReporter.h"
+#include "Layer.h"
+#include "TestableSurfaceFlinger.h"
+#include "fake/FakeClock.h"
+#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/MockEventThread.h"
+#include "mock/MockFrameTimeline.h"
+#include "mock/MockVsyncController.h"
+
+namespace android {
+
+using testing::_;
+using testing::DoAll;
+using testing::Mock;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::UnorderedElementsAre;
+
+using android::Hwc2::IComposer;
+using android::Hwc2::IComposerClient;
+
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
+struct TestableFpsListener : public gui::BnFpsListener {
+ TestableFpsListener() {}
+
+ float lastReportedFps = 0;
+
+ binder::Status onFpsReported(float fps) override {
+ lastReportedFps = fps;
+ return binder::Status::ok();
+ }
+};
+
+/**
+ * This class covers all the test that are related to refresh rate selection.
+ */
+class FpsReporterTest : public testing::Test {
+public:
+ FpsReporterTest();
+ ~FpsReporterTest() override;
+
+protected:
+ static constexpr int DEFAULT_DISPLAY_WIDTH = 1920;
+ static constexpr int DEFAULT_DISPLAY_HEIGHT = 1024;
+ static constexpr uint32_t WIDTH = 100;
+ static constexpr uint32_t HEIGHT = 100;
+ static constexpr uint32_t LAYER_FLAGS = 0;
+ static constexpr int32_t PRIORITY_UNSET = -1;
+
+ void setupScheduler();
+ void setupComposer(uint32_t virtualDisplayCount);
+ sp<BufferStateLayer> createBufferStateLayer(LayerMetadata metadata);
+
+ TestableSurfaceFlinger mFlinger;
+ Hwc2::mock::Composer* mComposer = nullptr;
+ mock::FrameTimeline mFrameTimeline =
+ mock::FrameTimeline(std::make_shared<impl::TimeStats>(), 0);
+
+ sp<Client> mClient;
+ sp<Layer> mParent;
+ sp<Layer> mTarget;
+ sp<Layer> mChild;
+ sp<Layer> mGrandChild;
+ sp<Layer> mUnrelated;
+
+ sp<TestableFpsListener> mFpsListener;
+ fake::FakeClock* mClock = new fake::FakeClock();
+ sp<FpsReporter> mFpsReporter =
+ new FpsReporter(mFrameTimeline, *(mFlinger.flinger()), std::unique_ptr<Clock>(mClock));
+};
+
+FpsReporterTest::FpsReporterTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+
+ setupScheduler();
+ setupComposer(0);
+ mFpsListener = new TestableFpsListener();
+}
+
+FpsReporterTest::~FpsReporterTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+sp<BufferStateLayer> FpsReporterTest::createBufferStateLayer(LayerMetadata metadata = {}) {
+ sp<Client> client;
+ LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", WIDTH, HEIGHT,
+ LAYER_FLAGS, metadata);
+ return new BufferStateLayer(args);
+}
+
+void FpsReporterTest::setupScheduler() {
+ auto eventThread = std::make_unique<mock::EventThread>();
+ auto sfEventThread = std::make_unique<mock::EventThread>();
+
+ EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
+ EXPECT_CALL(*eventThread, createEventConnection(_, _))
+ .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+ ResyncCallback())));
+
+ EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
+ EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
+ .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+ ResyncCallback())));
+
+ auto vsyncController = std::make_unique<mock::VsyncController>();
+ auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
+
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*vsyncTracker, currentPeriod())
+ .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
+ std::move(eventThread), std::move(sfEventThread));
+}
+
+void FpsReporterTest::setupComposer(uint32_t virtualDisplayCount) {
+ mComposer = new Hwc2::mock::Composer();
+ EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount));
+ mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
+
+ Mock::VerifyAndClear(mComposer);
+}
+
+namespace {
+
+TEST_F(FpsReporterTest, callsListeners) {
+ mParent = createBufferStateLayer();
+ const constexpr int32_t kTaskId = 12;
+ LayerMetadata targetMetadata;
+ targetMetadata.setInt32(METADATA_TASK_ID, kTaskId);
+ mTarget = createBufferStateLayer(targetMetadata);
+ mChild = createBufferStateLayer();
+ mGrandChild = createBufferStateLayer();
+ mUnrelated = createBufferStateLayer();
+ mParent->addChild(mTarget);
+ mTarget->addChild(mChild);
+ mChild->addChild(mGrandChild);
+ mParent->commitChildList();
+ mFlinger.mutableCurrentState().layersSortedByZ.add(mParent);
+ mFlinger.mutableCurrentState().layersSortedByZ.add(mTarget);
+ mFlinger.mutableCurrentState().layersSortedByZ.add(mChild);
+ mFlinger.mutableCurrentState().layersSortedByZ.add(mGrandChild);
+
+ float expectedFps = 44.0;
+
+ EXPECT_CALL(mFrameTimeline,
+ computeFps(UnorderedElementsAre(mTarget->getSequence(), mChild->getSequence(),
+ mGrandChild->getSequence())))
+ .WillOnce(Return(expectedFps));
+
+ mFpsReporter->addListener(mFpsListener, kTaskId);
+ mClock->advanceTime(600ms);
+ mFpsReporter->dispatchLayerFps();
+ EXPECT_EQ(expectedFps, mFpsListener->lastReportedFps);
+ mFpsReporter->removeListener(mFpsListener);
+ Mock::VerifyAndClearExpectations(&mFrameTimeline);
+
+ EXPECT_CALL(mFrameTimeline, computeFps(_)).Times(0);
+ mFpsReporter->dispatchLayerFps();
+}
+
+TEST_F(FpsReporterTest, rateLimits) {
+ const constexpr int32_t kTaskId = 12;
+ LayerMetadata targetMetadata;
+ targetMetadata.setInt32(METADATA_TASK_ID, kTaskId);
+ mTarget = createBufferStateLayer(targetMetadata);
+ mFlinger.mutableCurrentState().layersSortedByZ.add(mTarget);
+
+ float firstFps = 44.0;
+ float secondFps = 53.0;
+
+ EXPECT_CALL(mFrameTimeline, computeFps(UnorderedElementsAre(mTarget->getSequence())))
+ .WillOnce(Return(firstFps))
+ .WillOnce(Return(secondFps));
+
+ mFpsReporter->addListener(mFpsListener, kTaskId);
+ mClock->advanceTime(600ms);
+ mFpsReporter->dispatchLayerFps();
+ EXPECT_EQ(firstFps, mFpsListener->lastReportedFps);
+ mClock->advanceTime(200ms);
+ mFpsReporter->dispatchLayerFps();
+ EXPECT_EQ(firstFps, mFpsListener->lastReportedFps);
+ mClock->advanceTime(200ms);
+ mFpsReporter->dispatchLayerFps();
+ EXPECT_EQ(firstFps, mFpsListener->lastReportedFps);
+ mClock->advanceTime(200ms);
+ mFpsReporter->dispatchLayerFps();
+ EXPECT_EQ(secondFps, mFpsListener->lastReportedFps);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FpsTest.cpp b/services/surfaceflinger/tests/unittests/FpsTest.cpp
new file mode 100644
index 0000000..db732cf
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FpsTest.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2020 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 "Fps.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+
+TEST(FpsTest, construct) {
+ Fps fpsDefault;
+ EXPECT_FALSE(fpsDefault.isValid());
+
+ Fps fps1(60.0f);
+ EXPECT_TRUE(fps1.isValid());
+ Fps fps2 = Fps::fromPeriodNsecs(static_cast<nsecs_t>(1e9f / 60.0f));
+ EXPECT_TRUE(fps2.isValid());
+ EXPECT_TRUE(fps1.equalsWithMargin(fps2));
+}
+
+TEST(FpsTest, compare) {
+ constexpr float kEpsilon = 1e-4f;
+ const Fps::EqualsInBuckets equalsInBuckets;
+ const Fps::EqualsWithMargin equalsWithMargin;
+
+ EXPECT_TRUE(Fps(60.0f).equalsWithMargin(Fps(60.f)));
+ EXPECT_TRUE(Fps(60.0f).equalsWithMargin(Fps(60.f - kEpsilon)));
+ EXPECT_TRUE(Fps(60.0f).equalsWithMargin(Fps(60.f + kEpsilon)));
+
+ EXPECT_TRUE(equalsInBuckets(Fps(60.0f), Fps(60.0f)));
+ EXPECT_TRUE(equalsInBuckets(Fps(60.0f), Fps(60.0f - kEpsilon)));
+ EXPECT_TRUE(equalsInBuckets(Fps(60.0f), Fps(60.0f + kEpsilon)));
+
+ EXPECT_TRUE(equalsWithMargin(Fps(60.0f), Fps(60.0f)));
+ EXPECT_TRUE(equalsWithMargin(Fps(60.0f), Fps(60.0f - kEpsilon)));
+ EXPECT_TRUE(equalsWithMargin(Fps(60.0f), Fps(60.0f + kEpsilon)));
+
+ EXPECT_TRUE(Fps(60.0f).lessThanOrEqualWithMargin(Fps(60.f + kEpsilon)));
+ EXPECT_TRUE(Fps(60.0f).lessThanOrEqualWithMargin(Fps(60.f)));
+ EXPECT_TRUE(Fps(60.0f).lessThanOrEqualWithMargin(Fps(60.f - kEpsilon)));
+
+ EXPECT_TRUE(Fps(60.0f).greaterThanOrEqualWithMargin(Fps(60.f + kEpsilon)));
+ EXPECT_TRUE(Fps(60.0f).greaterThanOrEqualWithMargin(Fps(60.f)));
+ EXPECT_TRUE(Fps(60.0f).greaterThanOrEqualWithMargin(Fps(60.f - kEpsilon)));
+
+ // Fps with difference of 1 should be different
+ EXPECT_FALSE(Fps(60.0f).equalsWithMargin(Fps(61.f)));
+ EXPECT_TRUE(Fps(60.0f).lessThanWithMargin(Fps(61.f)));
+ EXPECT_TRUE(Fps(60.0f).greaterThanWithMargin(Fps(59.f)));
+
+ // These are common refresh rates which should be different.
+ EXPECT_FALSE(Fps(60.0f).equalsWithMargin(Fps(59.94f)));
+ EXPECT_TRUE(Fps(60.0f).greaterThanWithMargin(Fps(59.94f)));
+ EXPECT_FALSE(equalsInBuckets(Fps(60.0f), Fps(59.94f)));
+ EXPECT_FALSE(equalsWithMargin(Fps(60.0f), Fps(59.94f)));
+ EXPECT_NE(std::hash<Fps>()(Fps(60.0f)), std::hash<Fps>()(Fps(59.94f)));
+
+ EXPECT_FALSE(Fps(30.0f).equalsWithMargin(Fps(29.97f)));
+ EXPECT_TRUE(Fps(30.0f).greaterThanWithMargin(Fps(29.97f)));
+ EXPECT_FALSE(equalsInBuckets(Fps(30.0f), Fps(29.97f)));
+ EXPECT_FALSE(equalsWithMargin(Fps(30.0f), Fps(29.97f)));
+ EXPECT_NE(std::hash<Fps>()(Fps(30.0f)), std::hash<Fps>()(Fps(29.97f)));
+}
+
+TEST(FpsTest, getIntValue) {
+ EXPECT_EQ(30, Fps(30.1f).getIntValue());
+ EXPECT_EQ(31, Fps(30.9f).getIntValue());
+ EXPECT_EQ(31, Fps(30.5f).getIntValue());
+}
+
+TEST(FpsTest, equalsInBucketsImpliesEqualHashes) {
+ constexpr float kStep = 1e-4f;
+ const Fps::EqualsInBuckets equals;
+ for (float fps = 30.0f; fps < 31.0f; fps += kStep) {
+ const Fps left(fps);
+ const Fps right(fps + kStep);
+ if (equals(left, right)) {
+ ASSERT_EQ(std::hash<Fps>()(left), std::hash<Fps>()(right))
+ << "left= " << left << " right=" << right;
+ }
+ }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
new file mode 100644
index 0000000..6ed6148
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -0,0 +1,2231 @@
+/*
+ * Copyright 2020 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 "gmock/gmock-spec-builders.h"
+#include "mock/MockTimeStats.h"
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <FrameTimeline/FrameTimeline.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <perfetto/trace/trace.pb.h>
+#include <cinttypes>
+
+using namespace std::chrono_literals;
+using testing::_;
+using testing::AtLeast;
+using testing::Contains;
+using FrameTimelineEvent = perfetto::protos::FrameTimelineEvent;
+using ProtoExpectedDisplayFrameStart =
+ perfetto::protos::FrameTimelineEvent_ExpectedDisplayFrameStart;
+using ProtoExpectedSurfaceFrameStart =
+ perfetto::protos::FrameTimelineEvent_ExpectedSurfaceFrameStart;
+using ProtoActualDisplayFrameStart = perfetto::protos::FrameTimelineEvent_ActualDisplayFrameStart;
+using ProtoActualSurfaceFrameStart = perfetto::protos::FrameTimelineEvent_ActualSurfaceFrameStart;
+using ProtoFrameEnd = perfetto::protos::FrameTimelineEvent_FrameEnd;
+using ProtoPresentType = perfetto::protos::FrameTimelineEvent_PresentType;
+using ProtoJankType = perfetto::protos::FrameTimelineEvent_JankType;
+using ProtoPredictionType = perfetto::protos::FrameTimelineEvent_PredictionType;
+
+namespace android::frametimeline {
+
+class FrameTimelineTest : public testing::Test {
+public:
+ FrameTimelineTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+
+ ~FrameTimelineTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+
+ static void SetUpTestSuite() {
+ // Need to initialize tracing in process for testing, and only once per test suite.
+ perfetto::TracingInitArgs args;
+ args.backends = perfetto::kInProcessBackend;
+ perfetto::Tracing::Initialize(args);
+ }
+
+ void SetUp() override {
+ mTimeStats = std::make_shared<mock::TimeStats>();
+ mFrameTimeline = std::make_unique<impl::FrameTimeline>(mTimeStats, kSurfaceFlingerPid,
+ kTestThresholds);
+ mFrameTimeline->registerDataSource();
+ mTokenManager = &mFrameTimeline->mTokenManager;
+ mTraceCookieCounter = &mFrameTimeline->mTraceCookieCounter;
+ maxDisplayFrames = &mFrameTimeline->mMaxDisplayFrames;
+ maxTokenRetentionTime = mTokenManager->kMaxRetentionTime;
+ }
+
+ // Each tracing session can be used for a single block of Start -> Stop.
+ static std::unique_ptr<perfetto::TracingSession> getTracingSessionForTest() {
+ perfetto::TraceConfig cfg;
+ cfg.set_duration_ms(500);
+ cfg.add_buffers()->set_size_kb(1024);
+ auto* ds_cfg = cfg.add_data_sources()->mutable_config();
+ ds_cfg->set_name(impl::FrameTimeline::kFrameTimelineDataSource);
+
+ auto tracingSession = perfetto::Tracing::NewTrace(perfetto::kInProcessBackend);
+ tracingSession->Setup(cfg);
+ return tracingSession;
+ }
+
+ std::vector<perfetto::protos::TracePacket> readFrameTimelinePacketsBlocking(
+ perfetto::TracingSession* tracingSession) {
+ std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
+ perfetto::protos::Trace trace;
+ EXPECT_TRUE(trace.ParseFromArray(raw_trace.data(), int(raw_trace.size())));
+
+ std::vector<perfetto::protos::TracePacket> packets;
+ for (const auto& packet : trace.packet()) {
+ if (!packet.has_frame_timeline_event()) {
+ continue;
+ }
+ packets.emplace_back(packet);
+ }
+ return packets;
+ }
+
+ void addEmptyDisplayFrame() {
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ // Trigger a flushPresentFence by calling setSfPresent for the next frame
+ mFrameTimeline->setSfPresent(2500, presentFence1);
+ }
+
+ void flushTokens(nsecs_t flushTime) {
+ std::lock_guard<std::mutex> lock(mTokenManager->mMutex);
+ mTokenManager->flushTokens(flushTime);
+ }
+
+ SurfaceFrame& getSurfaceFrame(size_t displayFrameIdx, size_t surfaceFrameIdx) {
+ std::lock_guard<std::mutex> lock(mFrameTimeline->mMutex);
+ return *(mFrameTimeline->mDisplayFrames[displayFrameIdx]
+ ->getSurfaceFrames()[surfaceFrameIdx]);
+ }
+
+ std::shared_ptr<impl::FrameTimeline::DisplayFrame> getDisplayFrame(size_t idx) {
+ std::lock_guard<std::mutex> lock(mFrameTimeline->mMutex);
+ return mFrameTimeline->mDisplayFrames[idx];
+ }
+
+ static bool compareTimelineItems(const TimelineItem& a, const TimelineItem& b) {
+ return a.startTime == b.startTime && a.endTime == b.endTime &&
+ a.presentTime == b.presentTime;
+ }
+
+ const std::map<int64_t, TokenManagerPrediction>& getPredictions() const {
+ return mTokenManager->mPredictions;
+ }
+
+ uint32_t getNumberOfDisplayFrames() const {
+ std::lock_guard<std::mutex> lock(mFrameTimeline->mMutex);
+ return static_cast<uint32_t>(mFrameTimeline->mDisplayFrames.size());
+ }
+
+ int64_t snoopCurrentTraceCookie() const { return mTraceCookieCounter->mTraceCookie; }
+
+ void flushTrace() {
+ using FrameTimelineDataSource = impl::FrameTimeline::FrameTimelineDataSource;
+ FrameTimelineDataSource::Trace(
+ [&](FrameTimelineDataSource::TraceContext ctx) { ctx.Flush(); });
+ }
+
+ std::shared_ptr<mock::TimeStats> mTimeStats;
+ std::unique_ptr<impl::FrameTimeline> mFrameTimeline;
+ impl::TokenManager* mTokenManager;
+ TraceCookieCounter* mTraceCookieCounter;
+ FenceToFenceTimeMap fenceFactory;
+ uint32_t* maxDisplayFrames;
+ nsecs_t maxTokenRetentionTime;
+ static constexpr pid_t kSurfaceFlingerPid = 666;
+ static constexpr nsecs_t kPresentThreshold = std::chrono::nanoseconds(2ns).count();
+ static constexpr nsecs_t kDeadlineThreshold = std::chrono::nanoseconds(2ns).count();
+ static constexpr nsecs_t kStartThreshold = std::chrono::nanoseconds(2ns).count();
+ static constexpr JankClassificationThresholds kTestThresholds{kPresentThreshold,
+ kDeadlineThreshold,
+ kStartThreshold};
+};
+
+static const std::string sLayerNameOne = "layer1";
+static const std::string sLayerNameTwo = "layer2";
+static constexpr const uid_t sUidOne = 0;
+static constexpr pid_t sPidOne = 10;
+static constexpr pid_t sPidTwo = 20;
+static constexpr int32_t sInputEventId = 5;
+static constexpr int32_t sLayerIdOne = 1;
+static constexpr int32_t sLayerIdTwo = 2;
+
+TEST_F(FrameTimelineTest, tokenManagerRemovesStalePredictions) {
+ int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
+ EXPECT_EQ(getPredictions().size(), 1u);
+ flushTokens(systemTime() + maxTokenRetentionTime);
+ int64_t token2 = mTokenManager->generateTokenForPredictions({10, 20, 30});
+ std::optional<TimelineItem> predictions = mTokenManager->getPredictionsForToken(token1);
+
+ // token1 should have expired
+ EXPECT_EQ(getPredictions().size(), 1u);
+ EXPECT_EQ(predictions.has_value(), false);
+
+ predictions = mTokenManager->getPredictionsForToken(token2);
+ EXPECT_EQ(compareTimelineItems(*predictions, TimelineItem(10, 20, 30)), true);
+}
+
+TEST_F(FrameTimelineTest, createSurfaceFrameForToken_getOwnerPidReturnsCorrectPid) {
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidTwo, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
+ EXPECT_EQ(surfaceFrame1->getOwnerPid(), sPidOne);
+ EXPECT_EQ(surfaceFrame2->getOwnerPid(), sPidTwo);
+}
+
+TEST_F(FrameTimelineTest, createSurfaceFrameForToken_noToken) {
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
+ EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::None);
+}
+
+TEST_F(FrameTimelineTest, createSurfaceFrameForToken_expiredToken) {
+ int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
+ flushTokens(systemTime() + maxTokenRetentionTime);
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
+
+ EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Expired);
+}
+
+TEST_F(FrameTimelineTest, createSurfaceFrameForToken_validToken) {
+ int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
+
+ EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Valid);
+ EXPECT_EQ(compareTimelineItems(surfaceFrame->getPredictions(), TimelineItem(10, 20, 30)), true);
+}
+
+TEST_F(FrameTimelineTest, createSurfaceFrameForToken_validInputEventId) {
+ int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
+ constexpr int32_t inputEventId = 1;
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken({token1, inputEventId}, sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
+
+ EXPECT_EQ(inputEventId, surfaceFrame->getInputEventId());
+}
+
+TEST_F(FrameTimelineTest, presentFenceSignaled_droppedFramesNotUpdated) {
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
+
+ // Set up the display frame
+ mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
+ surfaceFrame1->setDropTime(12);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Dropped);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ mFrameTimeline->setSfPresent(25, presentFence1);
+ presentFence1->signalForTest(30);
+
+ addEmptyDisplayFrame();
+
+ auto& droppedSurfaceFrame = getSurfaceFrame(0, 0);
+ EXPECT_EQ(droppedSurfaceFrame.getPresentState(), SurfaceFrame::PresentState::Dropped);
+ EXPECT_EQ(0u, droppedSurfaceFrame.getActuals().endTime);
+ EXPECT_EQ(12u, droppedSurfaceFrame.getDropTime());
+ EXPECT_EQ(droppedSurfaceFrame.getActuals().presentTime, 0);
+}
+
+TEST_F(FrameTimelineTest, presentFenceSignaled_presentedFramesUpdated) {
+ // Layer specific increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)).Times(2);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 30});
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdTwo, sLayerNameTwo,
+ sLayerNameTwo, /*isBuffer*/ true);
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ mFrameTimeline->setSfPresent(26, presentFence1);
+ auto displayFrame = getDisplayFrame(0);
+ auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+ auto& presentedSurfaceFrame2 = getSurfaceFrame(0, 1);
+ presentFence1->signalForTest(42);
+
+ // Fences haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+ EXPECT_EQ(presentedSurfaceFrame1.getActuals().presentTime, 0);
+ EXPECT_EQ(presentedSurfaceFrame2.getActuals().presentTime, 0);
+
+ addEmptyDisplayFrame();
+
+ // Fences have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 42);
+ EXPECT_EQ(presentedSurfaceFrame1.getActuals().presentTime, 42);
+ EXPECT_EQ(presentedSurfaceFrame2.getActuals().presentTime, 42);
+ EXPECT_NE(surfaceFrame1->getJankType(), std::nullopt);
+ EXPECT_NE(surfaceFrame2->getJankType(), std::nullopt);
+}
+
+TEST_F(FrameTimelineTest, displayFramesSlidingWindowMovesAfterLimit) {
+ // Insert kMaxDisplayFrames' count of DisplayFrames to fill the deque
+ int frameTimeFactor = 0;
+ // Layer specific increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(_))
+ .Times(static_cast<int32_t>(*maxDisplayFrames));
+ for (size_t i = 0; i < *maxDisplayFrames; i++) {
+ auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken = mTokenManager->generateTokenForPredictions(
+ {10 + frameTimeFactor, 20 + frameTimeFactor, 30 + frameTimeFactor});
+ int64_t sfToken = mTokenManager->generateTokenForPredictions(
+ {22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor});
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId},
+ sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
+ mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, Fps::fromPeriodNsecs(11));
+ surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame);
+ mFrameTimeline->setSfPresent(27 + frameTimeFactor, presentFence);
+ presentFence->signalForTest(32 + frameTimeFactor);
+ frameTimeFactor += 30;
+ }
+ auto displayFrame0 = getDisplayFrame(0);
+
+ // The 0th Display Frame should have actuals 22, 27, 32
+ EXPECT_EQ(compareTimelineItems(displayFrame0->getActuals(), TimelineItem(22, 27, 32)), true);
+
+ // Add one more display frame
+ auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken = mTokenManager->generateTokenForPredictions(
+ {10 + frameTimeFactor, 20 + frameTimeFactor, 30 + frameTimeFactor});
+ int64_t sfToken = mTokenManager->generateTokenForPredictions(
+ {22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor});
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+ mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, Fps::fromPeriodNsecs(11));
+ surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame);
+ mFrameTimeline->setSfPresent(27 + frameTimeFactor, presentFence);
+ presentFence->signalForTest(32 + frameTimeFactor);
+ displayFrame0 = getDisplayFrame(0);
+
+ // The window should have slided by 1 now and the previous 0th display frame
+ // should have been removed from the deque
+ EXPECT_EQ(compareTimelineItems(displayFrame0->getActuals(), TimelineItem(52, 57, 62)), true);
+}
+
+TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceAfterQueue) {
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
+ "acquireFenceAfterQueue",
+ "acquireFenceAfterQueue", /*isBuffer*/ true);
+ surfaceFrame->setActualQueueTime(123);
+ surfaceFrame->setAcquireFenceTime(456);
+ EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
+}
+
+TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceBeforeQueue) {
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
+ "acquireFenceAfterQueue",
+ "acquireFenceAfterQueue", /*isBuffer*/ true);
+ surfaceFrame->setActualQueueTime(456);
+ surfaceFrame->setAcquireFenceTime(123);
+ EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
+}
+
+TEST_F(FrameTimelineTest, setMaxDisplayFramesSetsSizeProperly) {
+ auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ presentFence->signalForTest(2);
+
+ // Size shouldn't exceed maxDisplayFrames - 64
+ for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
+ int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
+ mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
+ surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame);
+ mFrameTimeline->setSfPresent(27, presentFence);
+ }
+ EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
+
+ // Increase the size to 256
+ mFrameTimeline->setMaxDisplayFrames(256);
+ EXPECT_EQ(*maxDisplayFrames, 256u);
+
+ for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
+ int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
+ mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
+ surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame);
+ mFrameTimeline->setSfPresent(27, presentFence);
+ }
+ EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
+
+ // Shrink the size to 128
+ mFrameTimeline->setMaxDisplayFrames(128);
+ EXPECT_EQ(*maxDisplayFrames, 128u);
+
+ for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
+ int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
+ mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
+ surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame);
+ mFrameTimeline->setSfPresent(27, presentFence);
+ }
+ EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
+}
+
+TEST_F(FrameTimelineTest, presentFenceSignaled_invalidSignalTime) {
+ Fps refreshRate = Fps::fromPeriodNsecs(11);
+
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
+ surfaceFrame1->setAcquireFenceTime(20);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+
+ mFrameTimeline->setSfPresent(59, presentFence1);
+ presentFence1->signalForTest(-1);
+ addEmptyDisplayFrame();
+
+ auto displayFrame0 = getDisplayFrame(0);
+ EXPECT_EQ(displayFrame0->getActuals().presentTime, -1);
+ EXPECT_EQ(displayFrame0->getJankType(), JankType::Unknown);
+ EXPECT_EQ(surfaceFrame1->getActuals().presentTime, -1);
+ EXPECT_EQ(surfaceFrame1->getJankType(), JankType::Unknown);
+}
+
+// Tests related to TimeStats
+TEST_F(FrameTimelineTest, presentFenceSignaled_doesNotReportForInvalidTokens) {
+ Fps refreshRate = Fps::fromPeriodNsecs(11);
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)).Times(0);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken1 = -1;
+ int64_t sfToken1 = -1;
+
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
+ surfaceFrame1->setAcquireFenceTime(20);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ presentFence1->signalForTest(70);
+
+ mFrameTimeline->setSfPresent(59, presentFence1);
+}
+
+TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfCpu) {
+ Fps refreshRate = Fps::fromPeriodNsecs(11);
+ EXPECT_CALL(*mTimeStats,
+ incrementJankyFrames(
+ TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
+ sLayerNameOne,
+ JankType::SurfaceFlingerCpuDeadlineMissed, 2, 10,
+ 0}));
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
+ surfaceFrame1->setAcquireFenceTime(20);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ presentFence1->signalForTest(70);
+
+ mFrameTimeline->setSfPresent(62, presentFence1);
+}
+
+TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfGpu) {
+ Fps refreshRate = Fps::fromPeriodNsecs(11);
+ EXPECT_CALL(*mTimeStats,
+ incrementJankyFrames(
+ TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
+ sLayerNameOne,
+ JankType::SurfaceFlingerGpuDeadlineMissed, 4, 10,
+ 0}));
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto gpuFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
+ surfaceFrame1->setAcquireFenceTime(20);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ gpuFence1->signalForTest(64);
+ presentFence1->signalForTest(70);
+
+ mFrameTimeline->setSfPresent(59, presentFence1, gpuFence1);
+}
+
+TEST_F(FrameTimelineTest, presentFenceSignaled_reportsDisplayMiss) {
+ Fps refreshRate = Fps::fromPeriodNsecs(30);
+ EXPECT_CALL(*mTimeStats,
+ incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
+ sLayerNameOne, JankType::DisplayHAL,
+ -4, 0, 0}));
+
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ surfaceFrame1->setAcquireFenceTime(20);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ presentFence1->signalForTest(90);
+ mFrameTimeline->setSfPresent(56, presentFence1);
+ EXPECT_EQ(surfaceFrame1->getJankType(), JankType::DisplayHAL);
+}
+
+TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMiss) {
+ Fps refreshRate = Fps(11.0);
+ EXPECT_CALL(*mTimeStats,
+ incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
+ sLayerNameOne,
+ JankType::AppDeadlineMissed, -4, 0,
+ 25}));
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({82, 90, 90});
+
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+ surfaceFrame1->setAcquireFenceTime(45);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
+
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ presentFence1->signalForTest(90);
+ mFrameTimeline->setSfPresent(86, presentFence1);
+
+ EXPECT_EQ(surfaceFrame1->getJankType(), JankType::AppDeadlineMissed);
+}
+
+TEST_F(FrameTimelineTest, presentFenceSignaled_reportsSfScheduling) {
+ Fps refreshRate = Fps::fromPeriodNsecs(32);
+ EXPECT_CALL(*mTimeStats,
+ incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
+ sLayerNameOne,
+ JankType::SurfaceFlingerScheduling,
+ -4, 0, -10}));
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({40, 60, 92});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+ surfaceFrame1->setAcquireFenceTime(50);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
+
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ presentFence1->signalForTest(60);
+ mFrameTimeline->setSfPresent(56, presentFence1);
+
+ EXPECT_EQ(surfaceFrame1->getJankType(), JankType::SurfaceFlingerScheduling);
+}
+
+TEST_F(FrameTimelineTest, presentFenceSignaled_reportsSfPredictionError) {
+ Fps refreshRate = Fps::fromPeriodNsecs(16);
+ EXPECT_CALL(*mTimeStats,
+ incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
+ sLayerNameOne,
+ JankType::PredictionError, -4, 5,
+ 0}));
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({30, 40, 60});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+ surfaceFrame1->setAcquireFenceTime(40);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
+
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ presentFence1->signalForTest(65);
+ mFrameTimeline->setSfPresent(56, presentFence1);
+
+ EXPECT_EQ(surfaceFrame1->getJankType(), JankType::PredictionError);
+}
+
+TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppBufferStuffing) {
+ Fps refreshRate = Fps::fromPeriodNsecs(32);
+ EXPECT_CALL(*mTimeStats,
+ incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
+ sLayerNameOne,
+ JankType::BufferStuffing, -4, 0,
+ 0}));
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({30, 40, 58});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({82, 90, 90});
+
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+ surfaceFrame1->setAcquireFenceTime(40);
+ mFrameTimeline->setSfWakeUp(sfToken1, 82, refreshRate);
+
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented,
+ /*previousLatchTime*/ 56);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ presentFence1->signalForTest(90);
+ mFrameTimeline->setSfPresent(86, presentFence1);
+
+ EXPECT_EQ(surfaceFrame1->getJankType(), JankType::BufferStuffing);
+}
+
+TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMissWithRenderRate) {
+ Fps refreshRate = Fps::fromPeriodNsecs(11);
+ Fps renderRate = Fps::fromPeriodNsecs(30);
+ EXPECT_CALL(*mTimeStats,
+ incrementJankyFrames(
+ TimeStats::JankyFramesInfo{refreshRate, renderRate, sUidOne, sLayerNameOne,
+ JankType::AppDeadlineMissed, -4, 0, 25}));
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({82, 90, 90});
+
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+ surfaceFrame1->setAcquireFenceTime(45);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
+
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ surfaceFrame1->setRenderRate(renderRate);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ presentFence1->signalForTest(90);
+ mFrameTimeline->setSfPresent(86, presentFence1);
+
+ EXPECT_EQ(surfaceFrame1->getJankType(), JankType::AppDeadlineMissed);
+}
+
+TEST_F(FrameTimelineTest, presentFenceSignaled_displayFramePredictionExpiredPresentsSurfaceFrame) {
+ Fps refreshRate = Fps::fromPeriodNsecs(11);
+ Fps renderRate = Fps::fromPeriodNsecs(30);
+
+ EXPECT_CALL(*mTimeStats,
+ incrementJankyFrames(
+ TimeStats::JankyFramesInfo{refreshRate, renderRate, sUidOne, sLayerNameOne,
+ JankType::Unknown | JankType::AppDeadlineMissed,
+ 0, 0, 25}));
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({82, 90, 90});
+
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+ surfaceFrame1->setAcquireFenceTime(45);
+ // Trigger a prediction expiry
+ flushTokens(systemTime() + maxTokenRetentionTime);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
+
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ surfaceFrame1->setRenderRate(renderRate);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ presentFence1->signalForTest(90);
+ mFrameTimeline->setSfPresent(86, presentFence1);
+
+ auto displayFrame = getDisplayFrame(0);
+ EXPECT_EQ(displayFrame->getJankType(), JankType::Unknown);
+ EXPECT_EQ(displayFrame->getFrameStartMetadata(), FrameStartMetadata::UnknownStart);
+ EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::UnknownFinish);
+ EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::UnknownPresent);
+
+ EXPECT_EQ(surfaceFrame1->getActuals().presentTime, 90);
+ EXPECT_EQ(surfaceFrame1->getJankType(), JankType::Unknown | JankType::AppDeadlineMissed);
+}
+
+/*
+ * Tracing Tests
+ *
+ * Trace packets are flushed all the way only when the next packet is traced.
+ * For example: trace<Display/Surface>Frame() will create a TracePacket but not flush it. Only when
+ * another TracePacket is created, the previous one is guaranteed to be flushed. The following tests
+ * will have additional empty frames created for this reason.
+ */
+TEST_F(FrameTimelineTest, tracing_noPacketsSentWithoutTraceStart) {
+ auto tracingSession = getTracingSessionForTest();
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
+
+ // Set up the display frame
+ mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Dropped);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ mFrameTimeline->setSfPresent(25, presentFence1);
+ presentFence1->signalForTest(30);
+
+ addEmptyDisplayFrame();
+
+ auto packets = readFrameTimelinePacketsBlocking(tracingSession.get());
+ EXPECT_EQ(packets.size(), 0u);
+}
+
+TEST_F(FrameTimelineTest, tracing_sanityTest) {
+ auto tracingSession = getTracingSessionForTest();
+ // Layer specific increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(_));
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+
+ tracingSession->StartBlocking();
+ int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
+ int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
+
+ // Set up the display frame
+ mFrameTimeline->setSfWakeUp(token2, 20, Fps::fromPeriodNsecs(11));
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ mFrameTimeline->setSfPresent(25, presentFence1);
+ presentFence1->signalForTest(30);
+
+ addEmptyDisplayFrame();
+ flushTrace();
+ tracingSession->StopBlocking();
+
+ auto packets = readFrameTimelinePacketsBlocking(tracingSession.get());
+ // Display Frame 1 has 8 packets - 4 from DisplayFrame and 4 from SurfaceFrame.
+ EXPECT_EQ(packets.size(), 8u);
+}
+
+TEST_F(FrameTimelineTest, traceDisplayFrame_invalidTokenDoesNotEmitTracePacket) {
+ auto tracingSession = getTracingSessionForTest();
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+
+ tracingSession->StartBlocking();
+
+ // Set up the display frame
+ mFrameTimeline->setSfWakeUp(-1, 20, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfPresent(25, presentFence1);
+ presentFence1->signalForTest(30);
+
+ addEmptyDisplayFrame();
+ flushTrace();
+ tracingSession->StopBlocking();
+
+ auto packets = readFrameTimelinePacketsBlocking(tracingSession.get());
+ EXPECT_EQ(packets.size(), 0u);
+}
+
+TEST_F(FrameTimelineTest, traceSurfaceFrame_invalidTokenDoesNotEmitTracePacket) {
+ auto tracingSession = getTracingSessionForTest();
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+
+ tracingSession->StartBlocking();
+ int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
+
+ // Set up the display frame
+ mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Dropped);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ mFrameTimeline->setSfPresent(25, presentFence1);
+ presentFence1->signalForTest(30);
+
+ addEmptyDisplayFrame();
+ flushTrace();
+ tracingSession->StopBlocking();
+
+ auto packets = readFrameTimelinePacketsBlocking(tracingSession.get());
+ // Display Frame 1 has 4 packets (SurfaceFrame shouldn't be traced since it has an invalid
+ // token).
+ EXPECT_EQ(packets.size(), 4u);
+}
+
+ProtoExpectedDisplayFrameStart createProtoExpectedDisplayFrameStart(int64_t cookie, int64_t token,
+ pid_t pid) {
+ ProtoExpectedDisplayFrameStart proto;
+ proto.set_cookie(cookie);
+ proto.set_token(token);
+ proto.set_pid(pid);
+ return proto;
+}
+
+ProtoActualDisplayFrameStart createProtoActualDisplayFrameStart(
+ int64_t cookie, int64_t token, pid_t pid, ProtoPresentType presentType, bool onTimeFinish,
+ bool gpuComposition, ProtoJankType jankType, ProtoPredictionType predictionType) {
+ ProtoActualDisplayFrameStart proto;
+ proto.set_cookie(cookie);
+ proto.set_token(token);
+ proto.set_pid(pid);
+ proto.set_present_type(presentType);
+ proto.set_on_time_finish(onTimeFinish);
+ proto.set_gpu_composition(gpuComposition);
+ proto.set_jank_type(jankType);
+ proto.set_prediction_type(predictionType);
+ return proto;
+}
+
+ProtoExpectedSurfaceFrameStart createProtoExpectedSurfaceFrameStart(int64_t cookie, int64_t token,
+ int64_t displayFrameToken,
+ pid_t pid,
+ std::string layerName) {
+ ProtoExpectedSurfaceFrameStart proto;
+ proto.set_cookie(cookie);
+ proto.set_token(token);
+ proto.set_display_frame_token(displayFrameToken);
+ proto.set_pid(pid);
+ proto.set_layer_name(layerName);
+ return proto;
+}
+
+ProtoActualSurfaceFrameStart createProtoActualSurfaceFrameStart(
+ int64_t cookie, int64_t token, int64_t displayFrameToken, pid_t pid, std::string layerName,
+ ProtoPresentType presentType, bool onTimeFinish, bool gpuComposition,
+ ProtoJankType jankType, ProtoPredictionType predictionType) {
+ ProtoActualSurfaceFrameStart proto;
+ proto.set_cookie(cookie);
+ proto.set_token(token);
+ proto.set_display_frame_token(displayFrameToken);
+ proto.set_pid(pid);
+ proto.set_layer_name(layerName);
+ proto.set_present_type(presentType);
+ proto.set_on_time_finish(onTimeFinish);
+ proto.set_gpu_composition(gpuComposition);
+ proto.set_jank_type(jankType);
+ proto.set_prediction_type(predictionType);
+ return proto;
+}
+
+ProtoFrameEnd createProtoFrameEnd(int64_t cookie) {
+ ProtoFrameEnd proto;
+ proto.set_cookie(cookie);
+ return proto;
+}
+
+void validateTraceEvent(const ProtoExpectedDisplayFrameStart& received,
+ const ProtoExpectedDisplayFrameStart& source) {
+ ASSERT_TRUE(received.has_cookie());
+ EXPECT_EQ(received.cookie(), source.cookie());
+
+ ASSERT_TRUE(received.has_token());
+ EXPECT_EQ(received.token(), source.token());
+
+ ASSERT_TRUE(received.has_pid());
+ EXPECT_EQ(received.pid(), source.pid());
+}
+
+void validateTraceEvent(const ProtoActualDisplayFrameStart& received,
+ const ProtoActualDisplayFrameStart& source) {
+ ASSERT_TRUE(received.has_cookie());
+ EXPECT_EQ(received.cookie(), source.cookie());
+
+ ASSERT_TRUE(received.has_token());
+ EXPECT_EQ(received.token(), source.token());
+
+ ASSERT_TRUE(received.has_pid());
+ EXPECT_EQ(received.pid(), source.pid());
+
+ ASSERT_TRUE(received.has_present_type());
+ EXPECT_EQ(received.present_type(), source.present_type());
+ ASSERT_TRUE(received.has_on_time_finish());
+ EXPECT_EQ(received.on_time_finish(), source.on_time_finish());
+ ASSERT_TRUE(received.has_gpu_composition());
+ EXPECT_EQ(received.gpu_composition(), source.gpu_composition());
+ ASSERT_TRUE(received.has_jank_type());
+ EXPECT_EQ(received.jank_type(), source.jank_type());
+ ASSERT_TRUE(received.has_prediction_type());
+ EXPECT_EQ(received.prediction_type(), source.prediction_type());
+}
+
+void validateTraceEvent(const ProtoExpectedSurfaceFrameStart& received,
+ const ProtoExpectedSurfaceFrameStart& source) {
+ ASSERT_TRUE(received.has_cookie());
+ EXPECT_EQ(received.cookie(), source.cookie());
+
+ ASSERT_TRUE(received.has_token());
+ EXPECT_EQ(received.token(), source.token());
+
+ ASSERT_TRUE(received.has_display_frame_token());
+ EXPECT_EQ(received.display_frame_token(), source.display_frame_token());
+
+ ASSERT_TRUE(received.has_pid());
+ EXPECT_EQ(received.pid(), source.pid());
+
+ ASSERT_TRUE(received.has_layer_name());
+ EXPECT_EQ(received.layer_name(), source.layer_name());
+}
+
+void validateTraceEvent(const ProtoActualSurfaceFrameStart& received,
+ const ProtoActualSurfaceFrameStart& source) {
+ ASSERT_TRUE(received.has_cookie());
+ EXPECT_EQ(received.cookie(), source.cookie());
+
+ ASSERT_TRUE(received.has_token());
+ EXPECT_EQ(received.token(), source.token());
+
+ ASSERT_TRUE(received.has_display_frame_token());
+ EXPECT_EQ(received.display_frame_token(), source.display_frame_token());
+
+ ASSERT_TRUE(received.has_pid());
+ EXPECT_EQ(received.pid(), source.pid());
+
+ ASSERT_TRUE(received.has_layer_name());
+ EXPECT_EQ(received.layer_name(), source.layer_name());
+
+ ASSERT_TRUE(received.has_present_type());
+ EXPECT_EQ(received.present_type(), source.present_type());
+ ASSERT_TRUE(received.has_on_time_finish());
+ EXPECT_EQ(received.on_time_finish(), source.on_time_finish());
+ ASSERT_TRUE(received.has_gpu_composition());
+ EXPECT_EQ(received.gpu_composition(), source.gpu_composition());
+ ASSERT_TRUE(received.has_jank_type());
+ EXPECT_EQ(received.jank_type(), source.jank_type());
+ ASSERT_TRUE(received.has_prediction_type());
+ EXPECT_EQ(received.prediction_type(), source.prediction_type());
+}
+
+void validateTraceEvent(const ProtoFrameEnd& received, const ProtoFrameEnd& source) {
+ ASSERT_TRUE(received.has_cookie());
+ EXPECT_EQ(received.cookie(), source.cookie());
+}
+
+TEST_F(FrameTimelineTest, traceDisplayFrame_emitsValidTracePacket) {
+ auto tracingSession = getTracingSessionForTest();
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+
+ tracingSession->StartBlocking();
+ int64_t displayFrameToken1 = mTokenManager->generateTokenForPredictions({10, 30, 30});
+
+ // Set up the display frame
+ mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfPresent(26, presentFence1);
+ presentFence1->signalForTest(31);
+
+ int64_t traceCookie = snoopCurrentTraceCookie();
+ auto protoExpectedDisplayFrameStart =
+ createProtoExpectedDisplayFrameStart(traceCookie + 1, displayFrameToken1,
+ kSurfaceFlingerPid);
+ auto protoExpectedDisplayFrameEnd = createProtoFrameEnd(traceCookie + 1);
+ auto protoActualDisplayFrameStart =
+ createProtoActualDisplayFrameStart(traceCookie + 2, displayFrameToken1,
+ kSurfaceFlingerPid,
+ FrameTimelineEvent::PRESENT_ON_TIME, true, false,
+ FrameTimelineEvent::JANK_NONE,
+ FrameTimelineEvent::PREDICTION_VALID);
+ auto protoActualDisplayFrameEnd = createProtoFrameEnd(traceCookie + 2);
+
+ addEmptyDisplayFrame();
+ flushTrace();
+ tracingSession->StopBlocking();
+
+ auto packets = readFrameTimelinePacketsBlocking(tracingSession.get());
+ EXPECT_EQ(packets.size(), 4u);
+
+ // Packet - 0 : ExpectedDisplayFrameStart
+ const auto& packet0 = packets[0];
+ ASSERT_TRUE(packet0.has_timestamp());
+ EXPECT_EQ(packet0.timestamp(), 10u);
+ ASSERT_TRUE(packet0.has_frame_timeline_event());
+
+ const auto& event0 = packet0.frame_timeline_event();
+ ASSERT_TRUE(event0.has_expected_display_frame_start());
+ const auto& expectedDisplayFrameStart = event0.expected_display_frame_start();
+ validateTraceEvent(expectedDisplayFrameStart, protoExpectedDisplayFrameStart);
+
+ // Packet - 1 : FrameEnd (ExpectedDisplayFrame)
+ const auto& packet1 = packets[1];
+ ASSERT_TRUE(packet1.has_timestamp());
+ EXPECT_EQ(packet1.timestamp(), 30u);
+ ASSERT_TRUE(packet1.has_frame_timeline_event());
+
+ const auto& event1 = packet1.frame_timeline_event();
+ ASSERT_TRUE(event1.has_frame_end());
+ const auto& expectedDisplayFrameEnd = event1.frame_end();
+ validateTraceEvent(expectedDisplayFrameEnd, protoExpectedDisplayFrameEnd);
+
+ // Packet - 2 : ActualDisplayFrameStart
+ const auto& packet2 = packets[2];
+ ASSERT_TRUE(packet2.has_timestamp());
+ EXPECT_EQ(packet2.timestamp(), 20u);
+ ASSERT_TRUE(packet2.has_frame_timeline_event());
+
+ const auto& event2 = packet2.frame_timeline_event();
+ ASSERT_TRUE(event2.has_actual_display_frame_start());
+ const auto& actualDisplayFrameStart = event2.actual_display_frame_start();
+ validateTraceEvent(actualDisplayFrameStart, protoActualDisplayFrameStart);
+
+ // Packet - 3 : FrameEnd (ActualDisplayFrame)
+ const auto& packet3 = packets[3];
+ ASSERT_TRUE(packet3.has_timestamp());
+ EXPECT_EQ(packet3.timestamp(), 31u);
+ ASSERT_TRUE(packet3.has_frame_timeline_event());
+
+ const auto& event3 = packet3.frame_timeline_event();
+ ASSERT_TRUE(event3.has_frame_end());
+ const auto& actualDisplayFrameEnd = event3.frame_end();
+ validateTraceEvent(actualDisplayFrameEnd, protoActualDisplayFrameEnd);
+}
+
+TEST_F(FrameTimelineTest, traceDisplayFrame_predictionExpiredDoesNotTraceExpectedTimeline) {
+ auto tracingSession = getTracingSessionForTest();
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+
+ tracingSession->StartBlocking();
+ int64_t displayFrameToken1 = mTokenManager->generateTokenForPredictions({10, 25, 30});
+ // Flush the token so that it would expire
+ flushTokens(systemTime() + maxTokenRetentionTime);
+
+ // Set up the display frame
+ mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfPresent(26, presentFence1);
+ presentFence1->signalForTest(31);
+
+ int64_t traceCookie = snoopCurrentTraceCookie();
+
+ auto protoActualDisplayFrameStart =
+ createProtoActualDisplayFrameStart(traceCookie + 1, displayFrameToken1,
+ kSurfaceFlingerPid,
+ FrameTimelineEvent::PRESENT_UNSPECIFIED, false,
+ false, FrameTimelineEvent::JANK_UNKNOWN,
+ FrameTimelineEvent::PREDICTION_EXPIRED);
+ auto protoActualDisplayFrameEnd = createProtoFrameEnd(traceCookie + 1);
+
+ addEmptyDisplayFrame();
+ flushTrace();
+ tracingSession->StopBlocking();
+
+ auto packets = readFrameTimelinePacketsBlocking(tracingSession.get());
+ // Only actual timeline packets should be in the trace
+ EXPECT_EQ(packets.size(), 2u);
+
+ // Packet - 0 : ActualDisplayFrameStart
+ const auto& packet0 = packets[0];
+ ASSERT_TRUE(packet0.has_timestamp());
+ EXPECT_EQ(packet0.timestamp(), 20u);
+ ASSERT_TRUE(packet0.has_frame_timeline_event());
+
+ const auto& event0 = packet0.frame_timeline_event();
+ ASSERT_TRUE(event0.has_actual_display_frame_start());
+ const auto& actualDisplayFrameStart = event0.actual_display_frame_start();
+ validateTraceEvent(actualDisplayFrameStart, protoActualDisplayFrameStart);
+
+ // Packet - 1 : FrameEnd (ActualDisplayFrame)
+ const auto& packet1 = packets[1];
+ ASSERT_TRUE(packet1.has_timestamp());
+ EXPECT_EQ(packet1.timestamp(), 31u);
+ ASSERT_TRUE(packet1.has_frame_timeline_event());
+
+ const auto& event1 = packet1.frame_timeline_event();
+ ASSERT_TRUE(event1.has_frame_end());
+ const auto& actualDisplayFrameEnd = event1.frame_end();
+ validateTraceEvent(actualDisplayFrameEnd, protoActualDisplayFrameEnd);
+}
+
+TEST_F(FrameTimelineTest, traceSurfaceFrame_emitsValidTracePacket) {
+ auto tracingSession = getTracingSessionForTest();
+ // Layer specific increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(_));
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+
+ tracingSession->StartBlocking();
+ int64_t surfaceFrameToken = mTokenManager->generateTokenForPredictions({10, 25, 40});
+ int64_t displayFrameToken1 = mTokenManager->generateTokenForPredictions({30, 35, 40});
+
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+ surfaceFrame1->setActualQueueTime(10);
+ surfaceFrame1->setDropTime(15);
+
+ surfaceFrame2->setActualQueueTime(15);
+ surfaceFrame2->setAcquireFenceTime(20);
+
+ // First 2 cookies will be used by the DisplayFrame
+ int64_t traceCookie = snoopCurrentTraceCookie() + 2;
+
+ auto protoDroppedSurfaceFrameExpectedStart =
+ createProtoExpectedSurfaceFrameStart(traceCookie + 1, surfaceFrameToken,
+ displayFrameToken1, sPidOne, sLayerNameOne);
+ auto protoDroppedSurfaceFrameExpectedEnd = createProtoFrameEnd(traceCookie + 1);
+ auto protoDroppedSurfaceFrameActualStart =
+ createProtoActualSurfaceFrameStart(traceCookie + 2, surfaceFrameToken,
+ displayFrameToken1, sPidOne, sLayerNameOne,
+ FrameTimelineEvent::PRESENT_DROPPED, false, false,
+ FrameTimelineEvent::JANK_NONE,
+ FrameTimelineEvent::PREDICTION_VALID);
+ auto protoDroppedSurfaceFrameActualEnd = createProtoFrameEnd(traceCookie + 2);
+
+ auto protoPresentedSurfaceFrameExpectedStart =
+ createProtoExpectedSurfaceFrameStart(traceCookie + 3, surfaceFrameToken,
+ displayFrameToken1, sPidOne, sLayerNameOne);
+ auto protoPresentedSurfaceFrameExpectedEnd = createProtoFrameEnd(traceCookie + 3);
+ auto protoPresentedSurfaceFrameActualStart =
+ createProtoActualSurfaceFrameStart(traceCookie + 4, surfaceFrameToken,
+ displayFrameToken1, sPidOne, sLayerNameOne,
+ FrameTimelineEvent::PRESENT_ON_TIME, true, false,
+ FrameTimelineEvent::JANK_NONE,
+ FrameTimelineEvent::PREDICTION_VALID);
+ auto protoPresentedSurfaceFrameActualEnd = createProtoFrameEnd(traceCookie + 4);
+
+ // Set up the display frame
+ mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, Fps::fromPeriodNsecs(11));
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Dropped);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ mFrameTimeline->setSfPresent(26, presentFence1);
+ presentFence1->signalForTest(40);
+
+ addEmptyDisplayFrame();
+ flushTrace();
+ tracingSession->StopBlocking();
+
+ auto packets = readFrameTimelinePacketsBlocking(tracingSession.get());
+ // 4 DisplayFrame + 4 DroppedSurfaceFrame + 4 PresentedSurfaceFrame
+ EXPECT_EQ(packets.size(), 12u);
+
+ // Packet - 4 : ExpectedSurfaceFrameStart1
+ const auto& packet4 = packets[4];
+ ASSERT_TRUE(packet4.has_timestamp());
+ EXPECT_EQ(packet4.timestamp(), 10u);
+ ASSERT_TRUE(packet4.has_frame_timeline_event());
+
+ const auto& event4 = packet4.frame_timeline_event();
+ ASSERT_TRUE(event4.has_expected_surface_frame_start());
+ const auto& expectedSurfaceFrameStart1 = event4.expected_surface_frame_start();
+ validateTraceEvent(expectedSurfaceFrameStart1, protoDroppedSurfaceFrameExpectedStart);
+
+ // Packet - 5 : FrameEnd (ExpectedSurfaceFrame1)
+ const auto& packet5 = packets[5];
+ ASSERT_TRUE(packet5.has_timestamp());
+ EXPECT_EQ(packet5.timestamp(), 25u);
+ ASSERT_TRUE(packet5.has_frame_timeline_event());
+
+ const auto& event5 = packet5.frame_timeline_event();
+ ASSERT_TRUE(event5.has_frame_end());
+ const auto& expectedSurfaceFrameEnd1 = event5.frame_end();
+ validateTraceEvent(expectedSurfaceFrameEnd1, protoDroppedSurfaceFrameExpectedEnd);
+
+ // Packet - 6 : ActualSurfaceFrameStart1
+ const auto& packet6 = packets[6];
+ ASSERT_TRUE(packet6.has_timestamp());
+ EXPECT_EQ(packet6.timestamp(), 10u);
+ ASSERT_TRUE(packet6.has_frame_timeline_event());
+
+ const auto& event6 = packet6.frame_timeline_event();
+ ASSERT_TRUE(event6.has_actual_surface_frame_start());
+ const auto& actualSurfaceFrameStart1 = event6.actual_surface_frame_start();
+ validateTraceEvent(actualSurfaceFrameStart1, protoDroppedSurfaceFrameActualStart);
+
+ // Packet - 7 : FrameEnd (ActualSurfaceFrame1)
+ const auto& packet7 = packets[7];
+ ASSERT_TRUE(packet7.has_timestamp());
+ EXPECT_EQ(packet7.timestamp(), 15u);
+ ASSERT_TRUE(packet7.has_frame_timeline_event());
+
+ const auto& event7 = packet7.frame_timeline_event();
+ ASSERT_TRUE(event7.has_frame_end());
+ const auto& actualSurfaceFrameEnd1 = event7.frame_end();
+ validateTraceEvent(actualSurfaceFrameEnd1, protoDroppedSurfaceFrameActualEnd);
+
+ // Packet - 8 : ExpectedSurfaceFrameStart2
+ const auto& packet8 = packets[8];
+ ASSERT_TRUE(packet8.has_timestamp());
+ EXPECT_EQ(packet8.timestamp(), 10u);
+ ASSERT_TRUE(packet8.has_frame_timeline_event());
+
+ const auto& event8 = packet8.frame_timeline_event();
+ ASSERT_TRUE(event8.has_expected_surface_frame_start());
+ const auto& expectedSurfaceFrameStart2 = event8.expected_surface_frame_start();
+ validateTraceEvent(expectedSurfaceFrameStart2, protoPresentedSurfaceFrameExpectedStart);
+
+ // Packet - 9 : FrameEnd (ExpectedSurfaceFrame2)
+ const auto& packet9 = packets[9];
+ ASSERT_TRUE(packet9.has_timestamp());
+ EXPECT_EQ(packet9.timestamp(), 25u);
+ ASSERT_TRUE(packet9.has_frame_timeline_event());
+
+ const auto& event9 = packet9.frame_timeline_event();
+ ASSERT_TRUE(event9.has_frame_end());
+ const auto& expectedSurfaceFrameEnd2 = event9.frame_end();
+ validateTraceEvent(expectedSurfaceFrameEnd2, protoPresentedSurfaceFrameExpectedEnd);
+
+ // Packet - 10 : ActualSurfaceFrameStart2
+ const auto& packet10 = packets[10];
+ ASSERT_TRUE(packet10.has_timestamp());
+ EXPECT_EQ(packet10.timestamp(), 10u);
+ ASSERT_TRUE(packet10.has_frame_timeline_event());
+
+ const auto& event10 = packet10.frame_timeline_event();
+ ASSERT_TRUE(event10.has_actual_surface_frame_start());
+ const auto& actualSurfaceFrameStart2 = event10.actual_surface_frame_start();
+ validateTraceEvent(actualSurfaceFrameStart2, protoPresentedSurfaceFrameActualStart);
+
+ // Packet - 11 : FrameEnd (ActualSurfaceFrame2)
+ const auto& packet11 = packets[11];
+ ASSERT_TRUE(packet11.has_timestamp());
+ EXPECT_EQ(packet11.timestamp(), 20u);
+ ASSERT_TRUE(packet11.has_frame_timeline_event());
+
+ const auto& event11 = packet11.frame_timeline_event();
+ ASSERT_TRUE(event11.has_frame_end());
+ const auto& actualSurfaceFrameEnd2 = event11.frame_end();
+ validateTraceEvent(actualSurfaceFrameEnd2, protoPresentedSurfaceFrameActualEnd);
+}
+
+TEST_F(FrameTimelineTest, traceSurfaceFrame_predictionExpiredDoesNotTraceExpectedTimeline) {
+ auto tracingSession = getTracingSessionForTest();
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+
+ tracingSession->StartBlocking();
+ constexpr nsecs_t appStartTime = std::chrono::nanoseconds(10ms).count();
+ constexpr nsecs_t appEndTime = std::chrono::nanoseconds(20ms).count();
+ constexpr nsecs_t appPresentTime = std::chrono::nanoseconds(30ms).count();
+ int64_t surfaceFrameToken =
+ mTokenManager->generateTokenForPredictions({appStartTime, appEndTime, appPresentTime});
+
+ // Flush the token so that it would expire
+ flushTokens(systemTime() + maxTokenRetentionTime);
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, /*inputEventId*/ 0},
+ sPidOne, sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+ surfaceFrame1->setActualQueueTime(appEndTime);
+ surfaceFrame1->setAcquireFenceTime(appEndTime);
+
+ constexpr nsecs_t sfStartTime = std::chrono::nanoseconds(20ms).count();
+ constexpr nsecs_t sfEndTime = std::chrono::nanoseconds(30ms).count();
+ constexpr nsecs_t sfPresentTime = std::chrono::nanoseconds(30ms).count();
+ int64_t displayFrameToken =
+ mTokenManager->generateTokenForPredictions({sfStartTime, sfEndTime, sfPresentTime});
+
+ // First 2 cookies will be used by the DisplayFrame
+ int64_t traceCookie = snoopCurrentTraceCookie() + 2;
+
+ auto protoActualSurfaceFrameStart =
+ createProtoActualSurfaceFrameStart(traceCookie + 1, surfaceFrameToken,
+ displayFrameToken, sPidOne, sLayerNameOne,
+ FrameTimelineEvent::PRESENT_UNSPECIFIED, false,
+ false, FrameTimelineEvent::JANK_UNKNOWN,
+ FrameTimelineEvent::PREDICTION_EXPIRED);
+ auto protoActualSurfaceFrameEnd = createProtoFrameEnd(traceCookie + 1);
+
+ // Set up the display frame
+ mFrameTimeline->setSfWakeUp(displayFrameToken, sfStartTime, Fps::fromPeriodNsecs(11));
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ mFrameTimeline->setSfPresent(sfEndTime, presentFence1);
+ presentFence1->signalForTest(sfPresentTime);
+
+ addEmptyDisplayFrame();
+ flushTrace();
+ tracingSession->StopBlocking();
+
+ auto packets = readFrameTimelinePacketsBlocking(tracingSession.get());
+ // Display Frame 4 packets + SurfaceFrame 2 packets
+ ASSERT_EQ(packets.size(), 6u);
+
+ // Packet - 4 : ActualSurfaceFrameStart
+ const auto& packet4 = packets[4];
+ ASSERT_TRUE(packet4.has_timestamp());
+ EXPECT_EQ(packet4.timestamp(),
+ static_cast<uint64_t>(appEndTime - SurfaceFrame::kPredictionExpiredStartTimeDelta));
+ ASSERT_TRUE(packet4.has_frame_timeline_event());
+
+ const auto& event4 = packet4.frame_timeline_event();
+ ASSERT_TRUE(event4.has_actual_surface_frame_start());
+ const auto& actualSurfaceFrameStart = event4.actual_surface_frame_start();
+ validateTraceEvent(actualSurfaceFrameStart, protoActualSurfaceFrameStart);
+
+ // Packet - 5 : FrameEnd (ActualSurfaceFrame)
+ const auto& packet5 = packets[5];
+ ASSERT_TRUE(packet5.has_timestamp());
+ EXPECT_EQ(packet5.timestamp(), static_cast<uint64_t>(appEndTime));
+ ASSERT_TRUE(packet5.has_frame_timeline_event());
+
+ const auto& event5 = packet5.frame_timeline_event();
+ ASSERT_TRUE(event5.has_frame_end());
+ const auto& actualSurfaceFrameEnd = event5.frame_end();
+ validateTraceEvent(actualSurfaceFrameEnd, protoActualSurfaceFrameEnd);
+}
+
+TEST_F(FrameTimelineTest, traceSurfaceFrame_predictionExpiredDroppedFramesTracedProperly) {
+ auto tracingSession = getTracingSessionForTest();
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+
+ tracingSession->StartBlocking();
+ constexpr nsecs_t appStartTime = std::chrono::nanoseconds(10ms).count();
+ constexpr nsecs_t appEndTime = std::chrono::nanoseconds(20ms).count();
+ constexpr nsecs_t appPresentTime = std::chrono::nanoseconds(30ms).count();
+ int64_t surfaceFrameToken =
+ mTokenManager->generateTokenForPredictions({appStartTime, appEndTime, appPresentTime});
+
+ // Flush the token so that it would expire
+ flushTokens(systemTime() + maxTokenRetentionTime);
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, /*inputEventId*/ 0},
+ sPidOne, sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+
+ constexpr nsecs_t sfStartTime = std::chrono::nanoseconds(22ms).count();
+ constexpr nsecs_t sfEndTime = std::chrono::nanoseconds(30ms).count();
+ constexpr nsecs_t sfPresentTime = std::chrono::nanoseconds(30ms).count();
+ int64_t displayFrameToken =
+ mTokenManager->generateTokenForPredictions({sfStartTime, sfEndTime, sfPresentTime});
+
+ // First 2 cookies will be used by the DisplayFrame
+ int64_t traceCookie = snoopCurrentTraceCookie() + 2;
+
+ auto protoActualSurfaceFrameStart =
+ createProtoActualSurfaceFrameStart(traceCookie + 1, surfaceFrameToken,
+ displayFrameToken, sPidOne, sLayerNameOne,
+ FrameTimelineEvent::PRESENT_DROPPED, false, false,
+ FrameTimelineEvent::JANK_NONE,
+ FrameTimelineEvent::PREDICTION_EXPIRED);
+ auto protoActualSurfaceFrameEnd = createProtoFrameEnd(traceCookie + 1);
+
+ // Set up the display frame
+ mFrameTimeline->setSfWakeUp(displayFrameToken, sfStartTime, Fps::fromPeriodNsecs(11));
+ surfaceFrame1->setDropTime(sfStartTime);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Dropped);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ mFrameTimeline->setSfPresent(sfEndTime, presentFence1);
+ presentFence1->signalForTest(sfPresentTime);
+
+ addEmptyDisplayFrame();
+ flushTrace();
+ tracingSession->StopBlocking();
+
+ auto packets = readFrameTimelinePacketsBlocking(tracingSession.get());
+ // Display Frame 4 packets + SurfaceFrame 2 packets
+ ASSERT_EQ(packets.size(), 6u);
+
+ // Packet - 4 : ActualSurfaceFrameStart
+ const auto& packet4 = packets[4];
+ ASSERT_TRUE(packet4.has_timestamp());
+ EXPECT_EQ(packet4.timestamp(),
+ static_cast<uint64_t>(sfStartTime - SurfaceFrame::kPredictionExpiredStartTimeDelta));
+ ASSERT_TRUE(packet4.has_frame_timeline_event());
+
+ const auto& event4 = packet4.frame_timeline_event();
+ ASSERT_TRUE(event4.has_actual_surface_frame_start());
+ const auto& actualSurfaceFrameStart = event4.actual_surface_frame_start();
+ validateTraceEvent(actualSurfaceFrameStart, protoActualSurfaceFrameStart);
+
+ // Packet - 5 : FrameEnd (ActualSurfaceFrame)
+ const auto& packet5 = packets[5];
+ ASSERT_TRUE(packet5.has_timestamp());
+ EXPECT_EQ(packet5.timestamp(), static_cast<uint64_t>(sfStartTime));
+ ASSERT_TRUE(packet5.has_frame_timeline_event());
+
+ const auto& event5 = packet5.frame_timeline_event();
+ ASSERT_TRUE(event5.has_frame_end());
+ const auto& actualSurfaceFrameEnd = event5.frame_end();
+ validateTraceEvent(actualSurfaceFrameEnd, protoActualSurfaceFrameEnd);
+}
+
+// Tests for Jank classification
+TEST_F(FrameTimelineTest, jankClassification_presentOnTimeDoesNotClassify) {
+ // Layer specific increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(_));
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken = mTokenManager->generateTokenForPredictions({10, 20, 30});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 30, 30});
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
+ surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame);
+ mFrameTimeline->setSfPresent(26, presentFence1);
+ auto displayFrame = getDisplayFrame(0);
+ auto& presentedSurfaceFrame = getSurfaceFrame(0, 0);
+ presentFence1->signalForTest(29);
+
+ // Fences haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+ EXPECT_EQ(presentedSurfaceFrame.getActuals().presentTime, 0);
+
+ addEmptyDisplayFrame();
+ displayFrame = getDisplayFrame(0);
+
+ // Fences have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 29);
+ EXPECT_EQ(presentedSurfaceFrame.getActuals().presentTime, 29);
+ EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+ EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame->getJankType(), JankType::None);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishEarlyPresent) {
+ Fps vsyncRate = Fps::fromPeriodNsecs(11);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 30, 40});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 60, 70});
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, vsyncRate);
+ mFrameTimeline->setSfPresent(26, presentFence1);
+ auto displayFrame = getDisplayFrame(0);
+ presentFence1->signalForTest(30);
+
+ // Fences for the first frame haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+
+ // Trigger a flush by finalizing the next DisplayFrame
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ mFrameTimeline->setSfWakeUp(sfToken2, 52, vsyncRate);
+ mFrameTimeline->setSfPresent(56, presentFence2);
+ displayFrame = getDisplayFrame(0);
+
+ // Fences for the first frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 30);
+ EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+ EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerScheduling);
+
+ // Fences for the second frame haven't been flushed yet, so it should be 0
+ auto displayFrame2 = getDisplayFrame(1);
+ presentFence2->signalForTest(65);
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+ addEmptyDisplayFrame();
+ displayFrame2 = getDisplayFrame(1);
+
+ // Fences for the second frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 65);
+ EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+ EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame2->getJankType(), JankType::PredictionError);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishLatePresent) {
+ Fps vsyncRate = Fps::fromPeriodNsecs(11);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 30, 40});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 60, 70});
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, vsyncRate);
+ mFrameTimeline->setSfPresent(26, presentFence1);
+ auto displayFrame = getDisplayFrame(0);
+ presentFence1->signalForTest(50);
+
+ // Fences for the first frame haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+
+ // Trigger a flush by finalizing the next DisplayFrame
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ mFrameTimeline->setSfWakeUp(sfToken2, 52, vsyncRate);
+ mFrameTimeline->setSfPresent(56, presentFence2);
+ displayFrame = getDisplayFrame(0);
+
+ // Fences for the first frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 50);
+ EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame->getJankType(), JankType::DisplayHAL);
+
+ // Fences for the second frame haven't been flushed yet, so it should be 0
+ auto displayFrame2 = getDisplayFrame(1);
+ presentFence2->signalForTest(75);
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+
+ addEmptyDisplayFrame();
+ displayFrame2 = getDisplayFrame(1);
+
+ // Fences for the second frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 75);
+ EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame2->getJankType(), JankType::PredictionError);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishEarlyPresent) {
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({12, 18, 40});
+ mFrameTimeline->setSfWakeUp(sfToken1, 12, Fps::fromPeriodNsecs(11));
+
+ mFrameTimeline->setSfPresent(22, presentFence1);
+ auto displayFrame = getDisplayFrame(0);
+ presentFence1->signalForTest(28);
+
+ // Fences haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+
+ addEmptyDisplayFrame();
+ displayFrame = getDisplayFrame(0);
+
+ // Fences have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 28);
+ EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+ EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerScheduling);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishLatePresent) {
+ /*
+ * Case 1 - cpu time > vsync period but combined time > deadline > deadline -> cpudeadlinemissed
+ * Case 2 - cpu time < vsync period but combined time > deadline -> gpudeadlinemissed
+ * Case 3 - previous frame ran longer -> sf_stuffing
+ * Case 4 - Long cpu under SF stuffing -> cpudeadlinemissed
+ */
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto presentFence3 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto presentFence4 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto gpuFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto gpuFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto gpuFence3 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto gpuFence4 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+ int64_t sfToken3 = mTokenManager->generateTokenForPredictions({82, 90, 90});
+ int64_t sfToken4 = mTokenManager->generateTokenForPredictions({112, 120, 120});
+
+ // case 1 - cpu time = 33 - 12 = 21, vsync period = 11
+ mFrameTimeline->setSfWakeUp(sfToken1, 12, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfPresent(33, presentFence1, gpuFence1);
+ auto displayFrame0 = getDisplayFrame(0);
+ gpuFence1->signalForTest(36);
+ presentFence1->signalForTest(52);
+
+ // Fences haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame0->getActuals().presentTime, 0);
+
+ // case 2 - cpu time = 56 - 52 = 4, vsync period = 30
+ mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(30));
+ mFrameTimeline->setSfPresent(56, presentFence2, gpuFence2);
+ auto displayFrame1 = getDisplayFrame(1);
+ gpuFence2->signalForTest(76);
+ presentFence2->signalForTest(90);
+
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
+ // Fences have flushed for first displayFrame, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame0->getActuals().presentTime, 52);
+ EXPECT_EQ(displayFrame0->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(displayFrame0->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(displayFrame0->getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
+
+ // case 3 - cpu time = 86 - 82 = 4, vsync period = 30
+ mFrameTimeline->setSfWakeUp(sfToken3, 106, Fps::fromPeriodNsecs(30));
+ mFrameTimeline->setSfPresent(112, presentFence3, gpuFence3);
+ auto displayFrame2 = getDisplayFrame(2);
+ gpuFence3->signalForTest(116);
+ presentFence3->signalForTest(120);
+
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+ // Fences have flushed for second displayFrame, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 90);
+ EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(displayFrame1->getJankType(), JankType::SurfaceFlingerGpuDeadlineMissed);
+
+ // case 4 - cpu time = 86 - 82 = 4, vsync period = 30
+ mFrameTimeline->setSfWakeUp(sfToken4, 120, Fps::fromPeriodNsecs(30));
+ mFrameTimeline->setSfPresent(140, presentFence4, gpuFence4);
+ auto displayFrame3 = getDisplayFrame(3);
+ gpuFence4->signalForTest(156);
+ presentFence4->signalForTest(180);
+
+ EXPECT_EQ(displayFrame3->getActuals().presentTime, 0);
+ // Fences have flushed for third displayFrame, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 120);
+ EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(displayFrame2->getJankType(), JankType::SurfaceFlingerStuffing);
+
+ addEmptyDisplayFrame();
+
+ // Fences have flushed for third displayFrame, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame3->getActuals().presentTime, 180);
+ EXPECT_EQ(displayFrame3->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(displayFrame3->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(displayFrame3->getJankType(), JankType::SurfaceFlingerGpuDeadlineMissed);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishEarlyPresent) {
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(_));
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 30, 40});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 60, 70});
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 16, 40});
+ int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 70});
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+ surfaceFrame1->setAcquireFenceTime(16);
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ mFrameTimeline->setSfPresent(27, presentFence1);
+ auto displayFrame1 = getDisplayFrame(0);
+ auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+ presentFence1->signalForTest(30);
+
+ // Fences for the first frame haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
+ auto actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.presentTime, 0);
+
+ // Trigger a flush by finalizing the next DisplayFrame
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+ surfaceFrame2->setAcquireFenceTime(36);
+ mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ mFrameTimeline->setSfPresent(57, presentFence2);
+ auto displayFrame2 = getDisplayFrame(1);
+ auto& presentedSurfaceFrame2 = getSurfaceFrame(1, 0);
+
+ // Fences for the first frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 30);
+ EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+ EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame1->getJankType(), JankType::SurfaceFlingerScheduling);
+
+ actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.presentTime, 30);
+ EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+ EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::SurfaceFlingerScheduling);
+
+ // Fences for the second frame haven't been flushed yet, so it should be 0
+ presentFence2->signalForTest(65);
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+ auto actuals2 = presentedSurfaceFrame2.getActuals();
+ EXPECT_EQ(actuals2.presentTime, 0);
+
+ ::testing::Mock::VerifyAndClearExpectations(mTimeStats.get());
+
+ EXPECT_CALL(*mTimeStats,
+ incrementJankyFrames(
+ TimeStats::JankyFramesInfo{Fps::fromPeriodNsecs(11), std::nullopt, sUidOne,
+ sLayerNameOne, JankType::PredictionError, -3, 5,
+ 0}));
+
+ addEmptyDisplayFrame();
+
+ // Fences for the second frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 65);
+ EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+ EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame2->getJankType(), JankType::PredictionError);
+
+ actuals2 = presentedSurfaceFrame2.getActuals();
+ EXPECT_EQ(actuals2.presentTime, 65);
+ EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+ EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(presentedSurfaceFrame2.getJankType(), JankType::PredictionError);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishLatePresent) {
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(_));
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 30, 40});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 60, 70});
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 16, 40});
+ int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 70});
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+ surfaceFrame1->setAcquireFenceTime(16);
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ mFrameTimeline->setSfPresent(26, presentFence1);
+ auto displayFrame1 = getDisplayFrame(0);
+ auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+ presentFence1->signalForTest(50);
+
+ // Fences for the first frame haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
+ auto actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.presentTime, 0);
+
+ // Trigger a flush by finalizing the next DisplayFrame
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+ surfaceFrame2->setAcquireFenceTime(36);
+ mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ mFrameTimeline->setSfPresent(57, presentFence2);
+ auto displayFrame2 = getDisplayFrame(1);
+ auto& presentedSurfaceFrame2 = getSurfaceFrame(1, 0);
+
+ // Fences for the first frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 50);
+ EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame1->getJankType(), JankType::DisplayHAL);
+
+ actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.presentTime, 50);
+ EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::DisplayHAL);
+
+ // Fences for the second frame haven't been flushed yet, so it should be 0
+ presentFence2->signalForTest(86);
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+ auto actuals2 = presentedSurfaceFrame2.getActuals();
+ EXPECT_EQ(actuals2.presentTime, 0);
+
+ ::testing::Mock::VerifyAndClearExpectations(mTimeStats.get());
+
+ EXPECT_CALL(*mTimeStats,
+ incrementJankyFrames(
+ TimeStats::JankyFramesInfo{Fps::fromPeriodNsecs(11), std::nullopt, sUidOne,
+ sLayerNameOne, JankType::PredictionError, -3, 5,
+ 0}));
+
+ addEmptyDisplayFrame();
+
+ // Fences for the second frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 86);
+ EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame2->getJankType(), JankType::PredictionError);
+
+ actuals2 = presentedSurfaceFrame2.getActuals();
+ EXPECT_EQ(actuals2.presentTime, 86);
+ EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(presentedSurfaceFrame2.getJankType(), JankType::PredictionError);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishEarlyPresent) {
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(_));
+
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({42, 50, 50});
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 26, 60});
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+ surfaceFrame1->setAcquireFenceTime(40);
+ mFrameTimeline->setSfWakeUp(sfToken1, 42, Fps::fromPeriodNsecs(11));
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ mFrameTimeline->setSfPresent(46, presentFence1);
+ auto displayFrame1 = getDisplayFrame(0);
+ auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+ presentFence1->signalForTest(50);
+
+ // Fences for the first frame haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
+ auto actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.presentTime, 0);
+
+ addEmptyDisplayFrame();
+
+ // Fences for the first frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 50);
+ EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+ EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame1->getJankType(), JankType::None);
+
+ actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.presentTime, 50);
+ EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+ EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::Unknown);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishLatePresent) {
+ // First frame - DisplayFrame is not janky. This should classify the SurfaceFrame as only
+ // AppDeadlineMissed. Second frame - DisplayFrame is janky. This should propagate DisplayFrame's
+ // jank to the SurfaceFrame along with AppDeadlineMissed.
+
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)).Times(2);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({32, 40, 40});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({42, 50, 50});
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 16, 30});
+ int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 50});
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+ surfaceFrame1->setAcquireFenceTime(26);
+ mFrameTimeline->setSfWakeUp(sfToken1, 32, Fps::fromPeriodNsecs(11));
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ mFrameTimeline->setSfPresent(36, presentFence1);
+ auto displayFrame1 = getDisplayFrame(0);
+ auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+ presentFence1->signalForTest(40);
+
+ // Fences for the first frame haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
+ auto actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.presentTime, 0);
+
+ // Trigger a flush by finalizing the next DisplayFrame
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+ surfaceFrame2->setAcquireFenceTime(40);
+ mFrameTimeline->setSfWakeUp(sfToken2, 43, Fps::fromPeriodNsecs(11));
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ mFrameTimeline->setSfPresent(56, presentFence2);
+ auto displayFrame2 = getDisplayFrame(1);
+ auto& presentedSurfaceFrame2 = getSurfaceFrame(1, 0);
+
+ // Fences for the first frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 40);
+ EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+ EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame1->getJankType(), JankType::None);
+
+ actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.presentTime, 40);
+ EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::AppDeadlineMissed);
+
+ // Fences for the second frame haven't been flushed yet, so it should be 0
+ presentFence2->signalForTest(60);
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+ auto actuals2 = presentedSurfaceFrame2.getActuals();
+ EXPECT_EQ(actuals2.presentTime, 0);
+
+ addEmptyDisplayFrame();
+
+ // Fences for the second frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 60);
+ EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(displayFrame2->getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
+
+ actuals2 = presentedSurfaceFrame2.getActuals();
+ EXPECT_EQ(actuals2.presentTime, 60);
+ EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(presentedSurfaceFrame2.getJankType(),
+ JankType::SurfaceFlingerCpuDeadlineMissed | JankType::AppDeadlineMissed);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_multiJankBufferStuffingAndAppDeadlineMissed) {
+ // Layer specific increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)).Times(2);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
+ int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
+
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({112, 120, 120});
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+ surfaceFrame1->setAcquireFenceTime(50);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, Fps::fromPeriodNsecs(30));
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ mFrameTimeline->setSfPresent(56, presentFence1);
+ auto displayFrame1 = getDisplayFrame(0);
+ auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+ presentFence1->signalForTest(60);
+
+ // Fences for the first frame haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
+ auto actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.presentTime, 0);
+
+ // Trigger a flush by finalizing the next DisplayFrame
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+ surfaceFrame2->setAcquireFenceTime(84);
+ mFrameTimeline->setSfWakeUp(sfToken2, 112, Fps::fromPeriodNsecs(30));
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented, 54);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ mFrameTimeline->setSfPresent(116, presentFence2);
+ auto displayFrame2 = getDisplayFrame(1);
+ auto& presentedSurfaceFrame2 = getSurfaceFrame(1, 0);
+ presentFence2->signalForTest(120);
+
+ // Fences for the first frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 60);
+ actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.endTime, 50);
+ EXPECT_EQ(actuals1.presentTime, 60);
+
+ EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+ EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame1->getJankType(), JankType::None);
+
+ EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::AppDeadlineMissed);
+
+ // Fences for the second frame haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+ auto actuals2 = presentedSurfaceFrame2.getActuals();
+ EXPECT_EQ(actuals2.presentTime, 0);
+
+ addEmptyDisplayFrame();
+
+ // Fences for the second frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 120);
+ actuals2 = presentedSurfaceFrame2.getActuals();
+ EXPECT_EQ(actuals2.presentTime, 120);
+
+ EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+ EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame2->getJankType(), JankType::None);
+
+ EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(presentedSurfaceFrame2.getJankType(),
+ JankType::AppDeadlineMissed | JankType::BufferStuffing);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_appDeadlineAdjustedForBufferStuffing) {
+ // Layer specific increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)).Times(2);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
+ int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
+
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({82, 90, 90});
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+ surfaceFrame1->setAcquireFenceTime(50);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, Fps::fromPeriodNsecs(30));
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ mFrameTimeline->setSfPresent(56, presentFence1);
+ auto displayFrame1 = getDisplayFrame(0);
+ auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+ presentFence1->signalForTest(60);
+
+ // Fences for the first frame haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
+ auto actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.presentTime, 0);
+
+ // Trigger a flush by finalizing the next DisplayFrame
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+ surfaceFrame2->setAcquireFenceTime(80);
+ mFrameTimeline->setSfWakeUp(sfToken2, 82, Fps::fromPeriodNsecs(30));
+ // Setting previous latch time to 54, adjusted deadline will be 54 + vsyncTime(30) = 84
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented, 54);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ mFrameTimeline->setSfPresent(86, presentFence2);
+ auto displayFrame2 = getDisplayFrame(1);
+ auto& presentedSurfaceFrame2 = getSurfaceFrame(1, 0);
+ presentFence2->signalForTest(90);
+
+ // Fences for the first frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 60);
+ actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.endTime, 50);
+ EXPECT_EQ(actuals1.presentTime, 60);
+
+ EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+ EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame1->getJankType(), JankType::None);
+
+ EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::AppDeadlineMissed);
+
+ // Fences for the second frame haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+ auto actuals2 = presentedSurfaceFrame2.getActuals();
+ EXPECT_EQ(actuals2.presentTime, 0);
+
+ addEmptyDisplayFrame();
+
+ // Fences for the second frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 90);
+ actuals2 = presentedSurfaceFrame2.getActuals();
+ EXPECT_EQ(actuals2.presentTime, 90);
+
+ EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+ EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame2->getJankType(), JankType::None);
+
+ EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(presentedSurfaceFrame2.getJankType(), JankType::BufferStuffing);
+}
+
+TEST_F(FrameTimelineTest, computeFps_noLayerIds_returnsZero) {
+ EXPECT_EQ(mFrameTimeline->computeFps({}), 0.0f);
+}
+
+TEST_F(FrameTimelineTest, computeFps_singleDisplayFrame_returnsZero) {
+ const auto oneHundredMs = std::chrono::nanoseconds(100ms).count();
+
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ presentFence1->signalForTest(oneHundredMs);
+ mFrameTimeline->setSfPresent(oneHundredMs, presentFence1);
+
+ EXPECT_EQ(mFrameTimeline->computeFps({sLayerIdOne}), 0.0f);
+}
+
+TEST_F(FrameTimelineTest, computeFps_twoDisplayFrames_oneLayer) {
+ const auto oneHundredMs = std::chrono::nanoseconds(100ms).count();
+ const auto twoHundredMs = std::chrono::nanoseconds(200ms).count();
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ presentFence1->signalForTest(oneHundredMs);
+ mFrameTimeline->setSfPresent(oneHundredMs, presentFence1);
+
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ presentFence2->signalForTest(twoHundredMs);
+ mFrameTimeline->setSfPresent(twoHundredMs, presentFence2);
+
+ EXPECT_EQ(mFrameTimeline->computeFps({sLayerIdOne}), 10.0);
+}
+
+TEST_F(FrameTimelineTest, computeFps_twoDisplayFrames_twoLayers) {
+ const auto oneHundredMs = std::chrono::nanoseconds(100ms).count();
+ const auto twoHundredMs = std::chrono::nanoseconds(200ms).count();
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ presentFence1->signalForTest(oneHundredMs);
+ mFrameTimeline->setSfPresent(oneHundredMs, presentFence1);
+
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdTwo, sLayerNameTwo, sLayerNameTwo,
+ /*isBuffer*/ true);
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ presentFence2->signalForTest(twoHundredMs);
+ mFrameTimeline->setSfPresent(twoHundredMs, presentFence2);
+
+ EXPECT_EQ(mFrameTimeline->computeFps({sLayerIdOne, sLayerIdTwo}), 10.0f);
+}
+
+TEST_F(FrameTimelineTest, computeFps_filtersOutLayers) {
+ const auto oneHundredMs = std::chrono::nanoseconds(100ms).count();
+ const auto twoHundredMs = std::chrono::nanoseconds(200ms).count();
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ presentFence1->signalForTest(oneHundredMs);
+ mFrameTimeline->setSfPresent(oneHundredMs, presentFence1);
+
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdTwo, sLayerNameTwo, sLayerNameTwo,
+ /*isBuffer*/ true);
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ presentFence2->signalForTest(twoHundredMs);
+ mFrameTimeline->setSfPresent(twoHundredMs, presentFence2);
+
+ EXPECT_EQ(mFrameTimeline->computeFps({sLayerIdOne}), 0.0f);
+}
+
+TEST_F(FrameTimelineTest, computeFps_averagesOverMultipleFrames) {
+ const auto oneHundredMs = std::chrono::nanoseconds(100ms).count();
+ const auto twoHundredMs = std::chrono::nanoseconds(200ms).count();
+ const auto threeHundredMs = std::chrono::nanoseconds(300ms).count();
+ const auto fiveHundredMs = std::chrono::nanoseconds(500ms).count();
+ const auto sixHundredMs = std::chrono::nanoseconds(600ms).count();
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ presentFence1->signalForTest(oneHundredMs);
+ mFrameTimeline->setSfPresent(oneHundredMs, presentFence1);
+
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ presentFence2->signalForTest(twoHundredMs);
+ mFrameTimeline->setSfPresent(twoHundredMs, presentFence2);
+
+ auto surfaceFrame3 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdTwo, sLayerNameTwo, sLayerNameTwo,
+ /*isBuffer*/ true);
+ auto presentFence3 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame3->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame3);
+ presentFence3->signalForTest(threeHundredMs);
+ mFrameTimeline->setSfPresent(threeHundredMs, presentFence3);
+
+ auto surfaceFrame4 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
+ auto presentFence4 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame4->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame4);
+ presentFence4->signalForTest(fiveHundredMs);
+ mFrameTimeline->setSfPresent(fiveHundredMs, presentFence4);
+
+ auto surfaceFrame5 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
+ auto presentFence5 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ // Dropped frames will be excluded from fps computation
+ surfaceFrame5->setPresentState(SurfaceFrame::PresentState::Dropped);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame5);
+ presentFence5->signalForTest(sixHundredMs);
+ mFrameTimeline->setSfPresent(sixHundredMs, presentFence5);
+
+ EXPECT_EQ(mFrameTimeline->computeFps({sLayerIdOne}), 5.0f);
+}
+
+} // namespace android::frametimeline
diff --git a/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
index a119e27..2c71a2e 100644
--- a/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
@@ -17,6 +17,7 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
#undef LOG_TAG
#define LOG_TAG "LibSurfaceFlingerUnittests"
@@ -381,4 +382,4 @@
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/tests/unittests/FramebufferSurfaceTest.cpp b/services/surfaceflinger/tests/unittests/FramebufferSurfaceTest.cpp
new file mode 100644
index 0000000..b8df640
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FramebufferSurfaceTest.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2021 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 "DisplayHardware/FramebufferSurface.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+
+class FramebufferSurfaceTest : public testing::Test {
+public:
+ ui::Size limitSize(const ui::Size& size, const ui::Size maxSize) {
+ return FramebufferSurface::limitSizeInternal(size, maxSize);
+ }
+};
+
+TEST_F(FramebufferSurfaceTest, limitSize) {
+ const ui::Size kMaxSize(1920, 1080);
+ EXPECT_EQ(ui::Size(1920, 1080), limitSize({3840, 2160}, kMaxSize));
+ EXPECT_EQ(ui::Size(1920, 1080), limitSize({1920, 1080}, kMaxSize));
+ EXPECT_EQ(ui::Size(1920, 1012), limitSize({4096, 2160}, kMaxSize));
+ EXPECT_EQ(ui::Size(1080, 1080), limitSize({3840, 3840}, kMaxSize));
+ EXPECT_EQ(ui::Size(1280, 720), limitSize({1280, 720}, kMaxSize));
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index 91b304c..6b82170 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -23,11 +23,19 @@
#include <vector>
+// StrictMock<T> derives from T and is not marked final, so the destructor of T is expected to be
+// virtual in case StrictMock<T> is used as a polymorphic base class. That is not the case here.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
#include <gmock/gmock.h>
+#pragma clang diagnostic pop
+
#include <gui/LayerMetadata.h>
#include <log/log.h>
+#include "DisplayHardware/DisplayMode.h"
#include "DisplayHardware/HWComposer.h"
+#include "DisplayHardware/Hal.h"
#include "mock/DisplayHardware/MockComposer.h"
// TODO(b/129481165): remove the #pragma below and fix conversion issues
@@ -36,7 +44,10 @@
namespace android {
namespace {
-namespace hal = android::hardware::graphics::composer::hal;
+namespace V2_1 = hardware::graphics::composer::V2_1;
+namespace V2_4 = hardware::graphics::composer::V2_4;
+
+using Hwc2::Config;
using ::testing::_;
using ::testing::DoAll;
@@ -45,27 +56,20 @@
using ::testing::SetArgPointee;
using ::testing::StrictMock;
-struct MockHWC2ComposerCallback : public HWC2::ComposerCallback {
- ~MockHWC2ComposerCallback() = default;
-
- MOCK_METHOD3(onHotplugReceived,
- void(int32_t sequenceId, hal::HWDisplayId display, hal::Connection connection));
- MOCK_METHOD2(onRefreshReceived, void(int32_t sequenceId, hal::HWDisplayId display));
+struct MockHWC2ComposerCallback final : StrictMock<HWC2::ComposerCallback> {
+ MOCK_METHOD3(onHotplugReceived, void(int32_t sequenceId, hal::HWDisplayId, hal::Connection));
+ MOCK_METHOD2(onRefreshReceived, void(int32_t sequenceId, hal::HWDisplayId));
MOCK_METHOD4(onVsyncReceived,
- void(int32_t sequenceId, hal::HWDisplayId display, int64_t timestamp,
- std::optional<hal::VsyncPeriodNanos> vsyncPeriod));
+ void(int32_t sequenceId, hal::HWDisplayId, int64_t timestamp,
+ std::optional<hal::VsyncPeriodNanos>));
MOCK_METHOD3(onVsyncPeriodTimingChangedReceived,
- void(int32_t sequenceId, hal::HWDisplayId display,
- const hal::VsyncPeriodChangeTimeline& updatedTimeline));
- MOCK_METHOD2(onSeamlessPossible, void(int32_t sequenceId, hal::HWDisplayId display));
+ void(int32_t sequenceId, hal::HWDisplayId, const hal::VsyncPeriodChangeTimeline&));
+ MOCK_METHOD2(onSeamlessPossible, void(int32_t sequenceId, hal::HWDisplayId));
};
-struct HWComposerTest : public testing::Test {
+struct HWComposerSetConfigurationTest : testing::Test {
Hwc2::mock::Composer* mHal = new StrictMock<Hwc2::mock::Composer>();
-};
-
-struct HWComposerSetConfigurationTest : public HWComposerTest {
- StrictMock<MockHWC2ComposerCallback> mCallback;
+ MockHWC2ComposerCallback mCallback;
};
TEST_F(HWComposerSetConfigurationTest, loadsLayerMetadataSupport) {
@@ -172,4 +176,4 @@
}
} // namespace
-} // namespace android
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index cae317b..b67ebca 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 The Android Open Source Project
+ * Copyright 2020 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.
@@ -16,11 +16,12 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
#undef LOG_TAG
#define LOG_TAG "LayerHistoryTest"
+#include <Layer.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <log/log.h>
@@ -30,53 +31,102 @@
#include "TestableScheduler.h"
#include "TestableSurfaceFlinger.h"
#include "mock/MockLayer.h"
+#include "mock/MockSchedulerCallback.h"
using testing::_;
using testing::Return;
-namespace android::scheduler {
+namespace android {
+
+namespace scheduler {
class LayerHistoryTest : public testing::Test {
protected:
- static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfo::PresentTimeHistory::HISTORY_SIZE;
- static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfo::MAX_FREQUENT_LAYER_PERIOD_NS;
+ static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfo::HISTORY_SIZE;
+ static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfo::kMaxPeriodForFrequentLayerNs;
+ static constexpr auto FREQUENT_LAYER_WINDOW_SIZE = LayerInfo::kFrequentLayerWindowSize;
+ static constexpr auto PRESENT_TIME_HISTORY_DURATION = LayerInfo::HISTORY_DURATION;
+ static constexpr auto REFRESH_RATE_AVERAGE_HISTORY_DURATION =
+ LayerInfo::RefreshRateHistory::HISTORY_DURATION;
- static constexpr float LO_FPS = 30.f;
- static constexpr nsecs_t LO_FPS_PERIOD = 33'333'333;
+ static constexpr Fps LO_FPS{30.f};
+ static constexpr auto LO_FPS_PERIOD = LO_FPS.getPeriodNsecs();
- static constexpr float HI_FPS = 90.f;
- static constexpr nsecs_t HI_FPS_PERIOD = 11'111'111;
+ static constexpr Fps HI_FPS{90.f};
+ static constexpr auto HI_FPS_PERIOD = HI_FPS.getPeriodNsecs();
LayerHistoryTest() { mFlinger.resetScheduler(mScheduler); }
- impl::LayerHistory& history() { return *mScheduler->mutableLayerHistory(); }
- const impl::LayerHistory& history() const { return *mScheduler->mutableLayerHistory(); }
+ void SetUp() override { ASSERT_TRUE(mScheduler->hasLayerHistory()); }
+
+ LayerHistory& history() { return *mScheduler->mutableLayerHistory(); }
+ const LayerHistory& history() const { return *mScheduler->mutableLayerHistory(); }
size_t layerCount() const { return mScheduler->layerHistorySize(); }
size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS { return history().mActiveLayersEnd; }
- size_t frequentLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
+ auto frequentLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
const auto& infos = history().mLayerInfos;
- return std::count_if(infos.begin(), infos.begin() + history().mActiveLayersEnd,
+ return std::count_if(infos.begin(),
+ infos.begin() + static_cast<long>(history().mActiveLayersEnd),
[now](const auto& pair) { return pair.second->isFrequent(now); });
}
+ auto animatingLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
+ const auto& infos = history().mLayerInfos;
+ return std::count_if(infos.begin(),
+ infos.begin() + static_cast<long>(history().mActiveLayersEnd),
+ [now](const auto& pair) { return pair.second->isAnimating(now); });
+ }
+
+ void setDefaultLayerVote(Layer* layer,
+ LayerHistory::LayerVoteType vote) NO_THREAD_SAFETY_ANALYSIS {
+ for (auto& [layerUnsafe, info] : history().mLayerInfos) {
+ if (layerUnsafe == layer) {
+ info->setDefaultLayerVote(vote);
+ return;
+ }
+ }
+ }
+
auto createLayer() { return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger())); }
+ auto createLayer(std::string name) {
+ return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger(), std::move(name)));
+ }
- Hwc2::mock::Display mDisplay;
- RefreshRateConfigs mConfigs{{HWC2::Display::Config::Builder(mDisplay, 0)
+ void recordFramesAndExpect(const sp<mock::MockLayer>& layer, nsecs_t& time, Fps frameRate,
+ Fps desiredRefreshRate, int numFrames) {
+ LayerHistory::Summary summary;
+ for (int i = 0; i < numFrames; i++) {
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += frameRate.getPeriodNsecs();
+
+ summary = history().summarize(time);
+ }
+
+ ASSERT_EQ(1, summary.size());
+ ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ ASSERT_TRUE(desiredRefreshRate.equalsWithMargin(summary[0].desiredRefreshRate))
+ << "Frame rate is " << frameRate;
+ }
+
+ RefreshRateConfigs mConfigs{{DisplayMode::Builder(0)
+ .setId(DisplayModeId(0))
.setVsyncPeriod(int32_t(LO_FPS_PERIOD))
- .setConfigGroup(0)
+ .setGroup(0)
.build(),
- HWC2::Display::Config::Builder(mDisplay, 1)
+ DisplayMode::Builder(1)
+ .setId(DisplayModeId(1))
.setVsyncPeriod(int32_t(HI_FPS_PERIOD))
- .setConfigGroup(0)
+ .setGroup(0)
.build()},
- HwcConfigIndexType(0)};
- TestableScheduler* const mScheduler{new TestableScheduler(mConfigs, false)};
- TestableSurfaceFlinger mFlinger;
+ DisplayModeId(0)};
- const nsecs_t mTime = systemTime();
+ mock::NoOpSchedulerCallback mSchedulerCallback;
+
+ TestableScheduler* const mScheduler = new TestableScheduler(mConfigs, mSchedulerCallback);
+
+ TestableSurfaceFlinger mFlinger;
};
namespace {
@@ -84,83 +134,260 @@
TEST_F(LayerHistoryTest, oneLayer) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameSelectionPriority()).WillRepeatedly(Return(1));
EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
- // no layers are returned if no layers are active.
- ASSERT_TRUE(history().summarize(mTime).empty());
+ const nsecs_t time = systemTime();
+
+ // No layers returned if no layers are active.
+ EXPECT_TRUE(history().summarize(time).empty());
EXPECT_EQ(0, activeLayerCount());
- // no layers are returned if active layers have insufficient history.
+ // Max returned if active layers have insufficient history.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
- history().record(layer.get(), 0, mTime, LayerHistory::LayerUpdateType::Buffer);
- ASSERT_TRUE(history().summarize(mTime).empty());
+ history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
}
- // High FPS is returned once enough history has been recorded.
+ // Max is returned since we have enough history but there is no timestamp votes.
for (int i = 0; i < 10; i++) {
- history().record(layer.get(), 0, mTime, LayerHistory::LayerUpdateType::Buffer);
- ASSERT_EQ(1, history().summarize(mTime).size());
- EXPECT_FLOAT_EQ(HI_FPS, history().summarize(mTime)[0].desiredRefreshRate);
+ history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
}
}
+TEST_F(LayerHistoryTest, oneInvisibleLayer) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = systemTime();
+
+ history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+ auto summary = history().summarize(time);
+ ASSERT_EQ(1, history().summarize(time).size());
+ // Layer is still considered inactive so we expect to get Min
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
+ history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+
+ summary = history().summarize(time);
+ EXPECT_TRUE(history().summarize(time).empty());
+ EXPECT_EQ(0, activeLayerCount());
+}
+
TEST_F(LayerHistoryTest, explicitTimestamp) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameSelectionPriority()).WillRepeatedly(Return(1));
EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
- nsecs_t time = mTime;
+ nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += LO_FPS_PERIOD;
}
- ASSERT_EQ(1, history().summarize(mTime).size());
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(mTime)[0].desiredRefreshRate);
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
+ EXPECT_TRUE(LO_FPS.equalsWithMargin(history().summarize(time)[0].desiredRefreshRate));
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
}
+TEST_F(LayerHistoryTest, oneLayerNoVote) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::NoVote);
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += HI_FPS_PERIOD;
+ }
+
+ ASSERT_TRUE(history().summarize(time).empty());
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer became inactive
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_TRUE(history().summarize(time).empty());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTest, oneLayerMinVote) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Min);
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += HI_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer became inactive
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_TRUE(history().summarize(time).empty());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTest, oneLayerMaxVote) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Max);
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += LO_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer became inactive
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_TRUE(history().summarize(time).empty());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTest, oneLayerExplicitVote) {
+ auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree())
+ .WillRepeatedly(
+ Return(Layer::FrameRate(Fps(73.4f), Layer::FrameRateCompatibility::Default)));
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += HI_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, history().summarize(time)[0].vote);
+ EXPECT_TRUE(Fps(73.4f).equalsWithMargin(history().summarize(time)[0].desiredRefreshRate));
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer became inactive, but the vote stays
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, history().summarize(time)[0].vote);
+ EXPECT_TRUE(Fps(73.4f).equalsWithMargin(history().summarize(time)[0].desiredRefreshRate));
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTest, oneLayerExplicitExactVote) {
+ auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree())
+ .WillRepeatedly(Return(
+ Layer::FrameRate(Fps(73.4f), Layer::FrameRateCompatibility::ExactOrMultiple)));
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += HI_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+ history().summarize(time)[0].vote);
+ EXPECT_TRUE(Fps(73.4f).equalsWithMargin(history().summarize(time)[0].desiredRefreshRate));
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer became inactive, but the vote stays
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+ history().summarize(time)[0].vote);
+ EXPECT_TRUE(Fps(73.4f).equalsWithMargin(history().summarize(time)[0].desiredRefreshRate));
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
TEST_F(LayerHistoryTest, multipleLayers) {
auto layer1 = createLayer();
auto layer2 = createLayer();
auto layer3 = createLayer();
EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer1, getFrameSelectionPriority()).WillRepeatedly(Return(1));
EXPECT_CALL(*layer1, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer2, getFrameSelectionPriority()).WillRepeatedly(Return(1));
EXPECT_CALL(*layer2, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer3, getFrameSelectionPriority()).WillRepeatedly(Return(1));
EXPECT_CALL(*layer3, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
- nsecs_t time = mTime;
+
+ nsecs_t time = systemTime();
EXPECT_EQ(3, layerCount());
EXPECT_EQ(0, activeLayerCount());
EXPECT_EQ(0, frequentLayerCount(time));
+ LayerHistory::Summary summary;
+
// layer1 is active but infrequent.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+ summary = history().summarize(time);
}
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+ ASSERT_EQ(1, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(0, frequentLayerCount(time));
@@ -168,26 +395,31 @@
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
+ summary = history().summarize(time);
}
// layer1 is still active but infrequent.
history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- ASSERT_EQ(2, history().summarize(time).size());
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
- EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
+ ASSERT_EQ(2, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
+ ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
+ EXPECT_TRUE(HI_FPS.equalsWithMargin(history().summarize(time)[1].desiredRefreshRate));
+
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
// layer1 is no longer active.
// layer2 is frequent and has low refresh rate.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ for (int i = 0; i < 2 * PRESENT_TIME_HISTORY_SIZE; i++) {
history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += LO_FPS_PERIOD;
+ summary = history().summarize(time);
}
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+ ASSERT_EQ(1, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_TRUE(LO_FPS.equalsWithMargin(summary[0].desiredRefreshRate));
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
@@ -201,26 +433,36 @@
history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
+ summary = history().summarize(time);
}
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+ ASSERT_EQ(2, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_TRUE(LO_FPS.equalsWithMargin(summary[0].desiredRefreshRate));
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[1].vote);
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(2, frequentLayerCount(time));
// layer3 becomes recently active.
history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- ASSERT_EQ(2, history().summarize(time).size());
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
- EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
+ summary = history().summarize(time);
+ ASSERT_EQ(2, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_TRUE(LO_FPS.equalsWithMargin(summary[0].desiredRefreshRate));
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
+ EXPECT_TRUE(HI_FPS.equalsWithMargin(summary[1].desiredRefreshRate));
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(2, frequentLayerCount(time));
// layer1 expires.
layer1.clear();
- ASSERT_EQ(2, history().summarize(time).size());
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
- EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
+ summary = history().summarize(time);
+ ASSERT_EQ(2, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_TRUE(LO_FPS.equalsWithMargin(summary[0].desiredRefreshRate));
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
+ EXPECT_TRUE(HI_FPS.equalsWithMargin(summary[1].desiredRefreshRate));
EXPECT_EQ(2, layerCount());
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(2, frequentLayerCount(time));
@@ -230,42 +472,294 @@
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += LO_FPS_PERIOD;
+ summary = history().summarize(time);
}
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+ ASSERT_EQ(1, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_TRUE(LO_FPS.equalsWithMargin(summary[0].desiredRefreshRate));
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
// layer2 expires.
layer2.clear();
- ASSERT_TRUE(history().summarize(time).empty());
+ summary = history().summarize(time);
+ EXPECT_TRUE(summary.empty());
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
EXPECT_EQ(0, frequentLayerCount(time));
// layer3 becomes active and has high refresh rate.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) {
history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
+ summary = history().summarize(time);
}
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[0].desiredRefreshRate);
+ ASSERT_EQ(1, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_TRUE(HI_FPS.equalsWithMargin(summary[0].desiredRefreshRate));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
// layer3 expires.
layer3.clear();
- ASSERT_TRUE(history().summarize(time).empty());
+ summary = history().summarize(time);
+ EXPECT_TRUE(summary.empty());
EXPECT_EQ(0, layerCount());
EXPECT_EQ(0, activeLayerCount());
EXPECT_EQ(0, frequentLayerCount(time));
}
+TEST_F(LayerHistoryTest, inactiveLayers) {
+ auto layer = createLayer();
+
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ nsecs_t time = systemTime();
+
+ // the very first updates makes the layer frequent
+ for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+ EXPECT_EQ(1, layerCount());
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ }
+
+ // the next update with the MAX_FREQUENT_LAYER_PERIOD_NS will get us to infrequent
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+ EXPECT_EQ(1, layerCount());
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ // advance the time for the previous frame to be inactive
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+
+ // Now event if we post a quick few frame we should stay infrequent
+ for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += HI_FPS_PERIOD;
+
+ EXPECT_EQ(1, layerCount());
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ }
+
+ // More quick frames will get us to frequent again
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += HI_FPS_PERIOD;
+
+ EXPECT_EQ(1, layerCount());
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTest, invisibleExplicitLayer) {
+ auto explicitVisiblelayer = createLayer();
+ auto explicitInvisiblelayer = createLayer();
+
+ EXPECT_CALL(*explicitVisiblelayer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*explicitVisiblelayer, getFrameRateForLayerTree())
+ .WillRepeatedly(Return(
+ Layer::FrameRate(Fps(60.0f), Layer::FrameRateCompatibility::ExactOrMultiple)));
+
+ EXPECT_CALL(*explicitInvisiblelayer, isVisible()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*explicitInvisiblelayer, getFrameRateForLayerTree())
+ .WillRepeatedly(Return(
+ Layer::FrameRate(Fps(90.0f), Layer::FrameRateCompatibility::ExactOrMultiple)));
+
+ nsecs_t time = systemTime();
+
+ // Post a buffer to the layers to make them active
+ history().record(explicitVisiblelayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(explicitInvisiblelayer.get(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
+
+ EXPECT_EQ(2, layerCount());
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+ history().summarize(time)[0].vote);
+ EXPECT_TRUE(Fps(60.0f).equalsWithMargin(history().summarize(time)[0].desiredRefreshRate));
+ EXPECT_EQ(2, activeLayerCount());
+ EXPECT_EQ(2, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTest, infrequentAnimatingLayer) {
+ auto layer = createLayer();
+
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // layer is active but infrequent.
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+ }
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // another update with the same cadence keep in infrequent
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // an update as animation will immediately vote for Max
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::AnimationTX);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(1, animatingLayerCount(time));
+}
+
+TEST_F(LayerHistoryTest, heuristicLayer60Hz) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ nsecs_t time = systemTime();
+ for (float fps = 54.0f; fps < 65.0f; fps += 0.1f) {
+ recordFramesAndExpect(layer, time, Fps(fps), Fps(60.0f), PRESENT_TIME_HISTORY_SIZE);
+ }
+}
+
+TEST_F(LayerHistoryTest, heuristicLayer60_30Hz) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ nsecs_t time = systemTime();
+ recordFramesAndExpect(layer, time, Fps(60.0f), Fps(60.0f), PRESENT_TIME_HISTORY_SIZE);
+
+ recordFramesAndExpect(layer, time, Fps(60.0f), Fps(60.0f), PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, Fps(30.0f), Fps(60.0f), PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, Fps(30.0f), Fps(30.0f), PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, Fps(60.0f), Fps(30.0f), PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, Fps(60.0f), Fps(60.0f), PRESENT_TIME_HISTORY_SIZE);
+}
+
+TEST_F(LayerHistoryTest, heuristicLayerNotOscillating) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ nsecs_t time = systemTime();
+
+ recordFramesAndExpect(layer, time, Fps(27.10f), Fps(30.0f), PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, Fps(26.90f), Fps(30.0f), PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, Fps(26.00f), Fps(24.0f), PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, Fps(26.90f), Fps(24.0f), PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, Fps(27.10f), Fps(30.0f), PRESENT_TIME_HISTORY_SIZE);
+}
+
+class LayerHistoryTestParameterized : public LayerHistoryTest,
+ public testing::WithParamInterface<std::chrono::nanoseconds> {
+};
+
+TEST_P(LayerHistoryTestParameterized, HeuristicLayerWithInfrequentLayer) {
+ std::chrono::nanoseconds infrequentUpdateDelta = GetParam();
+ auto heuristicLayer = createLayer("HeuristicLayer");
+
+ EXPECT_CALL(*heuristicLayer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*heuristicLayer, getFrameRateForLayerTree())
+ .WillRepeatedly(Return(Layer::FrameRate()));
+
+ auto infrequentLayer = createLayer("InfrequentLayer");
+ EXPECT_CALL(*infrequentLayer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*infrequentLayer, getFrameRateForLayerTree())
+ .WillRepeatedly(Return(Layer::FrameRate()));
+
+ const nsecs_t startTime = systemTime();
+
+ const std::chrono::nanoseconds heuristicUpdateDelta = 41'666'667ns;
+ history().record(heuristicLayer.get(), startTime, startTime,
+ LayerHistory::LayerUpdateType::Buffer);
+ history().record(infrequentLayer.get(), startTime, startTime,
+ LayerHistory::LayerUpdateType::Buffer);
+
+ nsecs_t time = startTime;
+ nsecs_t lastInfrequentUpdate = startTime;
+ const int totalInfrequentLayerUpdates = FREQUENT_LAYER_WINDOW_SIZE * 5;
+ int infrequentLayerUpdates = 0;
+ while (infrequentLayerUpdates <= totalInfrequentLayerUpdates) {
+ time += heuristicUpdateDelta.count();
+ history().record(heuristicLayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+
+ if (time - lastInfrequentUpdate >= infrequentUpdateDelta.count()) {
+ ALOGI("submitting infrequent frame [%d/%d]", infrequentLayerUpdates,
+ totalInfrequentLayerUpdates);
+ lastInfrequentUpdate = time;
+ history().record(infrequentLayer.get(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
+ infrequentLayerUpdates++;
+ }
+
+ if (time - startTime > PRESENT_TIME_HISTORY_DURATION.count()) {
+ ASSERT_NE(0, history().summarize(time).size());
+ ASSERT_GE(2, history().summarize(time).size());
+
+ bool max = false;
+ bool min = false;
+ Fps heuristic{0.0};
+ for (const auto& layer : history().summarize(time)) {
+ if (layer.vote == LayerHistory::LayerVoteType::Heuristic) {
+ heuristic = layer.desiredRefreshRate;
+ } else if (layer.vote == LayerHistory::LayerVoteType::Max) {
+ max = true;
+ } else if (layer.vote == LayerHistory::LayerVoteType::Min) {
+ min = true;
+ }
+ }
+
+ if (infrequentLayerUpdates > FREQUENT_LAYER_WINDOW_SIZE) {
+ EXPECT_TRUE(Fps(24.0f).equalsWithMargin(heuristic));
+ EXPECT_FALSE(max);
+ if (history().summarize(time).size() == 2) {
+ EXPECT_TRUE(min);
+ }
+ }
+ }
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(LeapYearTests, LayerHistoryTestParameterized,
+ ::testing::Values(1s, 2s, 3s, 4s, 5s));
+
} // namespace
-} // namespace android::scheduler
+} // namespace scheduler
+} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
deleted file mode 100644
index afd2b71..0000000
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
+++ /dev/null
@@ -1,748 +0,0 @@
-/*
- * Copyright 2020 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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "LayerHistoryTestV2"
-
-#include <Layer.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <log/log.h>
-
-#include "Scheduler/LayerHistory.h"
-#include "Scheduler/LayerInfoV2.h"
-#include "TestableScheduler.h"
-#include "TestableSurfaceFlinger.h"
-#include "mock/MockLayer.h"
-
-using testing::_;
-using testing::Return;
-
-namespace android::scheduler {
-
-class LayerHistoryTestV2 : public testing::Test {
-protected:
- static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfoV2::HISTORY_SIZE;
- static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfoV2::MAX_FREQUENT_LAYER_PERIOD_NS;
- static constexpr auto FREQUENT_LAYER_WINDOW_SIZE = LayerInfoV2::FREQUENT_LAYER_WINDOW_SIZE;
- static constexpr auto PRESENT_TIME_HISTORY_DURATION = LayerInfoV2::HISTORY_DURATION;
- static constexpr auto REFRESH_RATE_AVERAGE_HISTORY_DURATION =
- LayerInfoV2::RefreshRateHistory::HISTORY_DURATION;
-
- static constexpr float LO_FPS = 30.f;
- static constexpr auto LO_FPS_PERIOD = static_cast<nsecs_t>(1e9f / LO_FPS);
-
- static constexpr float HI_FPS = 90.f;
- static constexpr auto HI_FPS_PERIOD = static_cast<nsecs_t>(1e9f / HI_FPS);
-
- LayerHistoryTestV2() { mFlinger.resetScheduler(mScheduler); }
-
- impl::LayerHistoryV2& history() { return *mScheduler->mutableLayerHistoryV2(); }
- const impl::LayerHistoryV2& history() const { return *mScheduler->mutableLayerHistoryV2(); }
-
- size_t layerCount() const { return mScheduler->layerHistorySize(); }
- size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS { return history().mActiveLayersEnd; }
-
- auto frequentLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
- const auto& infos = history().mLayerInfos;
- return std::count_if(infos.begin(),
- infos.begin() + static_cast<long>(history().mActiveLayersEnd),
- [now](const auto& pair) { return pair.second->isFrequent(now); });
- }
-
- auto animatingLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
- const auto& infos = history().mLayerInfos;
- return std::count_if(infos.begin(),
- infos.begin() + static_cast<long>(history().mActiveLayersEnd),
- [now](const auto& pair) { return pair.second->isAnimating(now); });
- }
-
- void setLayerInfoVote(Layer* layer,
- LayerHistory::LayerVoteType vote) NO_THREAD_SAFETY_ANALYSIS {
- for (auto& [weak, info] : history().mLayerInfos) {
- if (auto strong = weak.promote(); strong && strong.get() == layer) {
- info->setDefaultLayerVote(vote);
- info->setLayerVote(vote, 0);
- return;
- }
- }
- }
-
- auto createLayer() { return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger())); }
- auto createLayer(std::string name) {
- return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger(), std::move(name)));
- }
-
- void recordFramesAndExpect(const sp<mock::MockLayer>& layer, nsecs_t& time, float frameRate,
- float desiredRefreshRate, int numFrames) {
- const nsecs_t framePeriod = static_cast<nsecs_t>(1e9f / frameRate);
- impl::LayerHistoryV2::Summary summary;
- for (int i = 0; i < numFrames; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += framePeriod;
-
- summary = history().summarize(time);
- }
-
- ASSERT_EQ(1, summary.size());
- ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- ASSERT_FLOAT_EQ(desiredRefreshRate, summary[0].desiredRefreshRate)
- << "Frame rate is " << frameRate;
- }
-
- Hwc2::mock::Display mDisplay;
- RefreshRateConfigs mConfigs{{HWC2::Display::Config::Builder(mDisplay, 0)
- .setVsyncPeriod(int32_t(LO_FPS_PERIOD))
- .setConfigGroup(0)
- .build(),
- HWC2::Display::Config::Builder(mDisplay, 1)
- .setVsyncPeriod(int32_t(HI_FPS_PERIOD))
- .setConfigGroup(0)
- .build()},
- HwcConfigIndexType(0)};
- TestableScheduler* const mScheduler{new TestableScheduler(mConfigs, true)};
- TestableSurfaceFlinger mFlinger;
-
-};
-
-namespace {
-
-TEST_F(LayerHistoryTestV2, oneLayer) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- const nsecs_t time = systemTime();
-
- // No layers returned if no layers are active.
- EXPECT_TRUE(history().summarize(time).empty());
- EXPECT_EQ(0, activeLayerCount());
-
- // Max returned if active layers have insufficient history.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
- history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- }
-
- // Max is returned since we have enough history but there is no timestamp votes.
- for (int i = 0; i < 10; i++) {
- history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- }
-}
-
-TEST_F(LayerHistoryTestV2, oneInvisibleLayer) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
-
- history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
- auto summary = history().summarize(time);
- ASSERT_EQ(1, history().summarize(time).size());
- // Layer is still considered inactive so we expect to get Min
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
-
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
-
- summary = history().summarize(time);
- EXPECT_TRUE(history().summarize(time).empty());
- EXPECT_EQ(0, activeLayerCount());
-}
-
-TEST_F(LayerHistoryTestV2, explicitTimestamp) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += LO_FPS_PERIOD;
- }
-
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTestV2, oneLayerNoVote) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::NoVote);
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- }
-
- ASSERT_TRUE(history().summarize(time).empty());
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer became inactive
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_TRUE(history().summarize(time).empty());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTestV2, oneLayerMinVote) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Min);
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- }
-
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer became inactive
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_TRUE(history().summarize(time).empty());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTestV2, oneLayerMaxVote) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Max);
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += LO_FPS_PERIOD;
- }
-
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer became inactive
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_TRUE(history().summarize(time).empty());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTestV2, oneLayerExplicitVote) {
- auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree())
- .WillRepeatedly(
- Return(Layer::FrameRate(73.4f, Layer::FrameRateCompatibility::Default)));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- }
-
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, history().summarize(time)[0].vote);
- EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer became inactive, but the vote stays
- setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, history().summarize(time)[0].vote);
- EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTestV2, oneLayerExplicitExactVote) {
- auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree())
- .WillRepeatedly(Return(
- Layer::FrameRate(73.4f, Layer::FrameRateCompatibility::ExactOrMultiple)));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- }
-
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
- history().summarize(time)[0].vote);
- EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer became inactive, but the vote stays
- setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
- history().summarize(time)[0].vote);
- EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTestV2, multipleLayers) {
- auto layer1 = createLayer();
- auto layer2 = createLayer();
- auto layer3 = createLayer();
-
- EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer1, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer2, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer3, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- nsecs_t time = systemTime();
-
- EXPECT_EQ(3, layerCount());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-
- impl::LayerHistoryV2::Summary summary;
-
- // layer1 is active but infrequent.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
- summary = history().summarize(time);
- }
-
- ASSERT_EQ(1, summary.size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-
- // layer2 is frequent and has high refresh rate.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- summary = history().summarize(time);
- }
-
- // layer1 is still active but infrequent.
- history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-
- ASSERT_EQ(2, summary.size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
- ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
- EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
- EXPECT_EQ(2, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer1 is no longer active.
- // layer2 is frequent and has low refresh rate.
- for (int i = 0; i < 2 * PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += LO_FPS_PERIOD;
- summary = history().summarize(time);
- }
-
- ASSERT_EQ(1, summary.size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer2 still has low refresh rate.
- // layer3 has high refresh rate but not enough history.
- constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD;
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
- if (i % RATIO == 0) {
- history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- }
-
- history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- summary = history().summarize(time);
- }
-
- ASSERT_EQ(2, summary.size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[1].vote);
- EXPECT_EQ(2, activeLayerCount());
- EXPECT_EQ(2, frequentLayerCount(time));
-
- // layer3 becomes recently active.
- history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- summary = history().summarize(time);
- ASSERT_EQ(2, summary.size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
- EXPECT_FLOAT_EQ(HI_FPS, summary[1].desiredRefreshRate);
- EXPECT_EQ(2, activeLayerCount());
- EXPECT_EQ(2, frequentLayerCount(time));
-
- // layer1 expires.
- layer1.clear();
- summary = history().summarize(time);
- ASSERT_EQ(2, summary.size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
- EXPECT_FLOAT_EQ(HI_FPS, summary[1].desiredRefreshRate);
- EXPECT_EQ(2, layerCount());
- EXPECT_EQ(2, activeLayerCount());
- EXPECT_EQ(2, frequentLayerCount(time));
-
- // layer2 still has low refresh rate.
- // layer3 becomes inactive.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += LO_FPS_PERIOD;
- summary = history().summarize(time);
- }
-
- ASSERT_EQ(1, summary.size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer2 expires.
- layer2.clear();
- summary = history().summarize(time);
- EXPECT_TRUE(summary.empty());
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-
- // layer3 becomes active and has high refresh rate.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) {
- history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- summary = history().summarize(time);
- }
-
- ASSERT_EQ(1, summary.size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_FLOAT_EQ(HI_FPS, summary[0].desiredRefreshRate);
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer3 expires.
- layer3.clear();
- summary = history().summarize(time);
- EXPECT_TRUE(summary.empty());
- EXPECT_EQ(0, layerCount());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTestV2, inactiveLayers) {
- auto layer = createLayer();
-
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- nsecs_t time = systemTime();
-
- // the very first updates makes the layer frequent
- for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-
- EXPECT_EQ(1, layerCount());
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
- }
-
- // the next update with the MAX_FREQUENT_LAYER_PERIOD_NS will get us to infrequent
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-
- EXPECT_EQ(1, layerCount());
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-
- // advance the time for the previous frame to be inactive
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
-
- // Now event if we post a quick few frame we should stay infrequent
- for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
-
- EXPECT_EQ(1, layerCount());
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- }
-
- // More quick frames will get us to frequent again
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
-
- EXPECT_EQ(1, layerCount());
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTestV2, invisibleExplicitLayer) {
- auto explicitVisiblelayer = createLayer();
- auto explicitInvisiblelayer = createLayer();
-
- EXPECT_CALL(*explicitVisiblelayer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*explicitVisiblelayer, getFrameRateForLayerTree())
- .WillRepeatedly(Return(
- Layer::FrameRate(60.0f, Layer::FrameRateCompatibility::ExactOrMultiple)));
-
- EXPECT_CALL(*explicitInvisiblelayer, isVisible()).WillRepeatedly(Return(false));
- EXPECT_CALL(*explicitInvisiblelayer, getFrameRateForLayerTree())
- .WillRepeatedly(Return(
- Layer::FrameRate(90.0f, Layer::FrameRateCompatibility::ExactOrMultiple)));
-
- nsecs_t time = systemTime();
-
- // Post a buffer to the layers to make them active
- history().record(explicitVisiblelayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- history().record(explicitInvisiblelayer.get(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
-
- EXPECT_EQ(2, layerCount());
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
- history().summarize(time)[0].vote);
- EXPECT_FLOAT_EQ(60.0f, history().summarize(time)[0].desiredRefreshRate);
- EXPECT_EQ(2, activeLayerCount());
- EXPECT_EQ(2, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTestV2, infrequentAnimatingLayer) {
- auto layer = createLayer();
-
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- nsecs_t time = systemTime();
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-
- // layer is active but infrequent.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
- }
-
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-
- // another update with the same cadence keep in infrequent
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-
- // an update as animation will immediately vote for Max
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::AnimationTX);
- time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(1, animatingLayerCount(time));
-}
-
-TEST_F(LayerHistoryTestV2, heuristicLayer60Hz) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- nsecs_t time = systemTime();
- for (float fps = 54.0f; fps < 65.0f; fps += 0.1f) {
- recordFramesAndExpect(layer, time, fps, 60.0f, PRESENT_TIME_HISTORY_SIZE);
- }
-}
-
-TEST_F(LayerHistoryTestV2, heuristicLayer60_30Hz) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- nsecs_t time = systemTime();
- recordFramesAndExpect(layer, time, 60.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
-
- recordFramesAndExpect(layer, time, 60.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, 30.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, 30.0f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, 60.0f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, 60.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
-}
-
-TEST_F(LayerHistoryTestV2, heuristicLayerNotOscillating) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- nsecs_t time = systemTime();
-
- recordFramesAndExpect(layer, time, 27.10f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, 26.90f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, 26.00f, 24.0f, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, 26.90f, 24.0f, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, 27.10f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
-}
-
-class LayerHistoryTestV2Parameterized
- : public LayerHistoryTestV2,
- public testing::WithParamInterface<std::chrono::nanoseconds> {};
-
-TEST_P(LayerHistoryTestV2Parameterized, HeuristicLayerWithInfrequentLayer) {
- std::chrono::nanoseconds infrequentUpdateDelta = GetParam();
- auto heuristicLayer = createLayer("HeuristicLayer");
-
- EXPECT_CALL(*heuristicLayer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*heuristicLayer, getFrameRateForLayerTree())
- .WillRepeatedly(Return(Layer::FrameRate()));
-
- auto infrequentLayer = createLayer("InfrequentLayer");
- EXPECT_CALL(*infrequentLayer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*infrequentLayer, getFrameRateForLayerTree())
- .WillRepeatedly(Return(Layer::FrameRate()));
-
- const nsecs_t startTime = systemTime();
-
- const std::chrono::nanoseconds heuristicUpdateDelta = 41'666'667ns;
- history().record(heuristicLayer.get(), startTime, startTime,
- LayerHistory::LayerUpdateType::Buffer);
- history().record(infrequentLayer.get(), startTime, startTime,
- LayerHistory::LayerUpdateType::Buffer);
-
- nsecs_t time = startTime;
- nsecs_t lastInfrequentUpdate = startTime;
- const int totalInfrequentLayerUpdates = FREQUENT_LAYER_WINDOW_SIZE * 5;
- int infrequentLayerUpdates = 0;
- while (infrequentLayerUpdates <= totalInfrequentLayerUpdates) {
- time += heuristicUpdateDelta.count();
- history().record(heuristicLayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-
- if (time - lastInfrequentUpdate >= infrequentUpdateDelta.count()) {
- ALOGI("submitting infrequent frame [%d/%d]", infrequentLayerUpdates,
- totalInfrequentLayerUpdates);
- lastInfrequentUpdate = time;
- history().record(infrequentLayer.get(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- infrequentLayerUpdates++;
- }
-
- if (time - startTime > PRESENT_TIME_HISTORY_DURATION.count()) {
- ASSERT_NE(0, history().summarize(time).size());
- ASSERT_GE(2, history().summarize(time).size());
-
- bool max = false;
- bool min = false;
- float heuristic = 0;
- for (const auto& layer : history().summarize(time)) {
- if (layer.vote == LayerHistory::LayerVoteType::Heuristic) {
- heuristic = layer.desiredRefreshRate;
- } else if (layer.vote == LayerHistory::LayerVoteType::Max) {
- max = true;
- } else if (layer.vote == LayerHistory::LayerVoteType::Min) {
- min = true;
- }
- }
-
- if (infrequentLayerUpdates > FREQUENT_LAYER_WINDOW_SIZE) {
- EXPECT_FLOAT_EQ(24.0f, heuristic);
- EXPECT_FALSE(max);
- if (history().summarize(time).size() == 2) {
- EXPECT_TRUE(min);
- }
- }
- }
- }
-}
-
-INSTANTIATE_TEST_CASE_P(LeapYearTests, LayerHistoryTestV2Parameterized,
- ::testing::Values(1s, 2s, 3s, 4s, 5s));
-
-} // namespace
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
new file mode 100644
index 0000000..325fb8f
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LayerInfoTest"
+
+#include <gtest/gtest.h>
+
+#include "Fps.h"
+#include "Scheduler/LayerHistory.h"
+#include "Scheduler/LayerInfo.h"
+
+namespace android::scheduler {
+
+class LayerInfoTest : public testing::Test {
+protected:
+ using FrameTimeData = LayerInfo::FrameTimeData;
+
+ void setFrameTimes(const std::deque<FrameTimeData>& frameTimes) {
+ layerInfo.mFrameTimes = frameTimes;
+ }
+
+ void setLastRefreshRate(Fps fps) {
+ layerInfo.mLastRefreshRate.reported = fps;
+ layerInfo.mLastRefreshRate.calculated = fps;
+ }
+
+ auto calculateAverageFrameTime() { return layerInfo.calculateAverageFrameTime(); }
+
+ LayerInfo layerInfo{"TestLayerInfo", 0, LayerHistory::LayerVoteType::Heuristic};
+};
+
+namespace {
+
+TEST_F(LayerInfoTest, prefersPresentTime) {
+ std::deque<FrameTimeData> frameTimes;
+ constexpr auto kExpectedFps = Fps(50.0f);
+ constexpr auto kPeriod = kExpectedFps.getPeriodNsecs();
+ constexpr int kNumFrames = 10;
+ for (int i = 1; i <= kNumFrames; i++) {
+ frameTimes.push_back(FrameTimeData{.presentTime = kPeriod * i,
+ .queueTime = 0,
+ .pendingModeChange = false});
+ }
+ setFrameTimes(frameTimes);
+ const auto averageFrameTime = calculateAverageFrameTime();
+ ASSERT_TRUE(averageFrameTime.has_value());
+ const auto averageFps = Fps::fromPeriodNsecs(*averageFrameTime);
+ ASSERT_TRUE(kExpectedFps.equalsWithMargin(averageFps))
+ << "Expected " << averageFps << " to be equal to " << kExpectedFps;
+}
+
+TEST_F(LayerInfoTest, fallbacksToQueueTimeIfNoPresentTime) {
+ std::deque<FrameTimeData> frameTimes;
+ constexpr auto kExpectedFps = Fps(50.0f);
+ constexpr auto kPeriod = kExpectedFps.getPeriodNsecs();
+ constexpr int kNumFrames = 10;
+ for (int i = 1; i <= kNumFrames; i++) {
+ frameTimes.push_back(FrameTimeData{.presentTime = 0,
+ .queueTime = kPeriod * i,
+ .pendingModeChange = false});
+ }
+ setFrameTimes(frameTimes);
+ setLastRefreshRate(Fps(20.0f)); // Set to some valid value
+ const auto averageFrameTime = calculateAverageFrameTime();
+ ASSERT_TRUE(averageFrameTime.has_value());
+ const auto averageFps = Fps::fromPeriodNsecs(*averageFrameTime);
+ ASSERT_TRUE(kExpectedFps.equalsWithMargin(averageFps))
+ << "Expected " << averageFps << " to be equal to " << kExpectedFps;
+}
+
+TEST_F(LayerInfoTest, returnsNulloptIfThereWasConfigChange) {
+ std::deque<FrameTimeData> frameTimesWithoutConfigChange;
+ const auto period = Fps(50.0f).getPeriodNsecs();
+ constexpr int kNumFrames = 10;
+ for (int i = 1; i <= kNumFrames; i++) {
+ frameTimesWithoutConfigChange.push_back(FrameTimeData{.presentTime = period * i,
+ .queueTime = period * i,
+ .pendingModeChange = false});
+ }
+
+ setFrameTimes(frameTimesWithoutConfigChange);
+ ASSERT_TRUE(calculateAverageFrameTime().has_value());
+
+ {
+ // Config change in the first record
+ auto frameTimes = frameTimesWithoutConfigChange;
+ frameTimes[0].pendingModeChange = true;
+ setFrameTimes(frameTimes);
+ ASSERT_FALSE(calculateAverageFrameTime().has_value());
+ }
+
+ {
+ // Config change in the last record
+ auto frameTimes = frameTimesWithoutConfigChange;
+ frameTimes[frameTimes.size() - 1].pendingModeChange = true;
+ setFrameTimes(frameTimes);
+ ASSERT_FALSE(calculateAverageFrameTime().has_value());
+ }
+
+ {
+ // Config change in the middle
+ auto frameTimes = frameTimesWithoutConfigChange;
+ frameTimes[frameTimes.size() / 2].pendingModeChange = true;
+ setFrameTimes(frameTimes);
+ ASSERT_FALSE(calculateAverageFrameTime().has_value());
+ }
+}
+
+// A frame can be recorded twice with very close presentation or queue times.
+// Make sure that this doesn't influence the calculated average FPS.
+TEST_F(LayerInfoTest, ignoresSmallPeriods) {
+ std::deque<FrameTimeData> frameTimes;
+ constexpr auto kExpectedFps = Fps(50.0f);
+ constexpr auto kExpectedPeriod = kExpectedFps.getPeriodNsecs();
+ constexpr auto kSmallPeriod = Fps(150.0f).getPeriodNsecs();
+ constexpr int kNumIterations = 10;
+ for (int i = 1; i <= kNumIterations; i++) {
+ frameTimes.push_back(FrameTimeData{.presentTime = kExpectedPeriod * i,
+ .queueTime = 0,
+ .pendingModeChange = false});
+
+ // A duplicate frame
+ frameTimes.push_back(FrameTimeData{.presentTime = kExpectedPeriod * i + kSmallPeriod,
+ .queueTime = 0,
+ .pendingModeChange = false});
+ }
+ setFrameTimes(frameTimes);
+ const auto averageFrameTime = calculateAverageFrameTime();
+ ASSERT_TRUE(averageFrameTime.has_value());
+ const auto averageFps = Fps::fromPeriodNsecs(*averageFrameTime);
+ ASSERT_TRUE(kExpectedFps.equalsWithMargin(averageFps))
+ << "Expected " << averageFps << " to be equal to " << kExpectedFps;
+}
+
+// There may be a big period of time between two frames. Make sure that
+// this doesn't influence the calculated average FPS.
+TEST_F(LayerInfoTest, ignoresLargePeriods) {
+ std::deque<FrameTimeData> frameTimes;
+ constexpr auto kExpectedFps = Fps(50.0f);
+ constexpr auto kExpectedPeriod = kExpectedFps.getPeriodNsecs();
+ constexpr auto kLargePeriod = Fps(9.0f).getPeriodNsecs();
+
+ auto record = [&](nsecs_t time) {
+ frameTimes.push_back(
+ FrameTimeData{.presentTime = time, .queueTime = 0, .pendingModeChange = false});
+ };
+
+ auto time = kExpectedPeriod; // Start with non-zero time.
+ record(time);
+ time += kLargePeriod;
+ record(time);
+ constexpr int kNumIterations = 10;
+ for (int i = 1; i <= kNumIterations; i++) {
+ time += kExpectedPeriod;
+ record(time);
+ }
+
+ setFrameTimes(frameTimes);
+ const auto averageFrameTime = calculateAverageFrameTime();
+ ASSERT_TRUE(averageFrameTime.has_value());
+ const auto averageFps = Fps::fromPeriodNsecs(*averageFrameTime);
+ ASSERT_TRUE(kExpectedFps.equalsWithMargin(averageFps))
+ << "Expected " << averageFps << " to be equal to " << kExpectedFps;
+}
+
+} // namespace
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp b/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp
index 75a061b..373fd74 100644
--- a/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wextra"
+
#undef LOG_TAG
#define LOG_TAG "LibSurfaceFlingerUnittests"
@@ -107,3 +111,6 @@
} // namespace
} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
new file mode 100644
index 0000000..8208b3f
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "FrameTimeline.h"
+#include "Scheduler/MessageQueue.h"
+#include "SurfaceFlinger.h"
+
+namespace android {
+
+using namespace std::chrono_literals;
+using namespace testing;
+
+using CallbackToken = scheduler::VSyncDispatch::CallbackToken;
+
+class TestableMessageQueue : public impl::MessageQueue {
+public:
+ class MockHandler : public MessageQueue::Handler {
+ public:
+ explicit MockHandler(MessageQueue& queue) : MessageQueue::Handler(queue) {}
+ ~MockHandler() override = default;
+ MOCK_METHOD2(dispatchInvalidate, void(int64_t vsyncId, nsecs_t expectedVSyncTimestamp));
+ };
+
+ TestableMessageQueue() = default;
+ ~TestableMessageQueue() override = default;
+
+ void initHandler(const sp<MockHandler>& handler) { mHandler = handler; }
+
+ void triggerVsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime) {
+ vsyncCallback(vsyncTime, targetWakeupTime, readyTime);
+ }
+};
+
+class MockVSyncDispatch : public scheduler::VSyncDispatch {
+public:
+ MockVSyncDispatch() = default;
+ ~MockVSyncDispatch() override = default;
+
+ MOCK_METHOD2(registerCallback,
+ CallbackToken(std::function<void(nsecs_t, nsecs_t, nsecs_t)> const&, std::string));
+ MOCK_METHOD1(unregisterCallback, void(CallbackToken));
+ MOCK_METHOD2(schedule, scheduler::ScheduleResult(CallbackToken, ScheduleTiming));
+ MOCK_METHOD1(cancel, scheduler::CancelResult(CallbackToken token));
+ MOCK_CONST_METHOD1(dump, void(std::string&));
+};
+
+class MockTokenManager : public frametimeline::TokenManager {
+public:
+ MockTokenManager() = default;
+ ~MockTokenManager() override = default;
+
+ MOCK_METHOD1(generateTokenForPredictions, int64_t(frametimeline::TimelineItem&& prediction));
+ MOCK_CONST_METHOD1(getPredictionsForToken, std::optional<frametimeline::TimelineItem>(int64_t));
+};
+
+class MessageQueueTest : public testing::Test {
+public:
+ MessageQueueTest() = default;
+ ~MessageQueueTest() override = default;
+
+ void SetUp() override {
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.initHandler(mHandler));
+
+ EXPECT_CALL(mVSyncDispatch, registerCallback(_, "sf")).WillOnce(Return(mCallbackToken));
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.initVsync(mVSyncDispatch, mTokenManager, mDuration));
+ EXPECT_CALL(mVSyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
+ }
+
+ sp<TestableMessageQueue::MockHandler> mHandler =
+ new TestableMessageQueue::MockHandler(mEventQueue);
+ MockVSyncDispatch mVSyncDispatch;
+ MockTokenManager mTokenManager;
+ TestableMessageQueue mEventQueue;
+
+ const CallbackToken mCallbackToken{5};
+ constexpr static auto mDuration = std::chrono::nanoseconds(100ms);
+ constexpr static auto mDifferentDuration = std::chrono::nanoseconds(250ms);
+};
+
+namespace {
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_F(MessageQueueTest, invalidate) {
+ const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
+ .readyDuration = 0,
+ .earliestVsync = 0};
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+}
+
+TEST_F(MessageQueueTest, invalidateTwice) {
+ InSequence s;
+ const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
+ .readyDuration = 0,
+ .earliestVsync = 0};
+
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+}
+
+TEST_F(MessageQueueTest, invalidateTwiceWithCallback) {
+ InSequence s;
+ const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
+ .readyDuration = 0,
+ .earliestVsync = 0};
+
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+
+ const auto startTime = 100;
+ const auto endTime = startTime + mDuration.count();
+ const auto presentTime = 500;
+ const auto vsyncId = 42;
+ EXPECT_CALL(mTokenManager,
+ generateTokenForPredictions(
+ frametimeline::TimelineItem(startTime, endTime, presentTime)))
+ .WillOnce(Return(vsyncId));
+ EXPECT_CALL(*mHandler, dispatchInvalidate(vsyncId, presentTime)).Times(1);
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.triggerVsyncCallback(presentTime, startTime, endTime));
+
+ const auto timingAfterCallback =
+ scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
+ .readyDuration = 0,
+ .earliestVsync = presentTime};
+
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).Times(1);
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+}
+
+TEST_F(MessageQueueTest, invalidateWithDurationChange) {
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.setDuration(mDifferentDuration));
+
+ const auto timing =
+ scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDifferentDuration.count(),
+ .readyDuration = 0,
+ .earliestVsync = 0};
+
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
index 0208728..6916764 100644
--- a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
@@ -19,9 +19,11 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <utils/Log.h>
+#include <utils/Timers.h>
#include "AsyncCallRecorder.h"
#include "Scheduler/OneShotTimer.h"
+#include "fake/FakeClock.h"
using namespace std::chrono_literals;
@@ -33,16 +35,6 @@
OneShotTimerTest() = default;
~OneShotTimerTest() override = default;
- // This timeout should be used when a 3ms callback is expected.
- // While the tests typically request a callback after 3ms, the scheduler
- // does not always cooperate, at it can take significantly longer (observed
- // 30ms).
- static constexpr auto waitTimeForExpected3msCallback = 100ms;
-
- // This timeout should be used when an 3ms callback is not expected.
- // Note that there can be false-negatives if the callback happens later.
- static constexpr auto waitTimeForUnexpected3msCallback = 6ms;
-
AsyncCallRecorder<void (*)()> mResetTimerCallback;
AsyncCallRecorder<void (*)()> mExpiredTimerCallback;
@@ -56,154 +48,179 @@
namespace {
TEST_F(OneShotTimerTest, createAndDestroyTest) {
+ fake::FakeClock* clock = new fake::FakeClock();
mIdleTimer = std::make_unique<scheduler::OneShotTimer>(
- 3ms, [] {}, [] {});
+ "TestTimer", 3ms, [] {}, [] {}, std::unique_ptr<fake::FakeClock>(clock));
}
TEST_F(OneShotTimerTest, startStopTest) {
- mIdleTimer = std::make_unique<scheduler::OneShotTimer>(30ms, mResetTimerCallback.getInvocable(),
- mExpiredTimerCallback.getInvocable());
- auto startTime = std::chrono::steady_clock::now();
+ fake::FakeClock* clock = new fake::FakeClock();
+ mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
+ mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable(),
+ std::unique_ptr<fake::FakeClock>(clock));
mIdleTimer->start();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
- // The idle timer fires after 30ms, so there should be no callback within
- // 25ms (waiting for a callback for the full 30ms would be problematic).
- bool callbackCalled = mExpiredTimerCallback.waitForCall(25ms).has_value();
- // Under ideal conditions there should be no event. But occasionally
- // it is possible that the wait just prior takes more than 30ms, and
- // a callback is observed. We check the elapsed time since before the OneShotTimer
- // thread was started as a sanity check to not have a flakey test.
- EXPECT_FALSE(callbackCalled && std::chrono::steady_clock::now() - startTime < 30ms);
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
- std::this_thread::sleep_for(std::chrono::milliseconds(25));
- EXPECT_FALSE(mResetTimerCallback.waitForCall().has_value());
+ clock->advanceTime(2ms);
+ EXPECT_TRUE(mExpiredTimerCallback.waitForCall().has_value());
+
+ clock->advanceTime(2ms);
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
mIdleTimer->stop();
}
TEST_F(OneShotTimerTest, resetTest) {
- mIdleTimer = std::make_unique<scheduler::OneShotTimer>(20ms, mResetTimerCallback.getInvocable(),
- mExpiredTimerCallback.getInvocable());
+ fake::FakeClock* clock = new fake::FakeClock();
+ mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
+ mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable(),
+ std::unique_ptr<fake::FakeClock>(clock));
+
mIdleTimer->start();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
- // Observe any event that happens in about 25ms. We don't care if one was
- // observed or not.
- mExpiredTimerCallback.waitForCall(25ms).has_value();
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+ clock->advanceTime(2ms);
+ EXPECT_TRUE(mExpiredTimerCallback.waitForCall().has_value());
mIdleTimer->reset();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
- // There may have been a race with the reset. Clear any callbacks we
- // received right afterwards.
- clearPendingCallbacks();
- // A single callback should be generated after 30ms
- EXPECT_TRUE(
- mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback + 30ms).has_value());
- // After one event, it should be idle, and not generate another.
- EXPECT_FALSE(
- mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback * 10).has_value());
- mIdleTimer->stop();
- // Final quick check that no more callback were observed.
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall(0ms).has_value());
- EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
+ clock->advanceTime(2ms);
+ EXPECT_TRUE(mExpiredTimerCallback.waitForCall().has_value());
+
+ clock->advanceTime(2ms);
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
}
TEST_F(OneShotTimerTest, resetBackToBackTest) {
- mIdleTimer = std::make_unique<scheduler::OneShotTimer>(20ms, mResetTimerCallback.getInvocable(),
- mExpiredTimerCallback.getInvocable());
+ fake::FakeClock* clock = new fake::FakeClock();
+ mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
+ mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable(),
+ std::unique_ptr<fake::FakeClock>(clock));
mIdleTimer->start();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
mIdleTimer->reset();
- EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).has_value());
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
mIdleTimer->reset();
- EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).has_value());
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
mIdleTimer->reset();
- EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).has_value());
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
mIdleTimer->reset();
- EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).has_value());
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
- // A single callback should be generated after 30ms
- EXPECT_TRUE(
- mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback + 30ms).has_value());
+ clock->advanceTime(2ms);
+ EXPECT_TRUE(mExpiredTimerCallback.waitForCall().has_value());
+
mIdleTimer->stop();
+ clock->advanceTime(2ms);
// Final quick check that no more callback were observed.
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall(0ms).has_value());
- EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
}
TEST_F(OneShotTimerTest, startNotCalledTest) {
- mIdleTimer = std::make_unique<scheduler::OneShotTimer>(3ms, mResetTimerCallback.getInvocable(),
- mExpiredTimerCallback.getInvocable());
+ fake::FakeClock* clock = new fake::FakeClock();
+ mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
+ mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable(),
+ std::unique_ptr<fake::FakeClock>(clock));
// The start hasn't happened, so the callback does not happen.
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
- EXPECT_FALSE(mResetTimerCallback.waitForCall().has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
mIdleTimer->stop();
+ clock->advanceTime(2ms);
// Final quick check that no more callback were observed.
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall(0ms).has_value());
- EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
}
TEST_F(OneShotTimerTest, idleTimerIdlesTest) {
- mIdleTimer = std::make_unique<scheduler::OneShotTimer>(3ms, mResetTimerCallback.getInvocable(),
- mExpiredTimerCallback.getInvocable());
+ fake::FakeClock* clock = new fake::FakeClock();
+ mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
+ mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable(),
+ std::unique_ptr<fake::FakeClock>(clock));
mIdleTimer->start();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+ clock->advanceTime(2ms);
+ EXPECT_TRUE(mExpiredTimerCallback.waitForCall().has_value());
- // A callback should be generated after 3ms
- EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
- // After one event, it should be idle, and not generate another.
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
- // Once reset, it should generate another
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
+
mIdleTimer->reset();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
- EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
+ clock->advanceTime(2ms);
+ EXPECT_TRUE(mExpiredTimerCallback.waitForCall().has_value());
mIdleTimer->stop();
+ clock->advanceTime(2ms);
// Final quick check that no more callback were observed.
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall(0ms).has_value());
- EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
}
TEST_F(OneShotTimerTest, timeoutCallbackExecutionTest) {
- mIdleTimer = std::make_unique<scheduler::OneShotTimer>(3ms, mResetTimerCallback.getInvocable(),
- mExpiredTimerCallback.getInvocable());
+ fake::FakeClock* clock = new fake::FakeClock();
+ mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
+ mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable(),
+ std::unique_ptr<fake::FakeClock>(clock));
mIdleTimer->start();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
- EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
+
+ clock->advanceTime(2ms);
+ EXPECT_TRUE(mExpiredTimerCallback.waitForCall().has_value());
mIdleTimer->stop();
+ clock->advanceTime(2ms);
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
}
TEST_F(OneShotTimerTest, noCallbacksAfterStopAndResetTest) {
- mIdleTimer = std::make_unique<scheduler::OneShotTimer>(3ms, mResetTimerCallback.getInvocable(),
- mExpiredTimerCallback.getInvocable());
+ fake::FakeClock* clock = new fake::FakeClock();
+ mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
+ mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable(),
+ std::unique_ptr<fake::FakeClock>(clock));
mIdleTimer->start();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
- EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
+ clock->advanceTime(2ms);
+ EXPECT_TRUE(mExpiredTimerCallback.waitForCall().has_value());
mIdleTimer->stop();
- clearPendingCallbacks();
mIdleTimer->reset();
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
- EXPECT_FALSE(mResetTimerCallback.waitForCall().has_value());
+ clock->advanceTime(2ms);
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
}
TEST_F(OneShotTimerTest, noCallbacksAfterStopTest) {
- mIdleTimer = std::make_unique<scheduler::OneShotTimer>(3ms, mResetTimerCallback.getInvocable(),
- mExpiredTimerCallback.getInvocable());
+ fake::FakeClock* clock = new fake::FakeClock();
+ mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
+ mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable(),
+ std::unique_ptr<fake::FakeClock>(clock));
mIdleTimer->start();
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
mIdleTimer->stop();
- clearPendingCallbacks();
mIdleTimer->reset();
+ clock->advanceTime(2ms);
// No more idle events should be observed
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
- EXPECT_FALSE(mResetTimerCallback.waitForCall().has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
deleted file mode 100644
index 0b74682..0000000
--- a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright 2019 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.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#undef LOG_TAG
-#define LOG_TAG "SchedulerUnittests"
-
-#include <gmock/gmock.h>
-#include <log/log.h>
-#include <thread>
-
-#include "Scheduler/PhaseOffsets.h"
-
-using namespace testing;
-
-namespace android {
-namespace scheduler {
-
-class TestablePhaseOffsetsAsDurations : public impl::PhaseDurations {
-public:
- TestablePhaseOffsetsAsDurations(float currentFps, nsecs_t sfDuration, nsecs_t appDuration,
- nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
- nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration)
- : impl::PhaseDurations({60.0f, 90.0f}, currentFps, sfDuration, appDuration,
- sfEarlyDuration, appEarlyDuration, sfEarlyGlDuration,
- appEarlyGlDuration) {}
-};
-
-class PhaseDurationTest : public testing::Test {
-protected:
- PhaseDurationTest()
- : mPhaseDurations(60.0f, 10'500'000, 20'500'000, 16'000'000, 16'500'000, 13'500'000,
- 21'000'000) {}
-
- ~PhaseDurationTest() = default;
-
- TestablePhaseOffsetsAsDurations mPhaseDurations;
-};
-
-namespace {
-/* ------------------------------------------------------------------------
- * Test cases
- */
-TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_60Hz) {
- mPhaseDurations.setRefreshRateFps(60.0f);
- auto currentOffsets = mPhaseDurations.getCurrentOffsets();
- auto offsets = mPhaseDurations.getOffsetsForRefreshRate(60.0f);
-
- EXPECT_EQ(currentOffsets, offsets);
- EXPECT_EQ(offsets.late.sf, 6'166'667);
-
- EXPECT_EQ(offsets.late.app, 2'333'334);
-
- EXPECT_EQ(offsets.early.sf, 666'667);
-
- EXPECT_EQ(offsets.early.app, 833'334);
-
- EXPECT_EQ(offsets.earlyGl.sf, 3'166'667);
-
- EXPECT_EQ(offsets.earlyGl.app, 15'500'001);
-}
-
-TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_90Hz) {
- mPhaseDurations.setRefreshRateFps(90.0f);
- auto currentOffsets = mPhaseDurations.getCurrentOffsets();
- auto offsets = mPhaseDurations.getOffsetsForRefreshRate(90.0f);
-
- EXPECT_EQ(currentOffsets, offsets);
- EXPECT_EQ(offsets.late.sf, 611'111);
-
- EXPECT_EQ(offsets.late.app, 2'333'333);
-
- EXPECT_EQ(offsets.early.sf, -4'888'889);
-
- EXPECT_EQ(offsets.early.app, 833'333);
-
- EXPECT_EQ(offsets.earlyGl.sf, -2'388'889);
-
- EXPECT_EQ(offsets.earlyGl.app, 9'944'444);
-}
-
-TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_DefaultOffsets) {
- TestablePhaseOffsetsAsDurations phaseOffsetsWithDefaultValues(60.0f, -1, -1, -1, -1, -1, -1);
-
- auto validateOffsets = [](auto& offsets) {
- EXPECT_EQ(offsets.late.sf, 1'000'000);
-
- EXPECT_EQ(offsets.late.app, 1'000'000);
-
- EXPECT_EQ(offsets.early.sf, 1'000'000);
-
- EXPECT_EQ(offsets.early.app, 1'000'000);
-
- EXPECT_EQ(offsets.earlyGl.sf, 1'000'000);
-
- EXPECT_EQ(offsets.earlyGl.app, 1'000'000);
- };
-
- phaseOffsetsWithDefaultValues.setRefreshRateFps(90.0f);
- auto currentOffsets = phaseOffsetsWithDefaultValues.getCurrentOffsets();
- auto offsets = phaseOffsetsWithDefaultValues.getOffsetsForRefreshRate(90.0f);
- EXPECT_EQ(currentOffsets, offsets);
- validateOffsets(offsets);
-
- phaseOffsetsWithDefaultValues.setRefreshRateFps(60.0f);
- currentOffsets = phaseOffsetsWithDefaultValues.getCurrentOffsets();
- offsets = phaseOffsetsWithDefaultValues.getOffsetsForRefreshRate(90.0f);
- EXPECT_EQ(currentOffsets, offsets);
- validateOffsets(offsets);
-}
-
-TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_unknownRefreshRate) {
- auto offsets = mPhaseDurations.getOffsetsForRefreshRate(14.7f);
-
- EXPECT_EQ(offsets.late.sf, 57'527'208);
-
- EXPECT_EQ(offsets.late.app, 37'027'208);
-
- EXPECT_EQ(offsets.early.sf, 52'027'208);
-
- EXPECT_EQ(offsets.early.app, 35'527'208);
-
- EXPECT_EQ(offsets.earlyGl.sf, 54'527'208);
-
- EXPECT_EQ(offsets.earlyGl.app, 33'527'208);
-}
-
-} // namespace
-
-class TestablePhaseOffsets : public impl::PhaseOffsets {
-public:
- TestablePhaseOffsets()
- : impl::PhaseOffsets({60.0f, 90.0f}, 60.0f, 1'000'000, 1'000'000, {}, {}, {}, {},
- 10'000'000) {}
-};
-
-class PhaseOffsetsTest : public testing::Test {
-protected:
- PhaseOffsetsTest() = default;
- ~PhaseOffsetsTest() = default;
-
- TestablePhaseOffsets mPhaseOffsets;
-};
-
-namespace {
-TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_unknownRefreshRate) {
- auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(14.7f);
-
- EXPECT_EQ(offsets.late.sf, 1'000'000);
-
- EXPECT_EQ(offsets.late.app, 1'000'000);
-
- EXPECT_EQ(offsets.early.sf, 1'000'000);
-
- EXPECT_EQ(offsets.early.app, 1'000'000);
-
- EXPECT_EQ(offsets.earlyGl.sf, 1'000'000);
-
- EXPECT_EQ(offsets.earlyGl.app, 1'000'000);
-}
-
-} // namespace
-} // namespace scheduler
-} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/PromiseTest.cpp b/services/surfaceflinger/tests/unittests/PromiseTest.cpp
deleted file mode 100644
index e4dc1fe..0000000
--- a/services/surfaceflinger/tests/unittests/PromiseTest.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright 2020 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 <algorithm>
-#include <future>
-#include <string>
-#include <thread>
-#include <vector>
-
-#include <gtest/gtest.h>
-
-#include "Promise.h"
-
-namespace android {
-namespace {
-
-using Bytes = std::vector<uint8_t>;
-
-Bytes decrement(Bytes bytes) {
- std::transform(bytes.begin(), bytes.end(), bytes.begin(), [](auto b) { return b - 1; });
- return bytes;
-}
-
-} // namespace
-
-TEST(PromiseTest, yield) {
- EXPECT_EQ(42, promise::yield(42).get());
-
- auto ptr = std::make_unique<char>('!');
- auto future = promise::yield(std::move(ptr));
- EXPECT_EQ('!', *future.get());
-}
-
-TEST(PromiseTest, chain) {
- std::packaged_task<const char*()> fetchString([] { return "ifmmp-"; });
-
- std::packaged_task<Bytes(std::string)> appendString([](std::string str) {
- str += "!xpsme";
- return Bytes{str.begin(), str.end()};
- });
-
- std::packaged_task<std::future<Bytes>(Bytes)> decrementBytes(
- [](Bytes bytes) { return promise::defer(decrement, std::move(bytes)); });
-
- auto fetch = fetchString.get_future();
- std::thread fetchThread(std::move(fetchString));
-
- std::thread appendThread, decrementThread;
-
- EXPECT_EQ("hello, world",
- promise::chain(std::move(fetch))
- .then([](const char* str) { return std::string(str); })
- .then([&](std::string str) {
- auto append = appendString.get_future();
- appendThread = std::thread(std::move(appendString), std::move(str));
- return append;
- })
- .then([&](Bytes bytes) {
- auto decrement = decrementBytes.get_future();
- decrementThread = std::thread(std::move(decrementBytes),
- std::move(bytes));
- return decrement;
- })
- .then([](std::future<Bytes> bytes) { return bytes; })
- .then([](const Bytes& bytes) {
- return std::string(bytes.begin(), bytes.end());
- })
- .get());
-
- fetchThread.join();
- appendThread.join();
- decrementThread.join();
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 1f6f166..0b70f27 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wextra"
+
#undef LOG_TAG
#define LOG_TAG "SchedulerUnittests"
@@ -21,15 +25,16 @@
#include <log/log.h>
#include <thread>
+#include <ui/Size.h>
+
#include "../../Scheduler/RefreshRateConfigs.h"
#include "DisplayHardware/HWC2.h"
#include "Scheduler/RefreshRateConfigs.h"
-#include "mock/DisplayHardware/MockDisplay.h"
using namespace std::chrono_literals;
-using testing::_;
namespace android {
+
namespace scheduler {
namespace hal = android::hardware::graphics::composer::hal;
@@ -43,99 +48,108 @@
RefreshRateConfigsTest();
~RefreshRateConfigsTest();
- float findClosestKnownFrameRate(const RefreshRateConfigs& refreshRateConfigs, float frameRate) {
+ Fps findClosestKnownFrameRate(const RefreshRateConfigs& refreshRateConfigs, Fps frameRate) {
return refreshRateConfigs.findClosestKnownFrameRate(frameRate);
}
- std::vector<float> getKnownFrameRate(const RefreshRateConfigs& refreshRateConfigs) {
+ std::vector<Fps> getKnownFrameRate(const RefreshRateConfigs& refreshRateConfigs) {
return refreshRateConfigs.mKnownFrameRates;
}
+ RefreshRate getMinRefreshRateByPolicy(const RefreshRateConfigs& refreshRateConfigs) {
+ std::lock_guard lock(refreshRateConfigs.mLock);
+ return refreshRateConfigs.getMinRefreshRateByPolicyLocked();
+ }
+
+ RefreshRate getMinSupportedRefreshRate(const RefreshRateConfigs& refreshRateConfigs) {
+ std::lock_guard lock(refreshRateConfigs.mLock);
+ return *refreshRateConfigs.mMinSupportedRefreshRate;
+ }
+
+ RefreshRate getMaxSupportedRefreshRate(const RefreshRateConfigs& refreshRateConfigs) {
+ std::lock_guard lock(refreshRateConfigs.mLock);
+ return *refreshRateConfigs.mMaxSupportedRefreshRate;
+ }
+
// Test config IDs
- static inline const HwcConfigIndexType HWC_CONFIG_ID_60 = HwcConfigIndexType(0);
- static inline const HwcConfigIndexType HWC_CONFIG_ID_90 = HwcConfigIndexType(1);
- static inline const HwcConfigIndexType HWC_CONFIG_ID_72 = HwcConfigIndexType(2);
- static inline const HwcConfigIndexType HWC_CONFIG_ID_120 = HwcConfigIndexType(3);
- static inline const HwcConfigIndexType HWC_CONFIG_ID_30 = HwcConfigIndexType(4);
+ static inline const DisplayModeId HWC_CONFIG_ID_60 = DisplayModeId(0);
+ static inline const DisplayModeId HWC_CONFIG_ID_90 = DisplayModeId(1);
+ static inline const DisplayModeId HWC_CONFIG_ID_72 = DisplayModeId(2);
+ static inline const DisplayModeId HWC_CONFIG_ID_120 = DisplayModeId(3);
+ static inline const DisplayModeId HWC_CONFIG_ID_30 = DisplayModeId(4);
+ static inline const DisplayModeId HWC_CONFIG_ID_25 = DisplayModeId(5);
+ static inline const DisplayModeId HWC_CONFIG_ID_50 = DisplayModeId(6);
// Test configs
- std::shared_ptr<const HWC2::Display::Config> mConfig60 =
- createConfig(HWC_CONFIG_ID_60, 0, static_cast<int64_t>(1e9f / 60));
- std::shared_ptr<const HWC2::Display::Config> mConfig90 =
- createConfig(HWC_CONFIG_ID_90, 0, static_cast<int64_t>(1e9f / 90));
- std::shared_ptr<const HWC2::Display::Config> mConfig90DifferentGroup =
- createConfig(HWC_CONFIG_ID_90, 1, static_cast<int64_t>(1e9f / 90));
- std::shared_ptr<const HWC2::Display::Config> mConfig90DifferentResolution =
- createConfig(HWC_CONFIG_ID_90, 0, static_cast<int64_t>(1e9f / 90), 111, 222);
- std::shared_ptr<const HWC2::Display::Config> mConfig72 =
- createConfig(HWC_CONFIG_ID_72, 0, static_cast<int64_t>(1e9f / 72));
- std::shared_ptr<const HWC2::Display::Config> mConfig72DifferentGroup =
- createConfig(HWC_CONFIG_ID_72, 1, static_cast<int64_t>(1e9f / 72));
- std::shared_ptr<const HWC2::Display::Config> mConfig120 =
- createConfig(HWC_CONFIG_ID_120, 0, static_cast<int64_t>(1e9f / 120));
- std::shared_ptr<const HWC2::Display::Config> mConfig120DifferentGroup =
- createConfig(HWC_CONFIG_ID_120, 1, static_cast<int64_t>(1e9f / 120));
- std::shared_ptr<const HWC2::Display::Config> mConfig30 =
- createConfig(HWC_CONFIG_ID_30, 0, static_cast<int64_t>(1e9f / 30));
+ DisplayModePtr mConfig60 = createDisplayMode(HWC_CONFIG_ID_60, 0, Fps(60.0f).getPeriodNsecs());
+ DisplayModePtr mConfig90 = createDisplayMode(HWC_CONFIG_ID_90, 0, Fps(90.0f).getPeriodNsecs());
+ DisplayModePtr mConfig90DifferentGroup =
+ createDisplayMode(HWC_CONFIG_ID_90, 1, Fps(90.0f).getPeriodNsecs());
+ DisplayModePtr mConfig90DifferentResolution =
+ createDisplayMode(HWC_CONFIG_ID_90, 0, Fps(90.0f).getPeriodNsecs(), ui::Size(111, 222));
+ DisplayModePtr mConfig72 = createDisplayMode(HWC_CONFIG_ID_72, 0, Fps(72.0f).getPeriodNsecs());
+ DisplayModePtr mConfig72DifferentGroup =
+ createDisplayMode(HWC_CONFIG_ID_72, 1, Fps(72.0f).getPeriodNsecs());
+ DisplayModePtr mConfig120 =
+ createDisplayMode(HWC_CONFIG_ID_120, 0, Fps(120.0f).getPeriodNsecs());
+ DisplayModePtr mConfig120DifferentGroup =
+ createDisplayMode(HWC_CONFIG_ID_120, 1, Fps(120.0f).getPeriodNsecs());
+ DisplayModePtr mConfig30 = createDisplayMode(HWC_CONFIG_ID_30, 0, Fps(30.0f).getPeriodNsecs());
+ DisplayModePtr mConfig30DifferentGroup =
+ createDisplayMode(HWC_CONFIG_ID_30, 1, Fps(30.0f).getPeriodNsecs());
+ DisplayModePtr mConfig25DifferentGroup =
+ createDisplayMode(HWC_CONFIG_ID_25, 1, Fps(25.0f).getPeriodNsecs());
+ DisplayModePtr mConfig50 = createDisplayMode(HWC_CONFIG_ID_50, 0, Fps(50.0f).getPeriodNsecs());
// Test device configurations
- std::vector<std::shared_ptr<const HWC2::Display::Config>> m60OnlyConfigDevice = {mConfig60};
- std::vector<std::shared_ptr<const HWC2::Display::Config>> m60_90Device = {mConfig60, mConfig90};
- std::vector<std::shared_ptr<const HWC2::Display::Config>> m60_90DeviceWithDifferentGroups =
- {mConfig60, mConfig90DifferentGroup};
- std::vector<std::shared_ptr<const HWC2::Display::Config>> m60_90DeviceWithDifferentResolutions =
- {mConfig60, mConfig90DifferentResolution};
- std::vector<std::shared_ptr<const HWC2::Display::Config>> m60_72_90Device = {mConfig60,
- mConfig90,
- mConfig72};
- std::vector<std::shared_ptr<const HWC2::Display::Config>> m60_90_72_120Device = {mConfig60,
- mConfig90,
- mConfig72,
- mConfig120};
- std::vector<std::shared_ptr<const HWC2::Display::Config>> m30_60_72_90_120Device = {mConfig60,
- mConfig90,
- mConfig72,
- mConfig120,
- mConfig30};
- std::vector<std::shared_ptr<const HWC2::Display::Config>> m30_60Device =
- {mConfig60, mConfig90DifferentGroup, mConfig72DifferentGroup, mConfig120DifferentGroup,
- mConfig30};
- std::vector<std::shared_ptr<const HWC2::Display::Config>> m30_60_72_90Device =
- {mConfig60, mConfig90, mConfig72, mConfig120DifferentGroup, mConfig30};
- std::vector<std::shared_ptr<const HWC2::Display::Config>> m30_60_90Device =
- {mConfig60, mConfig90, mConfig72DifferentGroup, mConfig120DifferentGroup, mConfig30};
+ // The positions of the configs in the arrays below MUST match their IDs. For example,
+ // the first config should always be 60Hz, the second 90Hz etc.
+ DisplayModes m60OnlyConfigDevice = {mConfig60};
+ DisplayModes m60_90Device = {mConfig60, mConfig90};
+ DisplayModes m60_90DeviceWithDifferentGroups = {mConfig60, mConfig90DifferentGroup};
+ DisplayModes m60_90DeviceWithDifferentResolutions = {mConfig60, mConfig90DifferentResolution};
+ DisplayModes m60_72_90Device = {mConfig60, mConfig90, mConfig72};
+ DisplayModes m60_90_72_120Device = {mConfig60, mConfig90, mConfig72, mConfig120};
+ DisplayModes m30_60_72_90_120Device = {mConfig60, mConfig90, mConfig72, mConfig120, mConfig30};
+ DisplayModes m30_60Device = {mConfig60, mConfig90DifferentGroup, mConfig72DifferentGroup,
+ mConfig120DifferentGroup, mConfig30};
+ DisplayModes m30_60_72_90Device = {mConfig60, mConfig90, mConfig72, mConfig120DifferentGroup,
+ mConfig30};
+ DisplayModes m30_60_90Device = {mConfig60, mConfig90, mConfig72DifferentGroup,
+ mConfig120DifferentGroup, mConfig30};
+ DisplayModes m25_30_50_60Device = {mConfig60,
+ mConfig90,
+ mConfig72DifferentGroup,
+ mConfig120DifferentGroup,
+ mConfig30DifferentGroup,
+ mConfig25DifferentGroup,
+ mConfig50};
// Expected RefreshRate objects
- RefreshRate mExpected60Config = {HWC_CONFIG_ID_60, mConfig60, "60fps", 60,
+ RefreshRate mExpected60Config = {HWC_CONFIG_ID_60, mConfig60, Fps(60),
RefreshRate::ConstructorTag(0)};
RefreshRate mExpectedAlmost60Config = {HWC_CONFIG_ID_60,
- createConfig(HWC_CONFIG_ID_60, 0, 16666665), "60fps", 60,
- RefreshRate::ConstructorTag(0)};
- RefreshRate mExpected90Config = {HWC_CONFIG_ID_90, mConfig90, "90fps", 90,
+ createDisplayMode(HWC_CONFIG_ID_60, 0, 16666665),
+ Fps(60), RefreshRate::ConstructorTag(0)};
+ RefreshRate mExpected90Config = {HWC_CONFIG_ID_90, mConfig90, Fps(90),
RefreshRate::ConstructorTag(0)};
RefreshRate mExpected90DifferentGroupConfig = {HWC_CONFIG_ID_90, mConfig90DifferentGroup,
- "90fps", 90, RefreshRate::ConstructorTag(0)};
+ Fps(90), RefreshRate::ConstructorTag(0)};
RefreshRate mExpected90DifferentResolutionConfig = {HWC_CONFIG_ID_90,
- mConfig90DifferentResolution, "90fps", 90,
+ mConfig90DifferentResolution, Fps(90),
RefreshRate::ConstructorTag(0)};
- RefreshRate mExpected72Config = {HWC_CONFIG_ID_72, mConfig72, "72fps", 72,
+ RefreshRate mExpected72Config = {HWC_CONFIG_ID_72, mConfig72, Fps(72.0f),
RefreshRate::ConstructorTag(0)};
- RefreshRate mExpected30Config = {HWC_CONFIG_ID_30, mConfig30, "30fps", 30,
+ RefreshRate mExpected30Config = {HWC_CONFIG_ID_30, mConfig30, Fps(30),
RefreshRate::ConstructorTag(0)};
- RefreshRate mExpected120Config = {HWC_CONFIG_ID_120, mConfig120, "120fps", 120,
+ RefreshRate mExpected120Config = {HWC_CONFIG_ID_120, mConfig120, Fps(120),
RefreshRate::ConstructorTag(0)};
-
- Hwc2::mock::Display mDisplay;
-
private:
- std::shared_ptr<const HWC2::Display::Config> createConfig(HwcConfigIndexType configId,
- int32_t configGroup,
- int64_t vsyncPeriod,
- int32_t hight = -1,
- int32_t width = -1);
+ DisplayModePtr createDisplayMode(DisplayModeId modeId, int32_t group, int64_t vsyncPeriod,
+ ui::Size resolution = ui::Size());
};
-using Builder = HWC2::Display::Config::Builder;
+using Builder = DisplayMode::Builder;
RefreshRateConfigsTest::RefreshRateConfigsTest() {
const ::testing::TestInfo* const test_info =
@@ -149,14 +163,14 @@
ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
}
-std::shared_ptr<const HWC2::Display::Config> RefreshRateConfigsTest::createConfig(
- HwcConfigIndexType configId, int32_t configGroup, int64_t vsyncPeriod, int32_t hight,
- int32_t width) {
- return HWC2::Display::Config::Builder(mDisplay, hal::HWConfigId(configId.value()))
+DisplayModePtr RefreshRateConfigsTest::createDisplayMode(DisplayModeId modeId, int32_t group,
+ int64_t vsyncPeriod, ui::Size resolution) {
+ return DisplayMode::Builder(hal::HWConfigId(modeId.value()))
+ .setId(modeId)
.setVsyncPeriod(int32_t(vsyncPeriod))
- .setConfigGroup(configGroup)
- .setHeight(hight)
- .setWidth(width)
+ .setGroup(group)
+ .setHeight(resolution.height)
+ .setWidth(resolution.width)
.build();
}
@@ -174,8 +188,10 @@
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60OnlyConfigDevice,
/*currentConfigId=*/HWC_CONFIG_ID_60);
- ASSERT_LT(refreshRateConfigs->setDisplayManagerPolicy({HwcConfigIndexType(10), {60, 60}}), 0);
- ASSERT_LT(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {20, 40}}), 0);
+ ASSERT_LT(refreshRateConfigs->setDisplayManagerPolicy({DisplayModeId(10), {Fps(60), Fps(60)}}),
+ 0);
+ ASSERT_LT(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(20), Fps(40)}}),
+ 0);
}
TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap) {
@@ -183,13 +199,13 @@
std::make_unique<RefreshRateConfigs>(m60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
- const auto& minRate = refreshRateConfigs->getMinRefreshRate();
- const auto& performanceRate = refreshRateConfigs->getMaxRefreshRate();
+ const auto& minRate = getMinSupportedRefreshRate(*refreshRateConfigs);
+ const auto& performanceRate = getMaxSupportedRefreshRate(*refreshRateConfigs);
ASSERT_EQ(mExpected60Config, minRate);
ASSERT_EQ(mExpected90Config, performanceRate);
- const auto& minRateByPolicy = refreshRateConfigs->getMinRefreshRateByPolicy();
+ const auto& minRateByPolicy = getMinRefreshRateByPolicy(*refreshRateConfigs);
const auto& performanceRateByPolicy = refreshRateConfigs->getMaxRefreshRateByPolicy();
ASSERT_EQ(minRateByPolicy, minRate);
ASSERT_EQ(performanceRateByPolicy, performanceRate);
@@ -200,19 +216,20 @@
std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
/*currentConfigId=*/HWC_CONFIG_ID_60);
- const auto& minRate = refreshRateConfigs->getMinRefreshRateByPolicy();
- const auto& performanceRate = refreshRateConfigs->getMaxRefreshRate();
- const auto& minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
+ const auto& minRate = getMinRefreshRateByPolicy(*refreshRateConfigs);
+ const auto& performanceRate = getMaxSupportedRefreshRate(*refreshRateConfigs);
+ const auto& minRate60 = getMinRefreshRateByPolicy(*refreshRateConfigs);
const auto& performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
ASSERT_EQ(mExpected60Config, minRate);
ASSERT_EQ(mExpected60Config, minRate60);
ASSERT_EQ(mExpected60Config, performanceRate60);
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {60, 90}}), 0);
- refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {Fps(60), Fps(90)}}),
+ 0);
+ refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
- const auto& minRate90 = refreshRateConfigs->getMinRefreshRateByPolicy();
+ const auto& minRate90 = getMinRefreshRateByPolicy(*refreshRateConfigs);
const auto& performanceRate90 = refreshRateConfigs->getMaxRefreshRateByPolicy();
ASSERT_EQ(mExpected90DifferentGroupConfig, performanceRate);
@@ -225,19 +242,20 @@
std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentResolutions,
/*currentConfigId=*/HWC_CONFIG_ID_60);
- const auto& minRate = refreshRateConfigs->getMinRefreshRateByPolicy();
- const auto& performanceRate = refreshRateConfigs->getMaxRefreshRate();
- const auto& minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
+ const auto& minRate = getMinRefreshRateByPolicy(*refreshRateConfigs);
+ const auto& performanceRate = getMaxSupportedRefreshRate(*refreshRateConfigs);
+ const auto& minRate60 = getMinRefreshRateByPolicy(*refreshRateConfigs);
const auto& performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
ASSERT_EQ(mExpected60Config, minRate);
ASSERT_EQ(mExpected60Config, minRate60);
ASSERT_EQ(mExpected60Config, performanceRate60);
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {60, 90}}), 0);
- refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {Fps(60), Fps(90)}}),
+ 0);
+ refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
- const auto& minRate90 = refreshRateConfigs->getMinRefreshRateByPolicy();
+ const auto& minRate90 = getMinRefreshRateByPolicy(*refreshRateConfigs);
const auto& performanceRate90 = refreshRateConfigs->getMaxRefreshRateByPolicy();
ASSERT_EQ(mExpected90DifferentResolutionConfig, performanceRate);
@@ -250,16 +268,17 @@
std::make_unique<RefreshRateConfigs>(m60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
- auto& minRate = refreshRateConfigs->getMinRefreshRateByPolicy();
- auto& performanceRate = refreshRateConfigs->getMaxRefreshRateByPolicy();
+ auto minRate = getMinRefreshRateByPolicy(*refreshRateConfigs);
+ auto performanceRate = refreshRateConfigs->getMaxRefreshRateByPolicy();
ASSERT_EQ(mExpected60Config, minRate);
ASSERT_EQ(mExpected90Config, performanceRate);
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 60}}), 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(60)}}),
+ 0);
- auto& minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
- auto& performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
+ auto minRate60 = getMinRefreshRateByPolicy(*refreshRateConfigs);
+ auto performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
ASSERT_EQ(mExpected60Config, minRate60);
ASSERT_EQ(mExpected60Config, performanceRate60);
}
@@ -269,80 +288,24 @@
std::make_unique<RefreshRateConfigs>(m60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
{
- auto& current = refreshRateConfigs->getCurrentRefreshRate();
- EXPECT_EQ(current.getConfigId(), HWC_CONFIG_ID_60);
+ auto current = refreshRateConfigs->getCurrentRefreshRate();
+ EXPECT_EQ(current.getModeId(), HWC_CONFIG_ID_60);
}
- refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+ refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
{
- auto& current = refreshRateConfigs->getCurrentRefreshRate();
- EXPECT_EQ(current.getConfigId(), HWC_CONFIG_ID_90);
+ auto current = refreshRateConfigs->getCurrentRefreshRate();
+ EXPECT_EQ(current.getModeId(), HWC_CONFIG_ID_90);
}
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90, 90}}), 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {Fps(90), Fps(90)}}),
+ 0);
{
- auto& current = refreshRateConfigs->getCurrentRefreshRate();
- EXPECT_EQ(current.getConfigId(), HWC_CONFIG_ID_90);
+ auto current = refreshRateConfigs->getCurrentRefreshRate();
+ EXPECT_EQ(current.getModeId(), HWC_CONFIG_ID_90);
}
}
-TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContent) {
- auto refreshRateConfigs =
- std::make_unique<RefreshRateConfigs>(m60_90Device,
- /*currentConfigId=*/HWC_CONFIG_ID_60);
-
- const auto makeLayerRequirements = [](float refreshRate) -> std::vector<LayerRequirement> {
- return {{"testLayer", LayerVoteType::Heuristic, refreshRate, /*weight*/ 1.0f,
- /*focused*/ false}};
- };
-
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(60.0f)));
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(45.0f)));
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(30.0f)));
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
-
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 60}}), 0);
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(60.0f)));
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(45.0f)));
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(30.0f)));
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
-
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90, 90}}), 0);
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(60.0f)));
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(45.0f)));
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(30.0f)));
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {0, 120}}), 0);
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(60.0f)));
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(45.0f)));
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(30.0f)));
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
-}
-
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_noLayers) {
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_72_90Device, /*currentConfigId=*/
@@ -354,7 +317,8 @@
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 60}}), 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(60)}}),
+ 0);
EXPECT_EQ(mExpected60Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
@@ -377,34 +341,35 @@
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 90.0f;
+ lr.desiredRefreshRate = Fps(90.0f);
lr.vote = LayerVoteType::Heuristic;
lr.name = "90Hz Heuristic";
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 60.0f;
+ lr.desiredRefreshRate = Fps(60.0f);
lr.name = "60Hz Heuristic";
EXPECT_EQ(mExpected60Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 45.0f;
+ lr.desiredRefreshRate = Fps(45.0f);
lr.name = "45Hz Heuristic";
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 30.0f;
+ lr.desiredRefreshRate = Fps(30.0f);
lr.name = "30Hz Heuristic";
EXPECT_EQ(mExpected60Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 24.0f;
+ lr.desiredRefreshRate = Fps(24.0f);
lr.name = "24Hz Heuristic";
EXPECT_EQ(mExpected60Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.name = "";
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 60}}), 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(60)}}),
+ 0);
lr.vote = LayerVoteType::Min;
EXPECT_EQ(mExpected60Config,
@@ -414,28 +379,30 @@
EXPECT_EQ(mExpected60Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 90.0f;
+ lr.desiredRefreshRate = Fps(90.0f);
lr.vote = LayerVoteType::Heuristic;
EXPECT_EQ(mExpected60Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 60.0f;
+ lr.desiredRefreshRate = Fps(60.0f);
EXPECT_EQ(mExpected60Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 45.0f;
+ lr.desiredRefreshRate = Fps(45.0f);
EXPECT_EQ(mExpected60Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 30.0f;
+ lr.desiredRefreshRate = Fps(30.0f);
EXPECT_EQ(mExpected60Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 24.0f;
+ lr.desiredRefreshRate = Fps(24.0f);
EXPECT_EQ(mExpected60Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90, 90}}), 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
+ {HWC_CONFIG_ID_90, {Fps(90.0f), Fps(90.0f)}}),
+ 0);
lr.vote = LayerVoteType::Min;
EXPECT_EQ(mExpected90Config,
@@ -445,28 +412,30 @@
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 90.0f;
+ lr.desiredRefreshRate = Fps(90.0f);
lr.vote = LayerVoteType::Heuristic;
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 60.0f;
+ lr.desiredRefreshRate = Fps(60.0f);
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 45.0f;
+ lr.desiredRefreshRate = Fps(45.0f);
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 30.0f;
+ lr.desiredRefreshRate = Fps(30.0f);
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 24.0f;
+ lr.desiredRefreshRate = Fps(24.0f);
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {0, 120}}), 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
+ {HWC_CONFIG_ID_60, {Fps(0.0f), Fps(120.0f)}}),
+ 0);
lr.vote = LayerVoteType::Min;
EXPECT_EQ(mExpected60Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
@@ -475,24 +444,24 @@
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 90.0f;
+ lr.desiredRefreshRate = Fps(90.0f);
lr.vote = LayerVoteType::Heuristic;
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 60.0f;
+ lr.desiredRefreshRate = Fps(60.0f);
EXPECT_EQ(mExpected60Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 45.0f;
+ lr.desiredRefreshRate = Fps(45.0f);
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 30.0f;
+ lr.desiredRefreshRate = Fps(30.0f);
EXPECT_EQ(mExpected60Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 24.0f;
+ lr.desiredRefreshRate = Fps(24.0f);
EXPECT_EQ(mExpected60Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
@@ -513,24 +482,24 @@
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 90.0f;
+ lr.desiredRefreshRate = Fps(90.0f);
lr.vote = LayerVoteType::Heuristic;
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 60.0f;
+ lr.desiredRefreshRate = Fps(60.0f);
EXPECT_EQ(mExpected60Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 45.0f;
+ lr.desiredRefreshRate = Fps(45.0f);
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 30.0f;
+ lr.desiredRefreshRate = Fps(30.0f);
EXPECT_EQ(mExpected60Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 24.0f;
+ lr.desiredRefreshRate = Fps(24.0f);
EXPECT_EQ(mExpected72Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
@@ -545,23 +514,23 @@
auto& lr1 = layers[0];
auto& lr2 = layers[1];
- lr1.desiredRefreshRate = 24.0f;
+ lr1.desiredRefreshRate = Fps(24.0f);
lr1.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = 60.0f;
+ lr2.desiredRefreshRate = Fps(60.0f);
lr2.vote = LayerVoteType::Heuristic;
EXPECT_EQ(mExpected120Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr1.desiredRefreshRate = 24.0f;
+ lr1.desiredRefreshRate = Fps(24.0f);
lr1.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = 48.0f;
+ lr2.desiredRefreshRate = Fps(48.0f);
lr2.vote = LayerVoteType::Heuristic;
EXPECT_EQ(mExpected72Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr1.desiredRefreshRate = 24.0f;
+ lr1.desiredRefreshRate = Fps(24.0f);
lr1.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = 48.0f;
+ lr2.desiredRefreshRate = Fps(48.0f);
lr2.vote = LayerVoteType::Heuristic;
EXPECT_EQ(mExpected72Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
@@ -577,82 +546,82 @@
auto& lr1 = layers[0];
auto& lr2 = layers[1];
- lr1.desiredRefreshRate = 24.0f;
+ lr1.desiredRefreshRate = Fps(24.0f);
lr1.vote = LayerVoteType::ExplicitDefault;
lr1.name = "24Hz ExplicitDefault";
- lr2.desiredRefreshRate = 60.0f;
+ lr2.desiredRefreshRate = Fps(60.0f);
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "60Hz Heuristic";
EXPECT_EQ(mExpected120Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr1.desiredRefreshRate = 24.0f;
+ lr1.desiredRefreshRate = Fps(24.0f);
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.name = "24Hz ExplicitExactOrMultiple";
- lr2.desiredRefreshRate = 60.0f;
+ lr2.desiredRefreshRate = Fps(60.0f);
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "60Hz Heuristic";
EXPECT_EQ(mExpected120Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr1.desiredRefreshRate = 24.0f;
+ lr1.desiredRefreshRate = Fps(24.0f);
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.name = "24Hz ExplicitExactOrMultiple";
- lr2.desiredRefreshRate = 60.0f;
+ lr2.desiredRefreshRate = Fps(60.0f);
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "60Hz ExplicitDefault";
EXPECT_EQ(mExpected120Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr1.desiredRefreshRate = 24.0f;
+ lr1.desiredRefreshRate = Fps(24.0f);
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.name = "24Hz ExplicitExactOrMultiple";
- lr2.desiredRefreshRate = 90.0f;
+ lr2.desiredRefreshRate = Fps(90.0f);
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "90Hz Heuristic";
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr1.desiredRefreshRate = 24.0f;
+ lr1.desiredRefreshRate = Fps(24.0f);
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.name = "24Hz ExplicitExactOrMultiple";
- lr2.desiredRefreshRate = 90.0f;
+ lr2.desiredRefreshRate = Fps(90.0f);
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz Heuristic";
EXPECT_EQ(mExpected72Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr1.desiredRefreshRate = 24.0f;
+ lr1.desiredRefreshRate = Fps(24.0f);
lr1.vote = LayerVoteType::ExplicitDefault;
lr1.name = "24Hz ExplicitDefault";
- lr2.desiredRefreshRate = 90.0f;
+ lr2.desiredRefreshRate = Fps(90.0f);
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "90Hz Heuristic";
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr1.desiredRefreshRate = 24.0f;
+ lr1.desiredRefreshRate = Fps(24.0f);
lr1.vote = LayerVoteType::Heuristic;
lr1.name = "24Hz Heuristic";
- lr2.desiredRefreshRate = 90.0f;
+ lr2.desiredRefreshRate = Fps(90.0f);
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz ExplicitDefault";
EXPECT_EQ(mExpected72Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr1.desiredRefreshRate = 24.0f;
+ lr1.desiredRefreshRate = Fps(24.0f);
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.name = "24Hz ExplicitExactOrMultiple";
- lr2.desiredRefreshRate = 90.0f;
+ lr2.desiredRefreshRate = Fps(90.0f);
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz ExplicitDefault";
EXPECT_EQ(mExpected72Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr1.desiredRefreshRate = 24.0f;
+ lr1.desiredRefreshRate = Fps(24.0f);
lr1.vote = LayerVoteType::ExplicitDefault;
lr1.name = "24Hz ExplicitDefault";
- lr2.desiredRefreshRate = 90.0f;
+ lr2.desiredRefreshRate = Fps(90.0f);
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.name = "90Hz ExplicitExactOrMultiple";
EXPECT_EQ(mExpected90Config,
@@ -675,24 +644,24 @@
EXPECT_EQ(mExpected60Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 90.0f;
+ lr.desiredRefreshRate = Fps(90.0f);
lr.vote = LayerVoteType::Heuristic;
EXPECT_EQ(mExpected60Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 60.0f;
+ lr.desiredRefreshRate = Fps(60.0f);
EXPECT_EQ(mExpected60Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 45.0f;
+ lr.desiredRefreshRate = Fps(45.0f);
EXPECT_EQ(mExpected60Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 30.0f;
+ lr.desiredRefreshRate = Fps(30.0f);
EXPECT_EQ(mExpected30Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 24.0f;
+ lr.desiredRefreshRate = Fps(24.0f);
EXPECT_EQ(mExpected60Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
@@ -715,41 +684,41 @@
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 90.0f;
+ lr.desiredRefreshRate = Fps(90.0f);
lr.vote = LayerVoteType::Heuristic;
lr.name = "90Hz Heuristic";
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- lr.desiredRefreshRate = 60.0f;
+ lr.desiredRefreshRate = Fps(60.0f);
lr.name = "60Hz Heuristic";
EXPECT_EQ(mExpected60Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
- lr.desiredRefreshRate = 45.0f;
+ lr.desiredRefreshRate = Fps(45.0f);
lr.name = "45Hz Heuristic";
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
- lr.desiredRefreshRate = 30.0f;
+ lr.desiredRefreshRate = Fps(30.0f);
lr.name = "30Hz Heuristic";
EXPECT_EQ(mExpected30Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
- lr.desiredRefreshRate = 24.0f;
+ lr.desiredRefreshRate = Fps(24.0f);
lr.name = "24Hz Heuristic";
EXPECT_EQ(mExpected72Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
- lr.desiredRefreshRate = 24.0f;
+ lr.desiredRefreshRate = Fps(24.0f);
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
lr.name = "24Hz ExplicitExactOrMultiple";
EXPECT_EQ(mExpected72Config,
@@ -775,39 +744,39 @@
lr1.vote = LayerVoteType::Min;
lr2.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = 24.0f;
+ lr2.desiredRefreshRate = Fps(24.0f);
EXPECT_EQ(mExpected60Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::Min;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr2.desiredRefreshRate = 24.0f;
+ lr2.desiredRefreshRate = Fps(24.0f);
EXPECT_EQ(mExpected60Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::Max;
lr2.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = 60.0f;
+ lr2.desiredRefreshRate = Fps(60.0f);
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::Max;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr2.desiredRefreshRate = 60.0f;
+ lr2.desiredRefreshRate = Fps(60.0f);
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::Heuristic;
- lr1.desiredRefreshRate = 15.0f;
+ lr1.desiredRefreshRate = Fps(15.0f);
lr2.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = 45.0f;
+ lr2.desiredRefreshRate = Fps(45.0f);
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::Heuristic;
- lr1.desiredRefreshRate = 30.0f;
+ lr1.desiredRefreshRate = Fps(30.0f);
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr2.desiredRefreshRate = 45.0f;
+ lr2.desiredRefreshRate = Fps(45.0f);
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
@@ -822,36 +791,13 @@
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
- lr.desiredRefreshRate = fps;
+ lr.desiredRefreshRate = Fps(fps);
const auto& refreshRate =
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
EXPECT_EQ(mExpected60Config, refreshRate) << fps << "Hz chooses " << refreshRate.getName();
}
}
-TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContent_Explicit) {
- auto refreshRateConfigs =
- std::make_unique<RefreshRateConfigs>(m60_90Device,
- /*currentConfigId=*/HWC_CONFIG_ID_60);
-
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
- LayerRequirement{.weight = 1.0f}};
- auto& lr1 = layers[0];
- auto& lr2 = layers[1];
-
- lr1.vote = LayerVoteType::Heuristic;
- lr1.desiredRefreshRate = 60.0f;
- lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr2.desiredRefreshRate = 90.0f;
- EXPECT_EQ(mExpected90Config, refreshRateConfigs->getRefreshRateForContent(layers));
-
- lr1.vote = LayerVoteType::Heuristic;
- lr1.desiredRefreshRate = 90.0f;
- lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr2.desiredRefreshRate = 60.0f;
- EXPECT_EQ(mExpected60Config, refreshRateConfigs->getRefreshRateForContent(layers));
-}
-
TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getBestRefreshRate_Explicit) {
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_90Device,
@@ -863,33 +809,33 @@
auto& lr2 = layers[1];
lr1.vote = LayerVoteType::Heuristic;
- lr1.desiredRefreshRate = 60.0f;
+ lr1.desiredRefreshRate = Fps(60.0f);
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr2.desiredRefreshRate = 90.0f;
+ lr2.desiredRefreshRate = Fps(90.0f);
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::ExplicitDefault;
- lr1.desiredRefreshRate = 90.0f;
+ lr1.desiredRefreshRate = Fps(90.0f);
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr2.desiredRefreshRate = 60.0f;
+ lr2.desiredRefreshRate = Fps(60.0f);
EXPECT_EQ(mExpected60Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::Heuristic;
- lr1.desiredRefreshRate = 90.0f;
+ lr1.desiredRefreshRate = Fps(90.0f);
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr2.desiredRefreshRate = 60.0f;
+ lr2.desiredRefreshRate = Fps(60.0f);
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
TEST_F(RefreshRateConfigsTest, testInPolicy) {
- ASSERT_TRUE(mExpectedAlmost60Config.inPolicy(60.000004f, 60.000004f));
- ASSERT_TRUE(mExpectedAlmost60Config.inPolicy(59.0f, 60.1f));
- ASSERT_FALSE(mExpectedAlmost60Config.inPolicy(75.0f, 90.0f));
- ASSERT_FALSE(mExpectedAlmost60Config.inPolicy(60.0011f, 90.0f));
- ASSERT_FALSE(mExpectedAlmost60Config.inPolicy(50.0f, 59.998f));
+ ASSERT_TRUE(mExpectedAlmost60Config.inPolicy(Fps(60.000004f), Fps(60.000004f)));
+ ASSERT_TRUE(mExpectedAlmost60Config.inPolicy(Fps(59.0f), Fps(60.1f)));
+ ASSERT_FALSE(mExpectedAlmost60Config.inPolicy(Fps(75.0f), Fps(90.0f)));
+ ASSERT_FALSE(mExpectedAlmost60Config.inPolicy(Fps(60.0011f), Fps(90.0f)));
+ ASSERT_FALSE(mExpectedAlmost60Config.inPolicy(Fps(50.0f), Fps(59.998f)));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_75HzContent) {
@@ -902,7 +848,7 @@
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) {
- lr.desiredRefreshRate = fps;
+ lr.desiredRefreshRate = Fps(fps);
const auto& refreshRate =
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
EXPECT_EQ(mExpected90Config, refreshRate) << fps << "Hz chooses " << refreshRate.getName();
@@ -920,25 +866,25 @@
auto& lr2 = layers[1];
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = 60.0f;
+ lr1.desiredRefreshRate = Fps(60.0f);
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = 90.0f;
+ lr2.desiredRefreshRate = Fps(90.0f);
lr2.name = "90Hz Heuristic";
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = 60.0f;
+ lr1.desiredRefreshRate = Fps(60.0f);
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::ExplicitDefault;
- lr2.desiredRefreshRate = 90.0f;
+ lr2.desiredRefreshRate = Fps(90.0f);
lr2.name = "90Hz ExplicitDefault";
EXPECT_EQ(mExpected60Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = 60.0f;
+ lr1.desiredRefreshRate = Fps(60.0f);
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
@@ -946,16 +892,16 @@
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = 30.0f;
+ lr1.desiredRefreshRate = Fps(30.0f);
lr1.name = "30Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = 90.0f;
+ lr2.desiredRefreshRate = Fps(90.0f);
lr2.name = "90Hz Heuristic";
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = 30.0f;
+ lr1.desiredRefreshRate = Fps(30.0f);
lr1.name = "30Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
@@ -974,7 +920,7 @@
auto& lr2 = layers[1];
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = 60.0f;
+ lr1.desiredRefreshRate = Fps(60.0f);
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::NoVote;
lr2.name = "NoVote";
@@ -982,7 +928,7 @@
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = 60.0f;
+ lr1.desiredRefreshRate = Fps(60.0f);
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::NoVote;
lr2.name = "NoVote";
@@ -990,7 +936,7 @@
refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = 60.0f;
+ lr1.desiredRefreshRate = Fps(60.0f);
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
@@ -998,7 +944,7 @@
refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = 60.0f;
+ lr1.desiredRefreshRate = Fps(60.0f);
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
@@ -1007,10 +953,10 @@
// The other layer starts to provide buffers
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = 60.0f;
+ lr1.desiredRefreshRate = Fps(60.0f);
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = 90.0f;
+ lr2.desiredRefreshRate = Fps(90.0f);
lr2.name = "90Hz Heuristic";
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
@@ -1034,40 +980,40 @@
auto& lr2 = layers[1];
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = 60.0f;
+ lr1.desiredRefreshRate = Fps(60.0f);
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = 60.0f;
+ lr2.desiredRefreshRate = Fps(60.0f);
lr2.name = "60Hz Heuristic";
refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false},
&consideredSignals);
EXPECT_EQ(true, consideredSignals.touch);
lr1.vote = LayerVoteType::ExplicitDefault;
- lr1.desiredRefreshRate = 60.0f;
+ lr1.desiredRefreshRate = Fps(60.0f);
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = 60.0f;
+ lr2.desiredRefreshRate = Fps(60.0f);
lr2.name = "60Hz Heuristic";
refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false},
&consideredSignals);
EXPECT_EQ(false, consideredSignals.touch);
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = 60.0f;
+ lr1.desiredRefreshRate = Fps(60.0f);
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = 60.0f;
+ lr2.desiredRefreshRate = Fps(60.0f);
lr2.name = "60Hz Heuristic";
refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false},
&consideredSignals);
EXPECT_EQ(true, consideredSignals.touch);
lr1.vote = LayerVoteType::ExplicitDefault;
- lr1.desiredRefreshRate = 60.0f;
+ lr1.desiredRefreshRate = Fps(60.0f);
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = 60.0f;
+ lr2.desiredRefreshRate = Fps(60.0f);
lr2.name = "60Hz Heuristic";
refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false},
&consideredSignals);
@@ -1103,7 +1049,7 @@
for (const auto& test : testCases) {
lr.vote = LayerVoteType::ExplicitDefault;
- lr.desiredRefreshRate = test.first;
+ lr.desiredRefreshRate = Fps(test.first);
std::stringstream ss;
ss << "ExplicitDefault " << test.first << " fps";
@@ -1111,7 +1057,7 @@
const auto& refreshRate =
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
- EXPECT_FLOAT_EQ(refreshRate.getFps(), test.second)
+ EXPECT_TRUE(refreshRate.getFps().equalsWithMargin(Fps(test.second)))
<< "Expecting " << test.first << "fps => " << test.second << "Hz";
}
}
@@ -1123,7 +1069,7 @@
/*currentConfigId=*/HWC_CONFIG_ID_90);
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
- {HWC_CONFIG_ID_90, {90.f, 90.f}, {60.f, 90.f}}),
+ {HWC_CONFIG_ID_90, {Fps(90.f), Fps(90.f)}, {Fps(60.f), Fps(90.f)}}),
0);
auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
@@ -1131,7 +1077,7 @@
RefreshRateConfigs::GlobalSignals consideredSignals;
lr.vote = LayerVoteType::ExplicitDefault;
- lr.desiredRefreshRate = 60.0f;
+ lr.desiredRefreshRate = Fps(60.0f);
lr.name = "60Hz ExplicitDefault";
lr.focused = true;
EXPECT_EQ(mExpected60Config,
@@ -1147,14 +1093,14 @@
/*currentConfigId=*/HWC_CONFIG_ID_60);
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
- {HWC_CONFIG_ID_60, {60.f, 60.f}, {60.f, 90.f}}),
+ {HWC_CONFIG_ID_60, {Fps(60.f), Fps(60.f)}, {Fps(60.f), Fps(90.f)}}),
0);
auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
auto& lr = layers[0];
lr.vote = LayerVoteType::ExplicitDefault;
- lr.desiredRefreshRate = 90.0f;
+ lr.desiredRefreshRate = Fps(90.0f);
lr.name = "90Hz ExplicitDefault";
lr.focused = true;
EXPECT_EQ(mExpected90Config,
@@ -1168,7 +1114,7 @@
/*currentConfigId=*/HWC_CONFIG_ID_90);
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
- {HWC_CONFIG_ID_90, {90.f, 90.f}, {60.f, 90.f}}),
+ {HWC_CONFIG_ID_90, {Fps(90.f), Fps(90.f)}, {Fps(60.f), Fps(90.f)}}),
0);
RefreshRateConfigs::GlobalSignals consideredSignals;
@@ -1181,7 +1127,7 @@
auto& lr = layers[0];
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr.desiredRefreshRate = 60.0f;
+ lr.desiredRefreshRate = Fps(60.0f);
lr.name = "60Hz ExplicitExactOrMultiple";
lr.focused = false;
EXPECT_EQ(mExpected90Config,
@@ -1192,7 +1138,7 @@
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.vote = LayerVoteType::ExplicitDefault;
- lr.desiredRefreshRate = 60.0f;
+ lr.desiredRefreshRate = Fps(60.0f);
lr.name = "60Hz ExplicitDefault";
lr.focused = false;
EXPECT_EQ(mExpected90Config,
@@ -1203,7 +1149,7 @@
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.vote = LayerVoteType::Heuristic;
- lr.desiredRefreshRate = 60.0f;
+ lr.desiredRefreshRate = Fps(60.0f);
lr.name = "60Hz Heuristic";
lr.focused = false;
EXPECT_EQ(mExpected90Config,
@@ -1214,7 +1160,7 @@
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.vote = LayerVoteType::Max;
- lr.desiredRefreshRate = 60.0f;
+ lr.desiredRefreshRate = Fps(60.0f);
lr.name = "60Hz Max";
lr.focused = false;
EXPECT_EQ(mExpected90Config,
@@ -1225,7 +1171,7 @@
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.vote = LayerVoteType::Min;
- lr.desiredRefreshRate = 60.0f;
+ lr.desiredRefreshRate = Fps(60.0f);
lr.name = "60Hz Min";
lr.focused = false;
EXPECT_EQ(mExpected90Config,
@@ -1236,28 +1182,286 @@
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
-TEST_F(RefreshRateConfigsTest, groupSwitching) {
+TEST_F(RefreshRateConfigsTest, groupSwitchingNotAllowed) {
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
/*currentConfigId=*/HWC_CONFIG_ID_60);
+ // The default policy doesn't allow group switching. Verify that no
+ // group switches are performed.
auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
auto& layer = layers[0];
layer.vote = LayerVoteType::ExplicitDefault;
- layer.desiredRefreshRate = 90.0f;
+ layer.desiredRefreshRate = Fps(90.0f);
+ layer.seamlessness = Seamlessness::SeamedAndSeamless;
layer.name = "90Hz ExplicitDefault";
+ layer.focused = true;
ASSERT_EQ(HWC_CONFIG_ID_60,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
- .getConfigId());
+ .getModeId());
+}
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayer) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
RefreshRateConfigs::Policy policy;
- policy.defaultConfig = refreshRateConfigs->getCurrentPolicy().defaultConfig;
+ policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
policy.allowGroupSwitching = true;
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& layer = layers[0];
+ layer.vote = LayerVoteType::ExplicitDefault;
+ layer.desiredRefreshRate = Fps(90.0f);
+ layer.seamlessness = Seamlessness::SeamedAndSeamless;
+ layer.name = "90Hz ExplicitDefault";
+ layer.focused = true;
ASSERT_EQ(HWC_CONFIG_ID_90,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
- .getConfigId());
+ .getModeId());
+}
+
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamless) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+ RefreshRateConfigs::Policy policy;
+ policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+ // Verify that we won't change the group if seamless switch is required.
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& layer = layers[0];
+ layer.vote = LayerVoteType::ExplicitDefault;
+ layer.desiredRefreshRate = Fps(90.0f);
+ layer.seamlessness = Seamlessness::OnlySeamless;
+ layer.name = "90Hz ExplicitDefault";
+ layer.focused = true;
+ ASSERT_EQ(HWC_CONFIG_ID_60,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getModeId());
+}
+
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+ RefreshRateConfigs::Policy policy;
+ policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+ refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+
+ // Verify that we won't do a seamless switch if we request the same mode as the default
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& layer = layers[0];
+ layer.vote = LayerVoteType::ExplicitDefault;
+ layer.desiredRefreshRate = Fps(60.0f);
+ layer.seamlessness = Seamlessness::OnlySeamless;
+ layer.name = "60Hz ExplicitDefault";
+ layer.focused = true;
+ ASSERT_EQ(HWC_CONFIG_ID_90,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getModeId());
+}
+
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerDefaultSeamlessness) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+ RefreshRateConfigs::Policy policy;
+ policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+ refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+
+ // Verify that if the current config is in another group and there are no layers with
+ // seamlessness=SeamedAndSeamless we'll go back to the default group.
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& layer = layers[0];
+ layer.vote = LayerVoteType::ExplicitDefault;
+ layer.desiredRefreshRate = Fps(60.0f);
+ layer.seamlessness = Seamlessness::Default;
+ layer.name = "60Hz ExplicitDefault";
+ layer.focused = true;
+
+ ASSERT_EQ(HWC_CONFIG_ID_60,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getModeId());
+}
+
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+ RefreshRateConfigs::Policy policy;
+ policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+ refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+
+ // If there's a layer with seamlessness=SeamedAndSeamless, another layer with
+ // seamlessness=OnlySeamless can't change the mode group.
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ layers[0].vote = LayerVoteType::ExplicitDefault;
+ layers[0].desiredRefreshRate = Fps(60.0f);
+ layers[0].seamlessness = Seamlessness::OnlySeamless;
+ layers[0].name = "60Hz ExplicitDefault";
+ layers[0].focused = true;
+
+ layers.push_back(LayerRequirement{.weight = 0.5f});
+ layers[1].vote = LayerVoteType::ExplicitDefault;
+ layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
+ layers[1].desiredRefreshRate = Fps(90.0f);
+ layers[1].name = "90Hz ExplicitDefault";
+ layers[1].focused = false;
+
+ ASSERT_EQ(HWC_CONFIG_ID_90,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getModeId());
+}
+
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+ RefreshRateConfigs::Policy policy;
+ policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+ refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+
+ // If there's a focused layer with seamlessness=SeamedAndSeamless, another layer with
+ // seamlessness=Default can't change the mode group back to the group of the default
+ // mode.
+ // For example, this may happen when a video playback requests and gets a seamed switch,
+ // but another layer (with default seamlessness) starts animating. The animating layer
+ // should not cause a seamed switch.
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ layers[0].seamlessness = Seamlessness::Default;
+ layers[0].desiredRefreshRate = Fps(60.0f);
+ layers[0].focused = true;
+ layers[0].vote = LayerVoteType::ExplicitDefault;
+ layers[0].name = "60Hz ExplicitDefault";
+
+ layers.push_back(LayerRequirement{.weight = 0.1f});
+ layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
+ layers[1].desiredRefreshRate = Fps(90.0f);
+ layers[1].focused = true;
+ layers[1].vote = LayerVoteType::ExplicitDefault;
+ layers[1].name = "90Hz ExplicitDefault";
+
+ ASSERT_EQ(HWC_CONFIG_ID_90,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getModeId());
+}
+
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+ RefreshRateConfigs::Policy policy;
+ policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+ refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+
+ // Layer with seamlessness=Default can change the mode group if there's a not
+ // focused layer with seamlessness=SeamedAndSeamless. This happens for example,
+ // when in split screen mode the user switches between the two visible applications.
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ layers[0].seamlessness = Seamlessness::Default;
+ layers[0].desiredRefreshRate = Fps(60.0f);
+ layers[0].focused = true;
+ layers[0].vote = LayerVoteType::ExplicitDefault;
+ layers[0].name = "60Hz ExplicitDefault";
+
+ layers.push_back(LayerRequirement{.weight = 0.7f});
+ layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
+ layers[1].desiredRefreshRate = Fps(90.0f);
+ layers[1].focused = false;
+ layers[1].vote = LayerVoteType::ExplicitDefault;
+ layers[1].name = "90Hz ExplicitDefault";
+
+ ASSERT_EQ(HWC_CONFIG_ID_60,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getModeId());
+}
+
+TEST_F(RefreshRateConfigsTest, nonSeamlessVotePrefersSeamlessSwitches) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m30_60Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ // Allow group switching.
+ RefreshRateConfigs::Policy policy;
+ policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& layer = layers[0];
+ layer.vote = LayerVoteType::ExplicitExactOrMultiple;
+ layer.desiredRefreshRate = Fps(60.0f);
+ layer.seamlessness = Seamlessness::SeamedAndSeamless;
+ layer.name = "60Hz ExplicitExactOrMultiple";
+ layer.focused = true;
+
+ ASSERT_EQ(HWC_CONFIG_ID_60,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getModeId());
+
+ refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_120);
+ ASSERT_EQ(HWC_CONFIG_ID_120,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getModeId());
+}
+
+TEST_F(RefreshRateConfigsTest, nonSeamlessExactAndSeamlessMultipleLayers) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m25_30_50_60Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ // Allow group switching.
+ RefreshRateConfigs::Policy policy;
+ policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+ auto layers = std::vector<
+ LayerRequirement>{LayerRequirement{.name = "60Hz ExplicitDefault",
+ .vote = LayerVoteType::ExplicitDefault,
+ .desiredRefreshRate = Fps(60.0f),
+ .seamlessness = Seamlessness::SeamedAndSeamless,
+ .weight = 0.5f,
+ .focused = false},
+ LayerRequirement{.name = "25Hz ExplicitExactOrMultiple",
+ .vote = LayerVoteType::ExplicitExactOrMultiple,
+ .desiredRefreshRate = Fps(25.0f),
+ .seamlessness = Seamlessness::OnlySeamless,
+ .weight = 1.0f,
+ .focused = true}};
+ auto& seamedLayer = layers[0];
+
+ ASSERT_EQ(HWC_CONFIG_ID_50,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getModeId());
+
+ seamedLayer.name = "30Hz ExplicitDefault", seamedLayer.desiredRefreshRate = Fps(30.0f);
+ refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_30);
+
+ ASSERT_EQ(HWC_CONFIG_ID_25,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getModeId());
}
TEST_F(RefreshRateConfigsTest, primaryVsAppRequestPolicy) {
@@ -1270,53 +1474,54 @@
// Return the config ID from calling getBestRefreshRate() for a single layer with the
// given voteType and fps.
- auto getFrameRate = [&](LayerVoteType voteType, float fps, bool touchActive = false,
- bool focused = true) -> HwcConfigIndexType {
+ auto getFrameRate = [&](LayerVoteType voteType, Fps fps, bool touchActive = false,
+ bool focused = true) -> DisplayModeId {
layers[0].vote = voteType;
layers[0].desiredRefreshRate = fps;
layers[0].focused = focused;
return refreshRateConfigs->getBestRefreshRate(layers, {.touch = touchActive, .idle = false})
- .getConfigId();
+ .getModeId();
};
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
- {HWC_CONFIG_ID_60, {30.f, 60.f}, {30.f, 90.f}}),
+ {HWC_CONFIG_ID_60, {Fps(30.f), Fps(60.f)}, {Fps(30.f), Fps(90.f)}}),
0);
EXPECT_EQ(HWC_CONFIG_ID_60,
refreshRateConfigs->getBestRefreshRate({}, {.touch = false, .idle = false})
- .getConfigId());
- EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::NoVote, 90.f));
- EXPECT_EQ(HWC_CONFIG_ID_30, getFrameRate(LayerVoteType::Min, 90.f));
- EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, 90.f));
- EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Heuristic, 90.f));
- EXPECT_EQ(HWC_CONFIG_ID_90, getFrameRate(LayerVoteType::ExplicitDefault, 90.f));
- EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90.f));
+ .getModeId());
+ EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::NoVote, Fps(90.f)));
+ EXPECT_EQ(HWC_CONFIG_ID_30, getFrameRate(LayerVoteType::Min, Fps(90.f)));
+ EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, Fps(90.f)));
+ EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Heuristic, Fps(90.f)));
+ EXPECT_EQ(HWC_CONFIG_ID_90, getFrameRate(LayerVoteType::ExplicitDefault, Fps(90.f)));
+ EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, Fps(90.f)));
// Layers not focused are not allowed to override primary config
EXPECT_EQ(HWC_CONFIG_ID_60,
- getFrameRate(LayerVoteType::ExplicitDefault, 90.f, /*touch=*/false,
+ getFrameRate(LayerVoteType::ExplicitDefault, Fps(90.f), /*touch=*/false,
/*focused=*/false));
EXPECT_EQ(HWC_CONFIG_ID_60,
- getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90.f, /*touch=*/false,
+ getFrameRate(LayerVoteType::ExplicitExactOrMultiple, Fps(90.f), /*touch=*/false,
/*focused=*/false));
// Touch boost should be restricted to the primary range.
- EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, 90.f, /*touch=*/true));
+ EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, Fps(90.f), /*touch=*/true));
// When we're higher than the primary range max due to a layer frame rate setting, touch boost
// shouldn't drag us back down to the primary range max.
- EXPECT_EQ(HWC_CONFIG_ID_90, getFrameRate(LayerVoteType::ExplicitDefault, 90.f, /*touch=*/true));
+ EXPECT_EQ(HWC_CONFIG_ID_90,
+ getFrameRate(LayerVoteType::ExplicitDefault, Fps(90.f), /*touch=*/true));
EXPECT_EQ(HWC_CONFIG_ID_60,
- getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90.f, /*touch=*/true));
+ getFrameRate(LayerVoteType::ExplicitExactOrMultiple, Fps(90.f), /*touch=*/true));
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
- {HWC_CONFIG_ID_60, {60.f, 60.f}, {60.f, 60.f}}),
+ {HWC_CONFIG_ID_60, {Fps(60.f), Fps(60.f)}, {Fps(60.f), Fps(60.f)}}),
0);
- EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::NoVote, 90.f));
- EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Min, 90.f));
- EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, 90.f));
- EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Heuristic, 90.f));
- EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::ExplicitDefault, 90.f));
- EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90.f));
+ EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::NoVote, Fps(90.f)));
+ EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Min, Fps(90.f)));
+ EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, Fps(90.f)));
+ EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Heuristic, Fps(90.f)));
+ EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::ExplicitDefault, Fps(90.f)));
+ EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, Fps(90.f)));
}
TEST_F(RefreshRateConfigsTest, idle) {
@@ -1327,23 +1532,22 @@
auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
layers[0].name = "Test layer";
- const auto getIdleFrameRate = [&](LayerVoteType voteType,
- bool touchActive) -> HwcConfigIndexType {
+ const auto getIdleFrameRate = [&](LayerVoteType voteType, bool touchActive) -> DisplayModeId {
layers[0].vote = voteType;
- layers[0].desiredRefreshRate = 90.f;
+ layers[0].desiredRefreshRate = Fps(90.f);
RefreshRateConfigs::GlobalSignals consideredSignals;
const auto configId =
refreshRateConfigs
->getBestRefreshRate(layers, {.touch = touchActive, .idle = true},
&consideredSignals)
- .getConfigId();
+ .getModeId();
// Refresh rate will be chosen by either touch state or idle state
EXPECT_EQ(!touchActive, consideredSignals.idle);
return configId;
};
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
- {HWC_CONFIG_ID_60, {60.f, 90.f}, {60.f, 90.f}}),
+ {HWC_CONFIG_ID_60, {Fps(60.f), Fps(90.f)}, {Fps(60.f), Fps(90.f)}}),
0);
// Idle should be lower priority than touch boost.
@@ -1359,10 +1563,10 @@
// With no layers, idle should still be lower priority than touch boost.
EXPECT_EQ(HWC_CONFIG_ID_90,
refreshRateConfigs->getBestRefreshRate({}, {.touch = true, .idle = true})
- .getConfigId());
+ .getModeId());
// Idle should be higher precedence than other layer frame rate considerations.
- refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+ refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::NoVote, /*touchActive=*/false));
EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Min, /*touchActive=*/false));
EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Max, /*touchActive=*/false));
@@ -1375,7 +1579,7 @@
// Idle should be applied rather than the current config when there are no layers.
EXPECT_EQ(HWC_CONFIG_ID_60,
refreshRateConfigs->getBestRefreshRate({}, {.touch = false, .idle = true})
- .getConfigId());
+ .getModeId());
}
TEST_F(RefreshRateConfigsTest, findClosestKnownFrameRate) {
@@ -1384,22 +1588,22 @@
/*currentConfigId=*/HWC_CONFIG_ID_60);
for (float fps = 1.0f; fps <= 120.0f; fps += 0.1f) {
- const auto knownFrameRate = findClosestKnownFrameRate(*refreshRateConfigs, fps);
- float expectedFrameRate;
+ const auto knownFrameRate = findClosestKnownFrameRate(*refreshRateConfigs, Fps(fps));
+ Fps expectedFrameRate;
if (fps < 26.91f) {
- expectedFrameRate = 24.0f;
+ expectedFrameRate = Fps(24.0f);
} else if (fps < 37.51f) {
- expectedFrameRate = 30.0f;
+ expectedFrameRate = Fps(30.0f);
} else if (fps < 52.51f) {
- expectedFrameRate = 45.0f;
+ expectedFrameRate = Fps(45.0f);
} else if (fps < 66.01f) {
- expectedFrameRate = 60.0f;
+ expectedFrameRate = Fps(60.0f);
} else if (fps < 81.01f) {
- expectedFrameRate = 72.0f;
+ expectedFrameRate = Fps(72.0f);
} else {
- expectedFrameRate = 90.0f;
+ expectedFrameRate = Fps(90.0f);
}
- EXPECT_FLOAT_EQ(expectedFrameRate, knownFrameRate)
+ EXPECT_TRUE(expectedFrameRate.equalsWithMargin(knownFrameRate))
<< "findClosestKnownFrameRate(" << fps << ") = " << knownFrameRate;
}
}
@@ -1410,26 +1614,27 @@
/*currentConfigId=*/HWC_CONFIG_ID_60);
struct ExpectedRate {
- float rate;
+ Fps rate;
const RefreshRate& expected;
};
/* clang-format off */
std::vector<ExpectedRate> knownFrameRatesExpectations = {
- {24.0f, mExpected60Config},
- {30.0f, mExpected60Config},
- {45.0f, mExpected90Config},
- {60.0f, mExpected60Config},
- {72.0f, mExpected90Config},
- {90.0f, mExpected90Config},
+ {Fps(24.0f), mExpected60Config},
+ {Fps(30.0f), mExpected60Config},
+ {Fps(45.0f), mExpected90Config},
+ {Fps(60.0f), mExpected60Config},
+ {Fps(72.0f), mExpected90Config},
+ {Fps(90.0f), mExpected90Config},
};
/* clang-format on */
// Make sure the test tests all the known frame rate
const auto knownFrameRateList = getKnownFrameRate(*refreshRateConfigs);
- const auto equal = std::equal(knownFrameRateList.begin(), knownFrameRateList.end(),
- knownFrameRatesExpectations.begin(),
- [](float a, const ExpectedRate& b) { return a == b.rate; });
+ const auto equal =
+ std::equal(knownFrameRateList.begin(), knownFrameRateList.end(),
+ knownFrameRatesExpectations.begin(),
+ [](Fps a, const ExpectedRate& b) { return a.equalsWithMargin(b.rate); });
EXPECT_TRUE(equal);
auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
@@ -1443,6 +1648,89 @@
}
}
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExact) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+ LayerRequirement{.weight = 0.5f}};
+ auto& explicitExactLayer = layers[0];
+ auto& explicitExactOrMultipleLayer = layers[1];
+
+ explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
+ explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
+ explicitExactOrMultipleLayer.desiredRefreshRate = Fps(60);
+
+ explicitExactLayer.vote = LayerVoteType::ExplicitExact;
+ explicitExactLayer.name = "ExplicitExact";
+ explicitExactLayer.desiredRefreshRate = Fps(30);
+
+ EXPECT_EQ(mExpected30Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected30Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+
+ explicitExactOrMultipleLayer.desiredRefreshRate = Fps(120);
+ explicitExactLayer.desiredRefreshRate = Fps(60);
+ EXPECT_EQ(mExpected60Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ explicitExactLayer.desiredRefreshRate = Fps(72);
+ EXPECT_EQ(mExpected72Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ explicitExactLayer.desiredRefreshRate = Fps(90);
+ EXPECT_EQ(mExpected90Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ explicitExactLayer.desiredRefreshRate = Fps(120);
+ EXPECT_EQ(mExpected120Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+}
+
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactEnableFrameRateOverride) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_60,
+ /*enableFrameRateOverride=*/true);
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+ LayerRequirement{.weight = 0.5f}};
+ auto& explicitExactLayer = layers[0];
+ auto& explicitExactOrMultipleLayer = layers[1];
+
+ explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
+ explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
+ explicitExactOrMultipleLayer.desiredRefreshRate = Fps(60);
+
+ explicitExactLayer.vote = LayerVoteType::ExplicitExact;
+ explicitExactLayer.name = "ExplicitExact";
+ explicitExactLayer.desiredRefreshRate = Fps(30);
+
+ EXPECT_EQ(mExpected60Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected120Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+
+ explicitExactOrMultipleLayer.desiredRefreshRate = Fps(120);
+ explicitExactLayer.desiredRefreshRate = Fps(60);
+ EXPECT_EQ(mExpected120Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ explicitExactLayer.desiredRefreshRate = Fps(72);
+ EXPECT_EQ(mExpected72Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ explicitExactLayer.desiredRefreshRate = Fps(90);
+ EXPECT_EQ(mExpected90Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ explicitExactLayer.desiredRefreshRate = Fps(120);
+ EXPECT_EQ(mExpected120Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+}
+
TEST_F(RefreshRateConfigsTest, testComparisonOperator) {
EXPECT_TRUE(mExpected60Config < mExpected90Config);
EXPECT_FALSE(mExpected60Config < mExpected60Config);
@@ -1459,18 +1747,221 @@
EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction());
// SetPolicy(60, 90), current 60Hz => TurnOn.
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 90}}), 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(90)}}),
+ 0);
EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction());
// SetPolicy(60, 60), current 60Hz => NoChange, avoid extra calls.
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 60}}), 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(60)}}),
+ 0);
EXPECT_EQ(KernelIdleTimerAction::NoChange, refreshRateConfigs->getIdleTimerAction());
// SetPolicy(90, 90), current 90Hz => TurnOff.
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90, 90}}), 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {Fps(90), Fps(90)}}),
+ 0);
EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
}
+TEST_F(RefreshRateConfigsTest, getFrameRateDivider) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_30);
+
+ const auto frameRate = Fps(30.f);
+ Fps displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
+ EXPECT_EQ(1, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate));
+
+ refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_60);
+ displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
+ EXPECT_EQ(2, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate));
+
+ refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_72);
+ displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
+ EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate));
+
+ refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+ displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
+ EXPECT_EQ(3, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate));
+
+ refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_120);
+ displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
+ EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate));
+
+ refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+ displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
+ EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, Fps(22.5f)));
+ EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, Fps(22.6f)));
+}
+
+TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_noLayers) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
+ HWC_CONFIG_ID_120);
+
+ auto layers = std::vector<LayerRequirement>{};
+ ASSERT_TRUE(refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false)
+ .empty());
+}
+
+TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_60on120) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
+ HWC_CONFIG_ID_120,
+ /*enableFrameRateOverride=*/true);
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ layers[0].name = "Test layer";
+ layers[0].ownerUid = 1234;
+ layers[0].desiredRefreshRate = Fps(60.0f);
+ layers[0].vote = LayerVoteType::ExplicitDefault;
+ auto frameRateOverrides =
+ refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+ ASSERT_EQ(1, frameRateOverrides.size());
+ ASSERT_EQ(1, frameRateOverrides.count(1234));
+ ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ frameRateOverrides =
+ refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+ ASSERT_EQ(1, frameRateOverrides.size());
+ ASSERT_EQ(1, frameRateOverrides.count(1234));
+ ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+
+ layers[0].vote = LayerVoteType::NoVote;
+ frameRateOverrides =
+ refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+ ASSERT_TRUE(frameRateOverrides.empty());
+
+ layers[0].vote = LayerVoteType::Min;
+ frameRateOverrides =
+ refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+ ASSERT_TRUE(frameRateOverrides.empty());
+
+ layers[0].vote = LayerVoteType::Max;
+ frameRateOverrides =
+ refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+ ASSERT_TRUE(frameRateOverrides.empty());
+
+ layers[0].vote = LayerVoteType::Heuristic;
+ frameRateOverrides =
+ refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+ ASSERT_TRUE(frameRateOverrides.empty());
+}
+
+TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_twoUids) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
+ HWC_CONFIG_ID_120,
+ /*enableFrameRateOverride=*/true);
+
+ auto layers = std::vector<LayerRequirement>{
+ LayerRequirement{.ownerUid = 1234, .weight = 1.0f},
+ LayerRequirement{.ownerUid = 5678, .weight = 1.0f},
+ };
+
+ layers[0].name = "Test layer 1234";
+ layers[0].desiredRefreshRate = Fps(60.0f);
+ layers[0].vote = LayerVoteType::ExplicitDefault;
+
+ layers[1].name = "Test layer 5678";
+ layers[1].desiredRefreshRate = Fps(30.0f);
+ layers[1].vote = LayerVoteType::ExplicitDefault;
+ auto frameRateOverrides =
+ refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+
+ ASSERT_EQ(2, frameRateOverrides.size());
+ ASSERT_EQ(1, frameRateOverrides.count(1234));
+ ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+ ASSERT_EQ(1, frameRateOverrides.count(5678));
+ ASSERT_EQ(30.0f, frameRateOverrides.at(5678).getValue());
+
+ layers[1].vote = LayerVoteType::Heuristic;
+ frameRateOverrides =
+ refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+ ASSERT_EQ(1, frameRateOverrides.size());
+ ASSERT_EQ(1, frameRateOverrides.count(1234));
+ ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+
+ layers[1].ownerUid = 1234;
+ frameRateOverrides =
+ refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+ ASSERT_TRUE(frameRateOverrides.empty());
+}
+
+TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_touch) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
+ HWC_CONFIG_ID_120,
+ /*enableFrameRateOverride=*/true);
+
+ auto layers = std::vector<LayerRequirement>{
+ LayerRequirement{.ownerUid = 1234, .weight = 1.0f},
+ };
+
+ layers[0].name = "Test layer";
+ layers[0].desiredRefreshRate = Fps(60.0f);
+ layers[0].vote = LayerVoteType::ExplicitDefault;
+
+ auto frameRateOverrides =
+ refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+ ASSERT_EQ(1, frameRateOverrides.size());
+ ASSERT_EQ(1, frameRateOverrides.count(1234));
+ ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+
+ frameRateOverrides =
+ refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/true);
+ ASSERT_EQ(1, frameRateOverrides.size());
+ ASSERT_EQ(1, frameRateOverrides.count(1234));
+ ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+
+ layers[0].vote = LayerVoteType::ExplicitExact;
+ frameRateOverrides =
+ refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+ ASSERT_EQ(1, frameRateOverrides.size());
+ ASSERT_EQ(1, frameRateOverrides.count(1234));
+ ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+
+ frameRateOverrides =
+ refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/true);
+ ASSERT_EQ(1, frameRateOverrides.size());
+ ASSERT_EQ(1, frameRateOverrides.count(1234));
+ ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ frameRateOverrides =
+ refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+ ASSERT_EQ(1, frameRateOverrides.size());
+ ASSERT_EQ(1, frameRateOverrides.count(1234));
+ ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+
+ frameRateOverrides =
+ refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/true);
+ ASSERT_TRUE(frameRateOverrides.empty());
+}
+
+TEST_F(RefreshRateConfigsTest, updateDisplayModes) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_30);
+ refreshRateConfigs->setDisplayManagerPolicy({DisplayModeId(HWC_CONFIG_ID_30),
+ /* allowGroupSwitching */ false,
+ /* range */ {Fps(30.0f), Fps(30.0f)}});
+
+ refreshRateConfigs->updateDisplayModes(m60_90Device, HWC_CONFIG_ID_60);
+
+ const auto currentRefreshRate = refreshRateConfigs->getCurrentRefreshRate();
+ EXPECT_TRUE(currentRefreshRate.getFps().equalsWithMargin(Fps(60.0)));
+ EXPECT_EQ(currentRefreshRate.getModeId(), HWC_CONFIG_ID_60);
+
+ EXPECT_TRUE(
+ getMaxSupportedRefreshRate(*refreshRateConfigs).getFps().equalsWithMargin(Fps(90.0)));
+ EXPECT_TRUE(
+ getMinSupportedRefreshRate(*refreshRateConfigs).getFps().equalsWithMargin(Fps(60.0)));
+}
+
} // namespace
} // namespace scheduler
} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wextra"
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
index 43b8e01..9c6ad06 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#undef LOG_TAG
#define LOG_TAG "LibSurfaceFlingerUnittests"
@@ -31,9 +27,8 @@
#include "Layer.h"
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
-#include "mock/MockDispSync.h"
-#include "mock/MockEventControlThread.h"
#include "mock/MockEventThread.h"
+#include "mock/MockVsyncController.h"
namespace android {
@@ -65,7 +60,7 @@
static constexpr int32_t PRIORITY_UNSET = -1;
void setupScheduler();
- void setupComposer(int virtualDisplayCount);
+ void setupComposer(uint32_t virtualDisplayCount);
sp<BufferQueueLayer> createBufferQueueLayer();
sp<BufferStateLayer> createBufferStateLayer();
sp<EffectLayer> createEffectLayer();
@@ -123,7 +118,8 @@
}
void RefreshRateSelectionTest::commitTransaction(Layer* layer) {
- layer->commitTransaction(layer->getCurrentState());
+ auto c = layer->getCurrentState();
+ layer->commitTransaction(c);
}
void RefreshRateSelectionTest::setupScheduler() {
@@ -132,26 +128,26 @@
EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*eventThread, createEventConnection(_, _))
- .WillOnce(Return(new EventThreadConnection(eventThread.get(), ResyncCallback(),
- ISurfaceComposer::eConfigChangedSuppress)));
+ .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+ ResyncCallback())));
EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
- .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
- ISurfaceComposer::eConfigChangedSuppress)));
+ .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+ ResyncCallback())));
- auto primaryDispSync = std::make_unique<mock::DispSync>();
+ auto vsyncController = std::make_unique<mock::VsyncController>();
+ auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
- EXPECT_CALL(*primaryDispSync, computeNextRefresh(0, _)).WillRepeatedly(Return(0));
- EXPECT_CALL(*primaryDispSync, getPeriod())
- .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
- EXPECT_CALL(*primaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0));
- mFlinger.setupScheduler(std::move(primaryDispSync),
- std::make_unique<mock::EventControlThread>(), std::move(eventThread),
- std::move(sfEventThread));
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*vsyncTracker, currentPeriod())
+ .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
+ std::move(eventThread), std::move(sfEventThread));
}
-void RefreshRateSelectionTest::setupComposer(int virtualDisplayCount) {
+void RefreshRateSelectionTest::setupComposer(uint32_t virtualDisplayCount) {
mComposer = new Hwc2::mock::Composer();
EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount));
mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
@@ -283,6 +279,3 @@
} // namespace
} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
index de66f8f..bf07106 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -16,7 +16,7 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
#undef LOG_TAG
#define LOG_TAG "SchedulerUnittests"
@@ -25,8 +25,9 @@
#include <log/log.h>
#include <thread>
+#include "DisplayHardware/DisplayMode.h"
+#include "Scheduler/RefreshRateConfigs.h"
#include "Scheduler/RefreshRateStats.h"
-#include "mock/DisplayHardware/MockDisplay.h"
#include "mock/MockTimeStats.h"
using namespace std::chrono_literals;
@@ -39,8 +40,8 @@
class RefreshRateStatsTest : public testing::Test {
protected:
- static inline const auto CONFIG_ID_0 = HwcConfigIndexType(0);
- static inline const auto CONFIG_ID_1 = HwcConfigIndexType(1);
+ static inline const auto CONFIG_ID_0 = DisplayModeId(0);
+ static inline const auto CONFIG_ID_1 = DisplayModeId(1);
static inline const auto CONFIG_GROUP_0 = 0;
static constexpr int64_t VSYNC_90 = 11111111;
static constexpr int64_t VSYNC_60 = 16666667;
@@ -48,22 +49,20 @@
RefreshRateStatsTest();
~RefreshRateStatsTest();
- void init(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
+ void init(const DisplayModes& configs) {
mRefreshRateConfigs =
std::make_unique<RefreshRateConfigs>(configs, /*currentConfig=*/CONFIG_ID_0);
- mRefreshRateStats = std::make_unique<RefreshRateStats>(*mRefreshRateConfigs, mTimeStats,
- /*currentConfigId=*/CONFIG_ID_0,
+
+ const auto currFps = mRefreshRateConfigs->getRefreshRateFromModeId(CONFIG_ID_0).getFps();
+ mRefreshRateStats = std::make_unique<RefreshRateStats>(mTimeStats, currFps,
/*currentPowerMode=*/PowerMode::OFF);
}
- Hwc2::mock::Display mDisplay;
mock::TimeStats mTimeStats;
std::unique_ptr<RefreshRateConfigs> mRefreshRateConfigs;
std::unique_ptr<RefreshRateStats> mRefreshRateStats;
- std::shared_ptr<const HWC2::Display::Config> createConfig(HwcConfigIndexType configId,
- int32_t configGroup,
- int64_t vsyncPeriod);
+ DisplayModePtr createDisplayMode(DisplayModeId modeId, int32_t group, int64_t vsyncPeriod);
};
RefreshRateStatsTest::RefreshRateStatsTest() {
@@ -78,11 +77,12 @@
ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
}
-std::shared_ptr<const HWC2::Display::Config> RefreshRateStatsTest::createConfig(
- HwcConfigIndexType configId, int32_t configGroup, int64_t vsyncPeriod) {
- return HWC2::Display::Config::Builder(mDisplay, configId.value())
- .setVsyncPeriod(int32_t(vsyncPeriod))
- .setConfigGroup(configGroup)
+DisplayModePtr RefreshRateStatsTest::createDisplayMode(DisplayModeId modeId, int32_t group,
+ int64_t vsyncPeriod) {
+ return DisplayMode::Builder(static_cast<hal::HWConfigId>(modeId.value()))
+ .setId(modeId)
+ .setVsyncPeriod(static_cast<int32_t>(vsyncPeriod))
+ .setGroup(group)
.build();
}
@@ -91,7 +91,7 @@
* Test cases
*/
TEST_F(RefreshRateStatsTest, oneConfigTest) {
- init({createConfig(CONFIG_ID_0, CONFIG_GROUP_0, VSYNC_90)});
+ init({createDisplayMode(CONFIG_ID_0, CONFIG_GROUP_0, VSYNC_90)});
EXPECT_CALL(mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1));
EXPECT_CALL(mTimeStats, recordRefreshRate(90, _)).Times(AtLeast(1));
@@ -102,43 +102,44 @@
// Setting up tests on mobile harness can be flaky with time passing, so testing for
// exact time changes can result in flaxy numbers. To avoid that remember old
// numbers to make sure the correct values are increasing in the next test.
- int screenOff = times["ScreenOff"];
+ auto screenOff = times["ScreenOff"];
// Screen is off by default.
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
EXPECT_LT(screenOff, times["ScreenOff"]);
- EXPECT_EQ(0u, times.count("90fps"));
+ EXPECT_EQ(0u, times.count("90.00fps"));
- mRefreshRateStats->setConfigMode(CONFIG_ID_0);
+ const auto config0Fps = mRefreshRateConfigs->getRefreshRateFromModeId(CONFIG_ID_0).getFps();
+ mRefreshRateStats->setRefreshRate(config0Fps);
mRefreshRateStats->setPowerMode(PowerMode::ON);
screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
EXPECT_EQ(screenOff, times["ScreenOff"]);
- ASSERT_EQ(1u, times.count("90fps"));
- EXPECT_LT(0, times["90fps"]);
+ ASSERT_EQ(1u, times.count("90.00fps"));
+ EXPECT_LT(0, times["90.00fps"]);
mRefreshRateStats->setPowerMode(PowerMode::DOZE);
- int ninety = mRefreshRateStats->getTotalTimes()["90fps"];
+ auto ninety = mRefreshRateStats->getTotalTimes()["90.00fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
EXPECT_LT(screenOff, times["ScreenOff"]);
- EXPECT_EQ(ninety, times["90fps"]);
+ EXPECT_EQ(ninety, times["90.00fps"]);
- mRefreshRateStats->setConfigMode(CONFIG_ID_0);
+ mRefreshRateStats->setRefreshRate(config0Fps);
screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
// Because the power mode is not PowerMode::ON, switching the config
// does not update refresh rates that come from the config.
EXPECT_LT(screenOff, times["ScreenOff"]);
- EXPECT_EQ(ninety, times["90fps"]);
+ EXPECT_EQ(ninety, times["90.00fps"]);
}
TEST_F(RefreshRateStatsTest, twoConfigsTest) {
- init({createConfig(CONFIG_ID_0, CONFIG_GROUP_0, VSYNC_90),
- createConfig(CONFIG_ID_1, CONFIG_GROUP_0, VSYNC_60)});
+ init({createDisplayMode(CONFIG_ID_0, CONFIG_GROUP_0, VSYNC_90),
+ createDisplayMode(CONFIG_ID_1, CONFIG_GROUP_0, VSYNC_60)});
EXPECT_CALL(mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1));
EXPECT_CALL(mTimeStats, recordRefreshRate(60, _)).Times(AtLeast(1));
@@ -150,70 +151,72 @@
// Setting up tests on mobile harness can be flaky with time passing, so testing for
// exact time changes can result in flaxy numbers. To avoid that remember old
// numbers to make sure the correct values are increasing in the next test.
- int screenOff = times["ScreenOff"];
+ auto screenOff = times["ScreenOff"];
// Screen is off by default.
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
EXPECT_LT(screenOff, times["ScreenOff"]);
- mRefreshRateStats->setConfigMode(CONFIG_ID_0);
+ const auto config0Fps = mRefreshRateConfigs->getRefreshRateFromModeId(CONFIG_ID_0).getFps();
+ const auto config1Fps = mRefreshRateConfigs->getRefreshRateFromModeId(CONFIG_ID_1).getFps();
+ mRefreshRateStats->setRefreshRate(config0Fps);
mRefreshRateStats->setPowerMode(PowerMode::ON);
screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
EXPECT_EQ(screenOff, times["ScreenOff"]);
- ASSERT_EQ(1u, times.count("90fps"));
- EXPECT_LT(0, times["90fps"]);
+ ASSERT_EQ(1u, times.count("90.00fps"));
+ EXPECT_LT(0, times["90.00fps"]);
// When power mode is normal, time for configs updates.
- mRefreshRateStats->setConfigMode(CONFIG_ID_1);
- int ninety = mRefreshRateStats->getTotalTimes()["90fps"];
+ mRefreshRateStats->setRefreshRate(config1Fps);
+ auto ninety = mRefreshRateStats->getTotalTimes()["90.00fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
EXPECT_EQ(screenOff, times["ScreenOff"]);
- EXPECT_EQ(ninety, times["90fps"]);
- ASSERT_EQ(1u, times.count("60fps"));
- EXPECT_LT(0, times["60fps"]);
+ EXPECT_EQ(ninety, times["90.00fps"]);
+ ASSERT_EQ(1u, times.count("60.00fps"));
+ EXPECT_LT(0, times["60.00fps"]);
- mRefreshRateStats->setConfigMode(CONFIG_ID_0);
- int sixty = mRefreshRateStats->getTotalTimes()["60fps"];
+ mRefreshRateStats->setRefreshRate(config0Fps);
+ auto sixty = mRefreshRateStats->getTotalTimes()["60.00fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
EXPECT_EQ(screenOff, times["ScreenOff"]);
- EXPECT_LT(ninety, times["90fps"]);
- EXPECT_EQ(sixty, times["60fps"]);
+ EXPECT_LT(ninety, times["90.00fps"]);
+ EXPECT_EQ(sixty, times["60.00fps"]);
- mRefreshRateStats->setConfigMode(CONFIG_ID_1);
- ninety = mRefreshRateStats->getTotalTimes()["90fps"];
+ mRefreshRateStats->setRefreshRate(config1Fps);
+ ninety = mRefreshRateStats->getTotalTimes()["90.00fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
EXPECT_EQ(screenOff, times["ScreenOff"]);
- EXPECT_EQ(ninety, times["90fps"]);
- EXPECT_LT(sixty, times["60fps"]);
+ EXPECT_EQ(ninety, times["90.00fps"]);
+ EXPECT_LT(sixty, times["60.00fps"]);
// Because the power mode is not PowerMode::ON, switching the config
// does not update refresh rates that come from the config.
mRefreshRateStats->setPowerMode(PowerMode::DOZE);
- mRefreshRateStats->setConfigMode(CONFIG_ID_0);
- sixty = mRefreshRateStats->getTotalTimes()["60fps"];
+ mRefreshRateStats->setRefreshRate(config0Fps);
+ sixty = mRefreshRateStats->getTotalTimes()["60.00fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
EXPECT_LT(screenOff, times["ScreenOff"]);
- EXPECT_EQ(ninety, times["90fps"]);
- EXPECT_EQ(sixty, times["60fps"]);
+ EXPECT_EQ(ninety, times["90.00fps"]);
+ EXPECT_EQ(sixty, times["60.00fps"]);
- mRefreshRateStats->setConfigMode(CONFIG_ID_1);
+ mRefreshRateStats->setRefreshRate(config1Fps);
screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
EXPECT_LT(screenOff, times["ScreenOff"]);
- EXPECT_EQ(ninety, times["90fps"]);
- EXPECT_EQ(sixty, times["60fps"]);
+ EXPECT_EQ(ninety, times["90.00fps"]);
+ EXPECT_EQ(sixty, times["60.00fps"]);
}
} // namespace
} // namespace scheduler
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wextra"
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 1aa7320..38e503f 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -14,40 +14,34 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#undef LOG_TAG
-#define LOG_TAG "SchedulerUnittests"
-
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <log/log.h>
#include <mutex>
-#include "Scheduler/EventControlThread.h"
#include "Scheduler/EventThread.h"
#include "Scheduler/RefreshRateConfigs.h"
#include "TestableScheduler.h"
-#include "mock/DisplayHardware/MockDisplay.h"
+#include "TestableSurfaceFlinger.h"
#include "mock/MockEventThread.h"
+#include "mock/MockLayer.h"
+#include "mock/MockSchedulerCallback.h"
using testing::_;
using testing::Return;
namespace android {
+namespace {
-constexpr PhysicalDisplayId PHYSICAL_DISPLAY_ID = 999;
+constexpr PhysicalDisplayId PHYSICAL_DISPLAY_ID(999);
class SchedulerTest : public testing::Test {
protected:
class MockEventThreadConnection : public android::EventThreadConnection {
public:
explicit MockEventThreadConnection(EventThread* eventThread)
- : EventThreadConnection(eventThread, ResyncCallback(),
- ISurfaceComposer::eConfigChangedSuppress) {}
+ : EventThreadConnection(eventThread, /*callingUid=*/0, ResyncCallback()) {}
~MockEventThreadConnection() = default;
MOCK_METHOD1(stealReceiveChannel, status_t(gui::BitTube* outChannel));
@@ -56,32 +50,30 @@
};
SchedulerTest();
- ~SchedulerTest() override;
- std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
- std::unique_ptr<TestableScheduler> mScheduler;
+ const scheduler::RefreshRateConfigs
+ mConfigs{{DisplayMode::Builder(0).setVsyncPeriod(16'666'667).setGroup(0).build()},
+ DisplayModeId(0)};
+
+ mock::SchedulerCallback mSchedulerCallback;
+
+ // The scheduler should initially disable VSYNC.
+ struct ExpectDisableVsync {
+ ExpectDisableVsync(mock::SchedulerCallback& callback) {
+ EXPECT_CALL(callback, setVsyncEnabled(false)).Times(1);
+ }
+ } mExpectDisableVsync{mSchedulerCallback};
+
+ TestableScheduler* mScheduler = new TestableScheduler{mConfigs, mSchedulerCallback};
Scheduler::ConnectionHandle mConnectionHandle;
mock::EventThread* mEventThread;
sp<MockEventThreadConnection> mEventThreadConnection;
- Hwc2::mock::Display mDisplay;
+
+ TestableSurfaceFlinger mFlinger;
};
SchedulerTest::SchedulerTest() {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
-
- std::vector<std::shared_ptr<const HWC2::Display::Config>> configs{
- HWC2::Display::Config::Builder(mDisplay, 0)
- .setVsyncPeriod(int32_t(16666667))
- .setConfigGroup(0)
- .build()};
- mRefreshRateConfigs = std::make_unique<
- scheduler::RefreshRateConfigs>(configs, /*currentConfig=*/HwcConfigIndexType(0));
-
- mScheduler = std::make_unique<TestableScheduler>(*mRefreshRateConfigs, false);
-
auto eventThread = std::make_unique<mock::EventThread>();
mEventThread = eventThread.get();
EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)).WillOnce(Return(0));
@@ -95,84 +87,117 @@
mConnectionHandle = mScheduler->createConnection(std::move(eventThread));
EXPECT_TRUE(mConnectionHandle);
+
+ mFlinger.resetScheduler(mScheduler);
}
-SchedulerTest::~SchedulerTest() {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
-}
-
-namespace {
-/* ------------------------------------------------------------------------
- * Test cases
- */
+} // namespace
TEST_F(SchedulerTest, invalidConnectionHandle) {
Scheduler::ConnectionHandle handle;
- sp<IDisplayEventConnection> connection;
- ASSERT_NO_FATAL_FAILURE(
- connection = mScheduler->createDisplayEventConnection(handle,
- ISurfaceComposer::
- eConfigChangedSuppress));
+ const sp<IDisplayEventConnection> connection = mScheduler->createDisplayEventConnection(handle);
+
EXPECT_FALSE(connection);
EXPECT_FALSE(mScheduler->getEventConnection(handle));
// The EXPECT_CALLS make sure we don't call the functions on the subsequent event threads.
EXPECT_CALL(*mEventThread, onHotplugReceived(_, _)).Times(0);
- ASSERT_NO_FATAL_FAILURE(mScheduler->onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false));
+ mScheduler->onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false);
EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(0);
- ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(handle));
+ mScheduler->onScreenAcquired(handle);
EXPECT_CALL(*mEventThread, onScreenReleased()).Times(0);
- ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(handle));
+ mScheduler->onScreenReleased(handle);
std::string output;
EXPECT_CALL(*mEventThread, dump(_)).Times(0);
- ASSERT_NO_FATAL_FAILURE(mScheduler->dump(handle, output));
+ mScheduler->dump(handle, output);
EXPECT_TRUE(output.empty());
- EXPECT_CALL(*mEventThread, setPhaseOffset(_)).Times(0);
- ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(handle, 10));
+ EXPECT_CALL(*mEventThread, setDuration(10ns, 20ns)).Times(0);
+ mScheduler->setDuration(handle, 10ns, 20ns);
}
TEST_F(SchedulerTest, validConnectionHandle) {
- sp<IDisplayEventConnection> connection;
- ASSERT_NO_FATAL_FAILURE(
- connection = mScheduler->createDisplayEventConnection(mConnectionHandle,
- ISurfaceComposer::
- eConfigChangedSuppress));
+ const sp<IDisplayEventConnection> connection =
+ mScheduler->createDisplayEventConnection(mConnectionHandle);
+
ASSERT_EQ(mEventThreadConnection, connection);
EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle));
EXPECT_CALL(*mEventThread, onHotplugReceived(PHYSICAL_DISPLAY_ID, false)).Times(1);
- ASSERT_NO_FATAL_FAILURE(
- mScheduler->onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false));
+ mScheduler->onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false);
EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(1);
- ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(mConnectionHandle));
+ mScheduler->onScreenAcquired(mConnectionHandle);
EXPECT_CALL(*mEventThread, onScreenReleased()).Times(1);
- ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(mConnectionHandle));
+ mScheduler->onScreenReleased(mConnectionHandle);
std::string output("dump");
EXPECT_CALL(*mEventThread, dump(output)).Times(1);
- ASSERT_NO_FATAL_FAILURE(mScheduler->dump(mConnectionHandle, output));
+ mScheduler->dump(mConnectionHandle, output);
EXPECT_FALSE(output.empty());
- EXPECT_CALL(*mEventThread, setPhaseOffset(10)).Times(1);
- ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(mConnectionHandle, 10));
+ EXPECT_CALL(*mEventThread, setDuration(10ns, 20ns)).Times(1);
+ mScheduler->setDuration(mConnectionHandle, 10ns, 20ns);
static constexpr size_t kEventConnections = 5;
- ON_CALL(*mEventThread, getEventThreadConnectionCount())
- .WillByDefault(Return(kEventConnections));
+ EXPECT_CALL(*mEventThread, getEventThreadConnectionCount()).WillOnce(Return(kEventConnections));
EXPECT_EQ(kEventConnections, mScheduler->getEventThreadConnectionCount(mConnectionHandle));
}
-} // namespace
-} // namespace android
+TEST_F(SchedulerTest, noLayerHistory) {
+ // Layer history should not be created if there is a single config.
+ ASSERT_FALSE(mScheduler->hasLayerHistory());
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+ sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger());
+
+ // Content detection should be no-op.
+ mScheduler->registerLayer(layer.get());
+ mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
+
+ constexpr bool kPowerStateNormal = true;
+ mScheduler->setDisplayPowerState(kPowerStateNormal);
+
+ constexpr uint32_t kDisplayArea = 999'999;
+ mScheduler->onPrimaryDisplayAreaChanged(kDisplayArea);
+
+ EXPECT_CALL(mSchedulerCallback, changeRefreshRate(_, _)).Times(0);
+ mScheduler->chooseRefreshRateForContent();
+}
+
+TEST_F(SchedulerTest, testDispatchCachedReportedMode) {
+ // If the optional fields are cleared, the function should return before
+ // onModeChange is called.
+ mScheduler->clearOptionalFieldsInFeatures();
+ EXPECT_NO_FATAL_FAILURE(mScheduler->dispatchCachedReportedMode());
+ EXPECT_CALL(*mEventThread, onModeChanged(_, _, _)).Times(0);
+}
+
+TEST_F(SchedulerTest, onNonPrimaryDisplayModeChanged_invalidParameters) {
+ DisplayModeId modeId = DisplayModeId(111);
+ nsecs_t vsyncPeriod = 111111;
+
+ // If the handle is incorrect, the function should return before
+ // onModeChange is called.
+ Scheduler::ConnectionHandle invalidHandle = {.id = 123};
+ EXPECT_NO_FATAL_FAILURE(mScheduler->onNonPrimaryDisplayModeChanged(invalidHandle,
+ PHYSICAL_DISPLAY_ID, modeId,
+ vsyncPeriod));
+ EXPECT_CALL(*mEventThread, onModeChanged(_, _, _)).Times(0);
+}
+
+TEST_F(SchedulerTest, calculateExtraBufferCount) {
+ EXPECT_EQ(0, mFlinger.calculateExtraBufferCount(Fps(60), 30ms));
+ EXPECT_EQ(1, mFlinger.calculateExtraBufferCount(Fps(90), 30ms));
+ EXPECT_EQ(2, mFlinger.calculateExtraBufferCount(Fps(120), 30ms));
+
+ EXPECT_EQ(1, mFlinger.calculateExtraBufferCount(Fps(60), 40ms));
+
+ EXPECT_EQ(0, mFlinger.calculateExtraBufferCount(Fps(60), 10ms));
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index 0d6c799..c088ddc 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -32,10 +32,9 @@
#pragma clang diagnostic pop // ignored "-Wconversion"
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
-#include "mock/MockDispSync.h"
-#include "mock/MockEventControlThread.h"
#include "mock/MockEventThread.h"
#include "mock/MockMessageQueue.h"
+#include "mock/MockVsyncController.h"
namespace android {
@@ -82,7 +81,7 @@
std::string name() override { return "BufferStateLayer"; }
sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override {
sp<Client> client;
- LayerCreationArgs args(flinger.flinger(), client, "buffer-queue-layer", WIDTH, HEIGHT,
+ LayerCreationArgs args(flinger.flinger(), client, "buffer-state-layer", WIDTH, HEIGHT,
LAYER_FLAGS, LayerMetadata());
return new BufferStateLayer(args);
}
@@ -109,11 +108,12 @@
*/
class SetFrameRateTest : public ::testing::TestWithParam<std::shared_ptr<LayerFactory>> {
protected:
- const FrameRate FRAME_RATE_VOTE1 = FrameRate(67.f, FrameRateCompatibility::Default);
- const FrameRate FRAME_RATE_VOTE2 = FrameRate(14.f, FrameRateCompatibility::ExactOrMultiple);
- const FrameRate FRAME_RATE_VOTE3 = FrameRate(99.f, FrameRateCompatibility::NoVote);
- const FrameRate FRAME_RATE_TREE = FrameRate(0, FrameRateCompatibility::NoVote);
- const FrameRate FRAME_RATE_NO_VOTE = FrameRate(0, FrameRateCompatibility::Default);
+ const FrameRate FRAME_RATE_VOTE1 = FrameRate(Fps(67.f), FrameRateCompatibility::Default);
+ const FrameRate FRAME_RATE_VOTE2 =
+ FrameRate(Fps(14.f), FrameRateCompatibility::ExactOrMultiple);
+ const FrameRate FRAME_RATE_VOTE3 = FrameRate(Fps(99.f), FrameRateCompatibility::NoVote);
+ const FrameRate FRAME_RATE_TREE = FrameRate(Fps(0.f), FrameRateCompatibility::NoVote);
+ const FrameRate FRAME_RATE_NO_VOTE = FrameRate(Fps(0.f), FrameRateCompatibility::Default);
SetFrameRateTest();
@@ -122,7 +122,6 @@
void addChild(sp<Layer> layer, sp<Layer> child);
void removeChild(sp<Layer> layer, sp<Layer> child);
- void reparentChildren(sp<Layer> layer, sp<Layer> child);
void commitTransaction();
TestableSurfaceFlinger mFlinger;
@@ -152,13 +151,10 @@
layer.get()->removeChild(child.get());
}
-void SetFrameRateTest::reparentChildren(sp<Layer> parent, sp<Layer> newParent) {
- parent.get()->reparentChildren(newParent);
-}
-
void SetFrameRateTest::commitTransaction() {
for (auto layer : mLayers) {
- layer.get()->commitTransaction(layer.get()->getCurrentState());
+ auto c = layer->getCurrentState();
+ layer->commitTransaction(c);
}
}
@@ -168,23 +164,24 @@
EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*eventThread, createEventConnection(_, _))
- .WillOnce(Return(new EventThreadConnection(eventThread.get(), ResyncCallback(),
- ISurfaceComposer::eConfigChangedSuppress)));
+ .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+ ResyncCallback())));
EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
- .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
- ISurfaceComposer::eConfigChangedSuppress)));
+ .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+ ResyncCallback())));
- auto primaryDispSync = std::make_unique<mock::DispSync>();
+ auto vsyncController = std::make_unique<mock::VsyncController>();
+ auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
- EXPECT_CALL(*primaryDispSync, computeNextRefresh(0, _)).WillRepeatedly(Return(0));
- EXPECT_CALL(*primaryDispSync, getPeriod())
- .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
- EXPECT_CALL(*primaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0));
- mFlinger.setupScheduler(std::move(primaryDispSync),
- std::make_unique<mock::EventControlThread>(), std::move(eventThread),
- std::move(sfEventThread));
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*vsyncTracker, currentPeriod())
+ .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
+ std::move(eventThread), std::move(sfEventThread), /*callback*/ nullptr,
+ /*hasMultipleModes*/ true);
}
void SetFrameRateTest::setupComposer(uint32_t virtualDisplayCount) {
@@ -259,13 +256,13 @@
commitTransaction();
EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_TREE, child2->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE2, child2->getFrameRateForLayerTree());
child1->setFrameRate(FRAME_RATE_NO_VOTE);
commitTransaction();
EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_TREE, child2->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE3, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE3, child2->getFrameRateForLayerTree());
parent->setFrameRate(FRAME_RATE_NO_VOTE);
commitTransaction();
@@ -289,8 +286,8 @@
parent->setFrameRate(FRAME_RATE_VOTE1);
commitTransaction();
EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_TREE, child2->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE1, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
parent->setFrameRate(FRAME_RATE_NO_VOTE);
commitTransaction();
@@ -352,14 +349,14 @@
parent->setFrameRate(FRAME_RATE_VOTE1);
commitTransaction();
EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE1, child1->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
addChild(child1, child2);
commitTransaction();
EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_TREE, child2->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE1, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
parent->setFrameRate(FRAME_RATE_NO_VOTE);
commitTransaction();
@@ -383,13 +380,13 @@
parent->setFrameRate(FRAME_RATE_VOTE1);
commitTransaction();
EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_TREE, child2->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE1, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
removeChild(child1, child2);
commitTransaction();
EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE1, child1->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
parent->setFrameRate(FRAME_RATE_NO_VOTE);
@@ -428,46 +425,74 @@
EXPECT_EQ(FRAME_RATE_NO_VOTE, child2_1->getFrameRateForLayerTree());
}
-TEST_P(SetFrameRateTest, SetAndGetRearentChildren) {
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-
- const auto& layerFactory = GetParam();
-
- auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto parent2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-
- addChild(parent, child1);
- addChild(child1, child2);
-
- child2->setFrameRate(FRAME_RATE_VOTE1);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, parent2->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-
- reparentChildren(parent, parent2);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_TREE, parent2->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-
- child2->setFrameRate(FRAME_RATE_NO_VOTE);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, parent2->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-}
-
INSTANTIATE_TEST_SUITE_P(PerLayerType, SetFrameRateTest,
testing::Values(std::make_shared<BufferQueueLayerFactory>(),
std::make_shared<BufferStateLayerFactory>(),
std::make_shared<EffectLayerFactory>()),
PrintToStringParamName);
+TEST_F(SetFrameRateTest, ValidateFrameRate) {
+ EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+ EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+ EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS, ""));
+ EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+ EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, "",
+ /*privileged=*/true));
+
+ EXPECT_FALSE(ValidateFrameRate(-1, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+ EXPECT_FALSE(ValidateFrameRate(1.0f / 0.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+ EXPECT_FALSE(ValidateFrameRate(0.0f / 0.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+
+ EXPECT_FALSE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+
+ // Invalid compatibility
+ EXPECT_FALSE(
+ ValidateFrameRate(60.0f, -1, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+ EXPECT_FALSE(ValidateFrameRate(60.0f, 2, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+
+ // Invalid change frame rate strategy
+ EXPECT_FALSE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT, -1, ""));
+ EXPECT_FALSE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT, 2, ""));
+}
+
+TEST_P(SetFrameRateTest, SetOnParentActivatesTree) {
+ const auto& layerFactory = GetParam();
+
+ auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ if (!parent->isVisible()) {
+ // This is a hack as all the test layers except EffectLayer are not visible,
+ // but since the logic is unified in Layer, it should be fine.
+ return;
+ }
+
+ auto child = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ addChild(parent, child);
+
+ parent->setFrameRate(FRAME_RATE_VOTE1);
+ commitTransaction();
+
+ mFlinger.mutableScheduler()
+ .mutableLayerHistory()
+ ->record(parent.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
+ mFlinger.mutableScheduler()
+ .mutableLayerHistory()
+ ->record(child.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
+
+ const auto layerHistorySummary =
+ mFlinger.mutableScheduler().mutableLayerHistory()->summarize(0);
+ ASSERT_EQ(2u, layerHistorySummary.size());
+ EXPECT_TRUE(FRAME_RATE_VOTE1.rate.equalsWithMargin(layerHistorySummary[0].desiredRefreshRate));
+ EXPECT_TRUE(FRAME_RATE_VOTE1.rate.equalsWithMargin(layerHistorySummary[1].desiredRefreshRate));
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp b/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp
index 5406879..45b7610 100644
--- a/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp
@@ -24,7 +24,6 @@
TEST(StrongTypeTest, comparison) {
using SpunkyType = StrongTyping<int, struct SpunkyTypeTag, Compare>;
- SpunkyType f2(22);
SpunkyType f1(10);
EXPECT_TRUE(f1 == f1);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
new file mode 100644
index 0000000..2362a31
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+namespace android {
+namespace {
+
+class CreateDisplayTest : public DisplayTransactionTest {};
+
+TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForNonsecureDisplay) {
+ const String8 name("virtual.test");
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ // The call should notify the interceptor that a display was created.
+ EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ sp<IBinder> displayToken = mFlinger.createDisplay(name, false);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The display should have been added to the current state
+ ASSERT_TRUE(hasCurrentDisplayState(displayToken));
+ const auto& display = getCurrentDisplayState(displayToken);
+ EXPECT_TRUE(display.isVirtual());
+ EXPECT_FALSE(display.isSecure);
+ EXPECT_EQ(name.string(), display.displayName);
+
+ // --------------------------------------------------------------------
+ // Cleanup conditions
+
+ // Destroying the display invalidates the display state.
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+}
+
+TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForSecureDisplay) {
+ const String8 name("virtual.test");
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ // The call should notify the interceptor that a display was created.
+ EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
+
+ // --------------------------------------------------------------------
+ // Invocation
+ int64_t oldId = IPCThreadState::self()->clearCallingIdentity();
+ // Set the calling identity to graphics so captureDisplay with secure is allowed.
+ IPCThreadState::self()->restoreCallingIdentity(static_cast<int64_t>(AID_GRAPHICS) << 32 |
+ AID_GRAPHICS);
+ sp<IBinder> displayToken = mFlinger.createDisplay(name, true);
+ IPCThreadState::self()->restoreCallingIdentity(oldId);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The display should have been added to the current state
+ ASSERT_TRUE(hasCurrentDisplayState(displayToken));
+ const auto& display = getCurrentDisplayState(displayToken);
+ EXPECT_TRUE(display.isVirtual());
+ EXPECT_TRUE(display.isSecure);
+ EXPECT_EQ(name.string(), display.displayName);
+
+ // --------------------------------------------------------------------
+ // Cleanup conditions
+
+ // Destroying the display invalidates the display state.
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+}
+
+} // namespace
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
new file mode 100644
index 0000000..0614434
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+namespace android {
+namespace {
+
+class DestroyDisplayTest : public DisplayTransactionTest {};
+
+TEST_F(DestroyDisplayTest, destroyDisplayClearsCurrentStateForDisplay) {
+ using Case = NonHwcVirtualDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A virtual display exists
+ auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
+ existing.inject();
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ // The call should notify the interceptor that a display was created.
+ EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
+
+ // Destroying the display invalidates the display state.
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.destroyDisplay(existing.token());
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The display should have been removed from the current state
+ EXPECT_FALSE(hasCurrentDisplayState(existing.token()));
+
+ // Ths display should still exist in the drawing state
+ EXPECT_TRUE(hasDrawingDisplayState(existing.token()));
+
+ // The display transaction needed flasg should be set
+ EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
+}
+
+TEST_F(DestroyDisplayTest, destroyDisplayHandlesUnknownDisplay) {
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ sp<BBinder> displayToken = new BBinder();
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.destroyDisplay(displayToken);
+}
+
+} // namespace
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayNativePrimariesTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayNativePrimariesTest.cpp
new file mode 100644
index 0000000..0171f1b
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayNativePrimariesTest.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace {
+
+class GetDisplayNativePrimaries : public DisplayTransactionTest {
+public:
+ GetDisplayNativePrimaries();
+ void populateDummyDisplayNativePrimaries(ui::DisplayPrimaries& primaries);
+ void checkDummyDisplayNativePrimaries(const ui::DisplayPrimaries& primaries);
+
+private:
+ static constexpr float mStartingTestValue = 1.0f;
+};
+
+GetDisplayNativePrimaries::GetDisplayNativePrimaries() {
+ SimplePrimaryDisplayCase::Display::injectHwcDisplay(this);
+ injectFakeNativeWindowSurfaceFactory();
+}
+
+void GetDisplayNativePrimaries::populateDummyDisplayNativePrimaries(
+ ui::DisplayPrimaries& primaries) {
+ float startingVal = mStartingTestValue;
+ primaries.red.X = startingVal++;
+ primaries.red.Y = startingVal++;
+ primaries.red.Z = startingVal++;
+ primaries.green.X = startingVal++;
+ primaries.green.Y = startingVal++;
+ primaries.green.Z = startingVal++;
+ primaries.blue.X = startingVal++;
+ primaries.blue.Y = startingVal++;
+ primaries.blue.Z = startingVal++;
+ primaries.white.X = startingVal++;
+ primaries.white.Y = startingVal++;
+ primaries.white.Z = startingVal++;
+}
+
+void GetDisplayNativePrimaries::checkDummyDisplayNativePrimaries(
+ const ui::DisplayPrimaries& primaries) {
+ float startingVal = mStartingTestValue;
+ EXPECT_EQ(primaries.red.X, startingVal++);
+ EXPECT_EQ(primaries.red.Y, startingVal++);
+ EXPECT_EQ(primaries.red.Z, startingVal++);
+ EXPECT_EQ(primaries.green.X, startingVal++);
+ EXPECT_EQ(primaries.green.Y, startingVal++);
+ EXPECT_EQ(primaries.green.Z, startingVal++);
+ EXPECT_EQ(primaries.blue.X, startingVal++);
+ EXPECT_EQ(primaries.blue.Y, startingVal++);
+ EXPECT_EQ(primaries.blue.Z, startingVal++);
+ EXPECT_EQ(primaries.white.X, startingVal++);
+ EXPECT_EQ(primaries.white.Y, startingVal++);
+ EXPECT_EQ(primaries.white.Z, startingVal++);
+}
+
+TEST_F(GetDisplayNativePrimaries, nullDisplayToken) {
+ ui::DisplayPrimaries primaries;
+ EXPECT_EQ(BAD_VALUE, mFlinger.getDisplayNativePrimaries(nullptr, primaries));
+}
+
+TEST_F(GetDisplayNativePrimaries, internalDisplayWithPrimariesData) {
+ auto injector = SimplePrimaryDisplayCase::Display::makeFakeExistingDisplayInjector(this);
+ injector.inject();
+ auto internalDisplayToken = injector.token();
+
+ ui::DisplayPrimaries expectedPrimaries;
+ populateDummyDisplayNativePrimaries(expectedPrimaries);
+ mFlinger.setInternalDisplayPrimaries(expectedPrimaries);
+
+ ui::DisplayPrimaries primaries;
+ EXPECT_EQ(NO_ERROR, mFlinger.getDisplayNativePrimaries(internalDisplayToken, primaries));
+
+ checkDummyDisplayNativePrimaries(primaries);
+}
+
+TEST_F(GetDisplayNativePrimaries, notInternalDisplayToken) {
+ sp<BBinder> notInternalDisplayToken = new BBinder();
+
+ ui::DisplayPrimaries primaries;
+ populateDummyDisplayNativePrimaries(primaries);
+ EXPECT_EQ(NAME_NOT_FOUND,
+ mFlinger.getDisplayNativePrimaries(notInternalDisplayToken, primaries));
+
+ // Check primaries argument wasn't modified in case of failure
+ checkDummyDisplayNativePrimaries(primaries);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp
new file mode 100644
index 0000000..b713334
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp
@@ -0,0 +1,782 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+namespace android {
+namespace {
+
+class HandleTransactionLockedTest : public DisplayTransactionTest {
+public:
+ template <typename Case>
+ void setupCommonPreconditions();
+
+ template <typename Case, bool connected>
+ static void expectHotplugReceived(mock::EventThread*);
+
+ template <typename Case>
+ void setupCommonCallExpectationsForConnectProcessing();
+
+ template <typename Case>
+ void setupCommonCallExpectationsForDisconnectProcessing();
+
+ template <typename Case>
+ void processesHotplugConnectCommon();
+
+ template <typename Case>
+ void ignoresHotplugConnectCommon();
+
+ template <typename Case>
+ void processesHotplugDisconnectCommon();
+
+ template <typename Case>
+ void verifyDisplayIsConnected(const sp<IBinder>& displayToken);
+
+ template <typename Case>
+ void verifyPhysicalDisplayIsConnected();
+
+ void verifyDisplayIsNotConnected(const sp<IBinder>& displayToken);
+};
+
+template <typename Case>
+void HandleTransactionLockedTest::setupCommonPreconditions() {
+ // Wide color displays support is configured appropriately
+ Case::WideColorSupport::injectConfigChange(this);
+
+ // SurfaceFlinger will use a test-controlled factory for BufferQueues
+ injectFakeBufferQueueFactory();
+
+ // SurfaceFlinger will use a test-controlled factory for native window
+ // surfaces.
+ injectFakeNativeWindowSurfaceFactory();
+}
+
+template <typename Case, bool connected>
+void HandleTransactionLockedTest::expectHotplugReceived(mock::EventThread* eventThread) {
+ const auto convert = [](auto physicalDisplayId) {
+ return std::make_optional(DisplayId{physicalDisplayId});
+ };
+
+ EXPECT_CALL(*eventThread,
+ onHotplugReceived(ResultOf(convert, Case::Display::DISPLAY_ID::get()), connected))
+ .Times(1);
+}
+
+template <typename Case>
+void HandleTransactionLockedTest::setupCommonCallExpectationsForConnectProcessing() {
+ Case::Display::setupHwcHotplugCallExpectations(this);
+
+ Case::Display::setupFramebufferConsumerBufferQueueCallExpectations(this);
+ Case::Display::setupFramebufferProducerBufferQueueCallExpectations(this);
+ Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
+ Case::Display::setupHwcGetActiveConfigCallExpectations(this);
+
+ Case::WideColorSupport::setupComposerCallExpectations(this);
+ Case::HdrSupport::setupComposerCallExpectations(this);
+ Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
+
+ EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
+ expectHotplugReceived<Case, true>(mEventThread);
+ expectHotplugReceived<Case, true>(mSFEventThread);
+}
+
+template <typename Case>
+void HandleTransactionLockedTest::setupCommonCallExpectationsForDisconnectProcessing() {
+ EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
+
+ expectHotplugReceived<Case, false>(mEventThread);
+ expectHotplugReceived<Case, false>(mSFEventThread);
+}
+
+template <typename Case>
+void HandleTransactionLockedTest::verifyDisplayIsConnected(const sp<IBinder>& displayToken) {
+ // The display device should have been set up in the list of displays.
+ ASSERT_TRUE(hasDisplayDevice(displayToken));
+ const auto& device = getDisplayDevice(displayToken);
+ EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
+ EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary());
+
+ std::optional<DisplayDeviceState::Physical> expectedPhysical;
+ if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) {
+ const auto displayId = PhysicalDisplayId::tryCast(Case::Display::DISPLAY_ID::get());
+ ASSERT_TRUE(displayId);
+ const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
+ ASSERT_TRUE(hwcDisplayId);
+ expectedPhysical = {.id = *displayId,
+ .type = *connectionType,
+ .hwcDisplayId = *hwcDisplayId};
+ }
+
+ // The display should have been set up in the current display state
+ ASSERT_TRUE(hasCurrentDisplayState(displayToken));
+ const auto& current = getCurrentDisplayState(displayToken);
+ EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), current.isVirtual());
+ EXPECT_EQ(expectedPhysical, current.physical);
+
+ // The display should have been set up in the drawing display state
+ ASSERT_TRUE(hasDrawingDisplayState(displayToken));
+ const auto& draw = getDrawingDisplayState(displayToken);
+ EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual());
+ EXPECT_EQ(expectedPhysical, draw.physical);
+}
+
+template <typename Case>
+void HandleTransactionLockedTest::verifyPhysicalDisplayIsConnected() {
+ // HWComposer should have an entry for the display
+ EXPECT_TRUE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
+
+ // SF should have a display token.
+ const auto displayId = Case::Display::DISPLAY_ID::get();
+ ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+ ASSERT_EQ(mFlinger.mutablePhysicalDisplayTokens().count(displayId), 1);
+ auto& displayToken = mFlinger.mutablePhysicalDisplayTokens()[displayId];
+
+ verifyDisplayIsConnected<Case>(displayToken);
+}
+
+void HandleTransactionLockedTest::verifyDisplayIsNotConnected(const sp<IBinder>& displayToken) {
+ EXPECT_FALSE(hasDisplayDevice(displayToken));
+ EXPECT_FALSE(hasCurrentDisplayState(displayToken));
+ EXPECT_FALSE(hasDrawingDisplayState(displayToken));
+}
+
+template <typename Case>
+void HandleTransactionLockedTest::processesHotplugConnectCommon() {
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ setupCommonPreconditions<Case>();
+
+ // A hotplug connect event is enqueued for a display
+ Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ setupCommonCallExpectationsForConnectProcessing<Case>();
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ verifyPhysicalDisplayIsConnected<Case>();
+
+ // --------------------------------------------------------------------
+ // Cleanup conditions
+
+ EXPECT_CALL(*mComposer,
+ setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
+ .WillOnce(Return(Error::NONE));
+ EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
+}
+
+template <typename Case>
+void HandleTransactionLockedTest::ignoresHotplugConnectCommon() {
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ setupCommonPreconditions<Case>();
+
+ // A hotplug connect event is enqueued for a display
+ Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // HWComposer should not have an entry for the display
+ EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
+}
+
+template <typename Case>
+void HandleTransactionLockedTest::processesHotplugDisconnectCommon() {
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ setupCommonPreconditions<Case>();
+
+ // A hotplug disconnect event is enqueued for a display
+ Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+
+ // The display is already completely set up.
+ Case::Display::injectHwcDisplay(this);
+ auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
+ existing.inject();
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ EXPECT_CALL(*mComposer, getDisplayIdentificationData(Case::Display::HWC_DISPLAY_ID, _, _))
+ .Times(0);
+
+ setupCommonCallExpectationsForDisconnectProcessing<Case>();
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // HWComposer should not have an entry for the display
+ EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
+
+ // SF should not have a display token.
+ const auto displayId = Case::Display::DISPLAY_ID::get();
+ ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+ ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 0);
+
+ // The existing token should have been removed
+ verifyDisplayIsNotConnected(existing.token());
+}
+
+TEST_F(HandleTransactionLockedTest, processesHotplugConnectPrimaryDisplay) {
+ processesHotplugConnectCommon<SimplePrimaryDisplayCase>();
+}
+
+TEST_F(HandleTransactionLockedTest, processesHotplugConnectExternalDisplay) {
+ // Inject a primary display.
+ PrimaryDisplayVariant::injectHwcDisplay(this);
+
+ processesHotplugConnectCommon<SimpleExternalDisplayCase>();
+}
+
+TEST_F(HandleTransactionLockedTest, ignoresHotplugConnectIfPrimaryAndExternalAlreadyConnected) {
+ // Inject both a primary and external display.
+ PrimaryDisplayVariant::injectHwcDisplay(this);
+ ExternalDisplayVariant::injectHwcDisplay(this);
+
+ // TODO: This is an unnecessary call.
+ EXPECT_CALL(*mComposer,
+ getDisplayIdentificationData(TertiaryDisplayVariant::HWC_DISPLAY_ID, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(TertiaryDisplay::PORT),
+ SetArgPointee<2>(TertiaryDisplay::GET_IDENTIFICATION_DATA()),
+ Return(Error::NONE)));
+
+ ignoresHotplugConnectCommon<SimpleTertiaryDisplayCase>();
+}
+
+TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectPrimaryDisplay) {
+ processesHotplugDisconnectCommon<SimplePrimaryDisplayCase>();
+}
+
+TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectExternalDisplay) {
+ processesHotplugDisconnectCommon<SimpleExternalDisplayCase>();
+}
+
+TEST_F(HandleTransactionLockedTest, processesHotplugConnectThenDisconnectPrimary) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ setupCommonPreconditions<Case>();
+
+ // A hotplug connect event is enqueued for a display
+ Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
+ // A hotplug disconnect event is also enqueued for the same display
+ Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ setupCommonCallExpectationsForConnectProcessing<Case>();
+ setupCommonCallExpectationsForDisconnectProcessing<Case>();
+
+ EXPECT_CALL(*mComposer,
+ setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
+ .WillOnce(Return(Error::NONE));
+ EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // HWComposer should not have an entry for the display
+ EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
+
+ // SF should not have a display token.
+ const auto displayId = Case::Display::DISPLAY_ID::get();
+ ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+ ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 0);
+}
+
+TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectThenConnectPrimary) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ setupCommonPreconditions<Case>();
+
+ // The display is already completely set up.
+ Case::Display::injectHwcDisplay(this);
+ auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
+ existing.inject();
+
+ // A hotplug disconnect event is enqueued for a display
+ Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+ // A hotplug connect event is also enqueued for the same display
+ Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ setupCommonCallExpectationsForConnectProcessing<Case>();
+ setupCommonCallExpectationsForDisconnectProcessing<Case>();
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The existing token should have been removed
+ verifyDisplayIsNotConnected(existing.token());
+ const auto displayId = Case::Display::DISPLAY_ID::get();
+ ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+ ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 1);
+ EXPECT_NE(existing.token(), mFlinger.mutablePhysicalDisplayTokens()[displayId]);
+
+ // A new display should be connected in its place
+
+ verifyPhysicalDisplayIsConnected<Case>();
+
+ // --------------------------------------------------------------------
+ // Cleanup conditions
+
+ EXPECT_CALL(*mComposer,
+ setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
+ .WillOnce(Return(Error::NONE));
+ EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
+}
+
+TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAdded) {
+ using Case = HwcVirtualDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // The HWC supports at least one virtual display
+ injectMockComposer(1);
+
+ setupCommonPreconditions<Case>();
+
+ // A virtual display was added to the current state, and it has a
+ // surface(producer)
+ sp<BBinder> displayToken = new BBinder();
+
+ DisplayDeviceState state;
+ state.isSecure = static_cast<bool>(Case::Display::SECURE);
+
+ sp<mock::GraphicBufferProducer> surface{new mock::GraphicBufferProducer()};
+ state.surface = surface;
+ mFlinger.mutableCurrentState().displays.add(displayToken, state);
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ Case::Display::setupFramebufferConsumerBufferQueueCallExpectations(this);
+ Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
+
+ EXPECT_CALL(*surface, query(NATIVE_WINDOW_WIDTH, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(Case::Display::WIDTH), Return(NO_ERROR)));
+ EXPECT_CALL(*surface, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(Case::Display::HEIGHT), Return(NO_ERROR)));
+ EXPECT_CALL(*surface, query(NATIVE_WINDOW_FORMAT, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_VIRTUAL_DISPLAY_SURFACE_FORMAT),
+ Return(NO_ERROR)));
+ EXPECT_CALL(*surface, query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR)));
+
+ EXPECT_CALL(*surface, setAsyncMode(true)).Times(1);
+
+ EXPECT_CALL(*mProducer, connect(_, NATIVE_WINDOW_API_EGL, false, _)).Times(1);
+ EXPECT_CALL(*mProducer, disconnect(_, _)).Times(1);
+
+ Case::Display::setupHwcVirtualDisplayCreationCallExpectations(this);
+ Case::WideColorSupport::setupComposerCallExpectations(this);
+ Case::HdrSupport::setupComposerCallExpectations(this);
+ Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The display device should have been set up in the list of displays.
+ verifyDisplayIsConnected<Case>(displayToken);
+
+ // --------------------------------------------------------------------
+ // Cleanup conditions
+
+ EXPECT_CALL(*mComposer, destroyVirtualDisplay(Case::Display::HWC_DISPLAY_ID))
+ .WillOnce(Return(Error::NONE));
+ EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
+
+ // Cleanup
+ mFlinger.mutableCurrentState().displays.removeItem(displayToken);
+ mFlinger.mutableDrawingState().displays.removeItem(displayToken);
+}
+
+TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAddedWithNoSurface) {
+ using Case = HwcVirtualDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // The HWC supports at least one virtual display
+ injectMockComposer(1);
+
+ setupCommonPreconditions<Case>();
+
+ // A virtual display was added to the current state, but it does not have a
+ // surface.
+ sp<BBinder> displayToken = new BBinder();
+
+ DisplayDeviceState state;
+ state.isSecure = static_cast<bool>(Case::Display::SECURE);
+
+ mFlinger.mutableCurrentState().displays.add(displayToken, state);
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // There will not be a display device set up.
+ EXPECT_FALSE(hasDisplayDevice(displayToken));
+
+ // The drawing display state will be set from the current display state.
+ ASSERT_TRUE(hasDrawingDisplayState(displayToken));
+ const auto& draw = getDrawingDisplayState(displayToken);
+ EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual());
+}
+
+TEST_F(HandleTransactionLockedTest, processesVirtualDisplayRemoval) {
+ using Case = HwcVirtualDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A virtual display is set up but is removed from the current state.
+ const auto displayId = Case::Display::DISPLAY_ID::get();
+ ASSERT_TRUE(HalVirtualDisplayId::tryCast(displayId));
+ mFlinger.mutableHwcDisplayData().try_emplace(displayId);
+ Case::Display::injectHwcDisplay(this);
+ auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
+ existing.inject();
+ mFlinger.mutableCurrentState().displays.removeItem(existing.token());
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The existing token should have been removed
+ verifyDisplayIsNotConnected(existing.token());
+}
+
+TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackChanges) {
+ using Case = NonHwcVirtualDisplayCase;
+
+ constexpr uint32_t oldLayerStack = 0u;
+ constexpr uint32_t newLayerStack = 123u;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // There is a change to the layerStack state
+ display.mutableDrawingDisplayState().layerStack = oldLayerStack;
+ display.mutableCurrentDisplayState().layerStack = newLayerStack;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ EXPECT_EQ(newLayerStack, display.mutableDisplayDevice()->getLayerStack());
+}
+
+TEST_F(HandleTransactionLockedTest, processesDisplayTransformChanges) {
+ using Case = NonHwcVirtualDisplayCase;
+
+ constexpr ui::Rotation oldTransform = ui::ROTATION_0;
+ constexpr ui::Rotation newTransform = ui::ROTATION_180;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // There is a change to the orientation state
+ display.mutableDrawingDisplayState().orientation = oldTransform;
+ display.mutableCurrentDisplayState().orientation = newTransform;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ EXPECT_EQ(newTransform, display.mutableDisplayDevice()->getOrientation());
+}
+
+TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackRectChanges) {
+ using Case = NonHwcVirtualDisplayCase;
+
+ const Rect oldLayerStackRect(0, 0, 0, 0);
+ const Rect newLayerStackRect(0, 0, 123, 456);
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // There is a change to the layerStackSpaceRect state
+ display.mutableDrawingDisplayState().layerStackSpaceRect = oldLayerStackRect;
+ display.mutableCurrentDisplayState().layerStackSpaceRect = newLayerStackRect;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ EXPECT_EQ(newLayerStackRect, display.mutableDisplayDevice()->getLayerStackSpaceRect());
+}
+
+TEST_F(HandleTransactionLockedTest, processesDisplayRectChanges) {
+ using Case = NonHwcVirtualDisplayCase;
+
+ const Rect oldDisplayRect(0, 0);
+ const Rect newDisplayRect(123, 456);
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // There is a change to the layerStackSpaceRect state
+ display.mutableDrawingDisplayState().orientedDisplaySpaceRect = oldDisplayRect;
+ display.mutableCurrentDisplayState().orientedDisplaySpaceRect = newDisplayRect;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ EXPECT_EQ(newDisplayRect, display.mutableDisplayDevice()->getOrientedDisplaySpaceRect());
+}
+
+TEST_F(HandleTransactionLockedTest, processesDisplayWidthChanges) {
+ using Case = NonHwcVirtualDisplayCase;
+
+ constexpr int oldWidth = 0;
+ constexpr int oldHeight = 10;
+ constexpr int newWidth = 123;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto nativeWindow = new mock::NativeWindow();
+ auto displaySurface = new compositionengine::mock::DisplaySurface();
+ sp<GraphicBuffer> buf = new GraphicBuffer();
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.setNativeWindow(nativeWindow);
+ display.setDisplaySurface(displaySurface);
+ // Setup injection expectations
+ EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+ .WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0)));
+ EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillOnce(DoAll(SetArgPointee<1>(oldHeight), Return(0)));
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1);
+ display.inject();
+
+ // There is a change to the layerStackSpaceRect state
+ display.mutableDrawingDisplayState().width = oldWidth;
+ display.mutableDrawingDisplayState().height = oldHeight;
+ display.mutableCurrentDisplayState().width = newWidth;
+ display.mutableCurrentDisplayState().height = oldHeight;
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ EXPECT_CALL(*displaySurface, resizeBuffers(ui::Size(newWidth, oldHeight))).Times(1);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+}
+
+TEST_F(HandleTransactionLockedTest, processesDisplayHeightChanges) {
+ using Case = NonHwcVirtualDisplayCase;
+
+ constexpr int oldWidth = 0;
+ constexpr int oldHeight = 10;
+ constexpr int newHeight = 123;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto nativeWindow = new mock::NativeWindow();
+ auto displaySurface = new compositionengine::mock::DisplaySurface();
+ sp<GraphicBuffer> buf = new GraphicBuffer();
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.setNativeWindow(nativeWindow);
+ display.setDisplaySurface(displaySurface);
+ // Setup injection expectations
+ EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+ .WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0)));
+ EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillOnce(DoAll(SetArgPointee<1>(oldHeight), Return(0)));
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1);
+ display.inject();
+
+ // There is a change to the layerStackSpaceRect state
+ display.mutableDrawingDisplayState().width = oldWidth;
+ display.mutableDrawingDisplayState().height = oldHeight;
+ display.mutableCurrentDisplayState().width = oldWidth;
+ display.mutableCurrentDisplayState().height = newHeight;
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ EXPECT_CALL(*displaySurface, resizeBuffers(ui::Size(oldWidth, newHeight))).Times(1);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+}
+
+TEST_F(HandleTransactionLockedTest, processesDisplaySizeDisplayRectAndLayerStackRectChanges) {
+ using Case = NonHwcVirtualDisplayCase;
+
+ constexpr uint32_t kOldWidth = 567;
+ constexpr uint32_t kOldHeight = 456;
+ const auto kOldSize = Rect(kOldWidth, kOldHeight);
+
+ constexpr uint32_t kNewWidth = 234;
+ constexpr uint32_t kNewHeight = 123;
+ const auto kNewSize = Rect(kNewWidth, kNewHeight);
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto nativeWindow = new mock::NativeWindow();
+ auto displaySurface = new compositionengine::mock::DisplaySurface();
+ sp<GraphicBuffer> buf = new GraphicBuffer();
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.setNativeWindow(nativeWindow);
+ display.setDisplaySurface(displaySurface);
+ // Setup injection expectations
+ EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+ .WillOnce(DoAll(SetArgPointee<1>(kOldWidth), Return(0)));
+ EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillOnce(DoAll(SetArgPointee<1>(kOldHeight), Return(0)));
+ display.inject();
+
+ // There is a change to the layerStackSpaceRect state
+ display.mutableDrawingDisplayState().width = kOldWidth;
+ display.mutableDrawingDisplayState().height = kOldHeight;
+ display.mutableDrawingDisplayState().layerStackSpaceRect = kOldSize;
+ display.mutableDrawingDisplayState().orientedDisplaySpaceRect = kOldSize;
+
+ display.mutableCurrentDisplayState().width = kNewWidth;
+ display.mutableCurrentDisplayState().height = kNewHeight;
+ display.mutableCurrentDisplayState().layerStackSpaceRect = kNewSize;
+ display.mutableCurrentDisplayState().orientedDisplaySpaceRect = kNewSize;
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ EXPECT_CALL(*displaySurface, resizeBuffers(kNewSize.getSize())).Times(1);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ EXPECT_EQ(display.mutableDisplayDevice()->getBounds(), kNewSize);
+ EXPECT_EQ(display.mutableDisplayDevice()->getWidth(), kNewWidth);
+ EXPECT_EQ(display.mutableDisplayDevice()->getHeight(), kNewHeight);
+ EXPECT_EQ(display.mutableDisplayDevice()->getOrientedDisplaySpaceRect(), kNewSize);
+ EXPECT_EQ(display.mutableDisplayDevice()->getLayerStackSpaceRect(), kNewSize);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
new file mode 100644
index 0000000..69e0501
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+#include <android/hardware/power/Boost.h>
+
+namespace android {
+namespace {
+
+using android::hardware::power::Boost;
+
+TEST_F(DisplayTransactionTest, notifyPowerBoostNotifiesTouchEvent) {
+ mFlinger.scheduler()->replaceTouchTimer(100);
+ std::this_thread::sleep_for(10ms); // wait for callback to be triggered
+ EXPECT_TRUE(mFlinger.scheduler()->isTouchActive()); // Starting timer activates touch
+
+ std::this_thread::sleep_for(110ms); // wait for reset touch timer to expire and trigger callback
+ EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
+
+ EXPECT_EQ(NO_ERROR, mFlinger.notifyPowerBoost(static_cast<int32_t>(Boost::CAMERA_SHOT)));
+ std::this_thread::sleep_for(10ms); // wait for callback to maybe be triggered
+ EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
+
+ std::this_thread::sleep_for(110ms); // wait for reset touch timer to expire and trigger callback
+ EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
+
+ EXPECT_EQ(NO_ERROR, mFlinger.notifyPowerBoost(static_cast<int32_t>(Boost::INTERACTION)));
+ std::this_thread::sleep_for(10ms); // wait for callback to be triggered.
+ EXPECT_TRUE(mFlinger.scheduler()->isTouchActive());
+}
+
+} // namespace
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnHotplugReceivedTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnHotplugReceivedTest.cpp
new file mode 100644
index 0000000..42f4cf3
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnHotplugReceivedTest.cpp
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+namespace android {
+namespace {
+
+class OnHotplugReceivedTest : public DisplayTransactionTest {};
+
+TEST_F(OnHotplugReceivedTest, hotplugEnqueuesEventsForDisplayTransaction) {
+ constexpr int currentSequenceId = 123;
+ constexpr HWDisplayId hwcDisplayId1 = 456;
+ constexpr HWDisplayId hwcDisplayId2 = 654;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // Set the current sequence id for accepted events
+ mFlinger.mutableComposerSequenceId() = currentSequenceId;
+
+ // Set the main thread id so that the current thread does not appear to be
+ // the main thread.
+ mFlinger.mutableMainThreadId() = std::thread::id();
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ // We expect invalidate() to be invoked once to trigger display transaction
+ // processing.
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ // Simulate two hotplug events (a connect and a disconnect)
+ mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId1, Connection::CONNECTED);
+ mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId2, Connection::DISCONNECTED);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The display transaction needed flag should be set.
+ EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
+
+ // All events should be in the pending event queue.
+ const auto& pendingEvents = mFlinger.mutablePendingHotplugEvents();
+ ASSERT_EQ(2u, pendingEvents.size());
+ EXPECT_EQ(hwcDisplayId1, pendingEvents[0].hwcDisplayId);
+ EXPECT_EQ(Connection::CONNECTED, pendingEvents[0].connection);
+ EXPECT_EQ(hwcDisplayId2, pendingEvents[1].hwcDisplayId);
+ EXPECT_EQ(Connection::DISCONNECTED, pendingEvents[1].connection);
+}
+
+TEST_F(OnHotplugReceivedTest, hotplugDiscardsUnexpectedEvents) {
+ constexpr int currentSequenceId = 123;
+ constexpr int otherSequenceId = 321;
+ constexpr HWDisplayId displayId = 456;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // Set the current sequence id for accepted events
+ mFlinger.mutableComposerSequenceId() = currentSequenceId;
+
+ // Set the main thread id so that the current thread does not appear to be
+ // the main thread.
+ mFlinger.mutableMainThreadId() = std::thread::id();
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ // We do not expect any calls to invalidate().
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(0);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ // Call with an unexpected sequence id
+ mFlinger.onHotplugReceived(otherSequenceId, displayId, Connection::INVALID);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The display transaction needed flag should not be set
+ EXPECT_FALSE(hasTransactionFlagSet(eDisplayTransactionNeeded));
+
+ // There should be no pending events
+ EXPECT_TRUE(mFlinger.mutablePendingHotplugEvents().empty());
+}
+
+TEST_F(OnHotplugReceivedTest, hotplugProcessesEnqueuedEventsIfCalledOnMainThread) {
+ constexpr int currentSequenceId = 123;
+ constexpr HWDisplayId displayId1 = 456;
+
+ // --------------------------------------------------------------------
+ // Note:
+ // --------------------------------------------------------------------
+ // This test case is a bit tricky. We want to verify that
+ // onHotplugReceived() calls processDisplayHotplugEventsLocked(), but we
+ // don't really want to provide coverage for everything the later function
+ // does as there are specific tests for it.
+ // --------------------------------------------------------------------
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // Set the current sequence id for accepted events
+ mFlinger.mutableComposerSequenceId() = currentSequenceId;
+
+ // Set the main thread id so that the current thread does appear to be the
+ // main thread.
+ mFlinger.mutableMainThreadId() = std::this_thread::get_id();
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ // We expect invalidate() to be invoked once to trigger display transaction
+ // processing.
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ // Simulate a disconnect on a display id that is not connected. This should
+ // be enqueued by onHotplugReceived(), and dequeued by
+ // processDisplayHotplugEventsLocked(), but then ignored as invalid.
+ mFlinger.onHotplugReceived(currentSequenceId, displayId1, Connection::DISCONNECTED);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The display transaction needed flag should be set.
+ EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
+
+ // There should be no event queued on return, as it should have been
+ // processed.
+ EXPECT_TRUE(mFlinger.mutablePendingHotplugEvents().empty());
+}
+
+} // namespace
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
new file mode 100644
index 0000000..ef8b149
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+namespace android {
+namespace {
+
+class OnInitializeDisplaysTest : public DisplayTransactionTest {};
+
+TEST_F(OnInitializeDisplaysTest, onInitializeDisplaysSetsUpPrimaryDisplay) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A primary display is set up
+ Case::Display::injectHwcDisplay(this);
+ auto primaryDisplay = Case::Display::makeFakeExistingDisplayInjector(this);
+ primaryDisplay.inject();
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ // We expect the surface interceptor to possibly be used, but we treat it as
+ // disabled since it is called as a side effect rather than directly by this
+ // function.
+ EXPECT_CALL(*mSurfaceInterceptor, isEnabled()).WillOnce(Return(false));
+
+ // We expect a call to get the active display config.
+ Case::Display::setupHwcGetActiveConfigCallExpectations(this);
+
+ // We expect invalidate() to be invoked once to trigger display transaction
+ // processing.
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+ EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.onInitializeDisplays();
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The primary display should have a current state
+ ASSERT_TRUE(hasCurrentDisplayState(primaryDisplay.token()));
+ const auto& primaryDisplayState = getCurrentDisplayState(primaryDisplay.token());
+ // The layer stack state should be set to zero
+ EXPECT_EQ(0u, primaryDisplayState.layerStack);
+ // The orientation state should be set to zero
+ EXPECT_EQ(ui::ROTATION_0, primaryDisplayState.orientation);
+
+ // The orientedDisplaySpaceRect state should be set to INVALID
+ EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.orientedDisplaySpaceRect);
+
+ // The layerStackSpaceRect state should be set to INVALID
+ EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.layerStackSpaceRect);
+
+ // The width and height should both be zero
+ EXPECT_EQ(0u, primaryDisplayState.width);
+ EXPECT_EQ(0u, primaryDisplayState.height);
+
+ // The display should be set to PowerMode::ON
+ ASSERT_TRUE(hasDisplayDevice(primaryDisplay.token()));
+ auto displayDevice = primaryDisplay.mutableDisplayDevice();
+ EXPECT_EQ(PowerMode::ON, displayDevice->getPowerMode());
+
+ // The display refresh period should be set in the orientedDisplaySpaceRect tracker.
+ FrameStats stats;
+ mFlinger.getAnimFrameTracker().getStats(&stats);
+ EXPECT_EQ(DEFAULT_VSYNC_PERIOD, stats.refreshPeriodNano);
+
+ // The display transaction needed flag should be set.
+ EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
+
+ // The compositor timing should be set to default values
+ const auto& compositorTiming = mFlinger.getCompositorTiming();
+ EXPECT_EQ(-DEFAULT_VSYNC_PERIOD, compositorTiming.deadline);
+ EXPECT_EQ(DEFAULT_VSYNC_PERIOD, compositorTiming.interval);
+ EXPECT_EQ(DEFAULT_VSYNC_PERIOD, compositorTiming.presentLatency);
+}
+
+} // namespace
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp
new file mode 100644
index 0000000..be01984
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp
@@ -0,0 +1,493 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace {
+
+class SetDisplayStateLockedTest : public DisplayTransactionTest {};
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingWithUnknownDisplay) {
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // We have an unknown display token not associated with a known display
+ sp<BBinder> displayToken = new BBinder();
+
+ // The requested display state references the unknown display.
+ DisplayState state;
+ state.what = DisplayState::eLayerStackChanged;
+ state.token = displayToken;
+ state.layerStack = 456;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags are empty
+ EXPECT_EQ(0u, flags);
+
+ // The display token still doesn't match anything known.
+ EXPECT_FALSE(hasCurrentDisplayState(displayToken));
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingWhenNoChanges) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is already set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // No changes are made to the display
+ DisplayState state;
+ state.what = 0;
+ state.token = display.token();
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags are empty
+ EXPECT_EQ(0u, flags);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfSurfaceDidNotChange) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is already set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // There is a surface that can be set.
+ sp<mock::GraphicBufferProducer> surface = new mock::GraphicBufferProducer();
+
+ // The current display state has the surface set
+ display.mutableCurrentDisplayState().surface = surface;
+
+ // The incoming request sets the same surface
+ DisplayState state;
+ state.what = DisplayState::eSurfaceChanged;
+ state.token = display.token();
+ state.surface = surface;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags are empty
+ EXPECT_EQ(0u, flags);
+
+ // The current display state is unchanged.
+ EXPECT_EQ(surface.get(), display.getCurrentDisplayState().surface.get());
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfSurfaceChanged) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is already set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // There is a surface that can be set.
+ sp<mock::GraphicBufferProducer> surface = new mock::GraphicBufferProducer();
+
+ // The current display state does not have a surface
+ display.mutableCurrentDisplayState().surface = nullptr;
+
+ // The incoming request sets a surface
+ DisplayState state;
+ state.what = DisplayState::eSurfaceChanged;
+ state.token = display.token();
+ state.surface = surface;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags indicate a transaction is needed
+ EXPECT_EQ(eDisplayTransactionNeeded, flags);
+
+ // The current display layer stack state is set to the new value
+ EXPECT_EQ(surface.get(), display.getCurrentDisplayState().surface.get());
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfLayerStackDidNotChange) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is already set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The display has a layer stack set
+ display.mutableCurrentDisplayState().layerStack = 456u;
+
+ // The incoming request sets the same layer stack
+ DisplayState state;
+ state.what = DisplayState::eLayerStackChanged;
+ state.token = display.token();
+ state.layerStack = 456u;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags are empty
+ EXPECT_EQ(0u, flags);
+
+ // The current display state is unchanged
+ EXPECT_EQ(456u, display.getCurrentDisplayState().layerStack);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfLayerStackChanged) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The display has a layer stack set
+ display.mutableCurrentDisplayState().layerStack = 654u;
+
+ // The incoming request sets a different layer stack
+ DisplayState state;
+ state.what = DisplayState::eLayerStackChanged;
+ state.token = display.token();
+ state.layerStack = 456u;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags indicate a transaction is needed
+ EXPECT_EQ(eDisplayTransactionNeeded, flags);
+
+ // The desired display state has been set to the new value.
+ EXPECT_EQ(456u, display.getCurrentDisplayState().layerStack);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfProjectionDidNotChange) {
+ using Case = SimplePrimaryDisplayCase;
+ constexpr ui::Rotation initialOrientation = ui::ROTATION_180;
+ const Rect initialOrientedDisplayRect = {1, 2, 3, 4};
+ const Rect initialLayerStackRect = {5, 6, 7, 8};
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The current display state projection state is all set
+ display.mutableCurrentDisplayState().orientation = initialOrientation;
+ display.mutableCurrentDisplayState().orientedDisplaySpaceRect = initialOrientedDisplayRect;
+ display.mutableCurrentDisplayState().layerStackSpaceRect = initialLayerStackRect;
+
+ // The incoming request sets the same projection state
+ DisplayState state;
+ state.what = DisplayState::eDisplayProjectionChanged;
+ state.token = display.token();
+ state.orientation = initialOrientation;
+ state.orientedDisplaySpaceRect = initialOrientedDisplayRect;
+ state.layerStackSpaceRect = initialLayerStackRect;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags are empty
+ EXPECT_EQ(0u, flags);
+
+ // The current display state is unchanged
+ EXPECT_EQ(initialOrientation, display.getCurrentDisplayState().orientation);
+
+ EXPECT_EQ(initialOrientedDisplayRect,
+ display.getCurrentDisplayState().orientedDisplaySpaceRect);
+ EXPECT_EQ(initialLayerStackRect, display.getCurrentDisplayState().layerStackSpaceRect);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfOrientationChanged) {
+ using Case = SimplePrimaryDisplayCase;
+ constexpr ui::Rotation initialOrientation = ui::ROTATION_90;
+ constexpr ui::Rotation desiredOrientation = ui::ROTATION_180;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The current display state has an orientation set
+ display.mutableCurrentDisplayState().orientation = initialOrientation;
+
+ // The incoming request sets a different orientation
+ DisplayState state;
+ state.what = DisplayState::eDisplayProjectionChanged;
+ state.token = display.token();
+ state.orientation = desiredOrientation;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags indicate a transaction is needed
+ EXPECT_EQ(eDisplayTransactionNeeded, flags);
+
+ // The current display state has the new value.
+ EXPECT_EQ(desiredOrientation, display.getCurrentDisplayState().orientation);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfFrameChanged) {
+ using Case = SimplePrimaryDisplayCase;
+ const Rect initialOrientedDisplayRect = {0, 0, 0, 0};
+ const Rect desiredOrientedDisplayRect = {5, 6, 7, 8};
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The current display state does not have a orientedDisplaySpaceRect
+ display.mutableCurrentDisplayState().orientedDisplaySpaceRect = initialOrientedDisplayRect;
+
+ // The incoming request sets a orientedDisplaySpaceRect
+ DisplayState state;
+ state.what = DisplayState::eDisplayProjectionChanged;
+ state.token = display.token();
+ state.orientedDisplaySpaceRect = desiredOrientedDisplayRect;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags indicate a transaction is needed
+ EXPECT_EQ(eDisplayTransactionNeeded, flags);
+
+ // The current display state has the new value.
+ EXPECT_EQ(desiredOrientedDisplayRect,
+ display.getCurrentDisplayState().orientedDisplaySpaceRect);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfLayerStackRectChanged) {
+ using Case = SimplePrimaryDisplayCase;
+ const Rect initialLayerStackRect = {0, 0, 0, 0};
+ const Rect desiredLayerStackRect = {5, 6, 7, 8};
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The current display state does not have a layerStackSpaceRect
+ display.mutableCurrentDisplayState().layerStackSpaceRect = initialLayerStackRect;
+
+ // The incoming request sets a layerStackSpaceRect
+ DisplayState state;
+ state.what = DisplayState::eDisplayProjectionChanged;
+ state.token = display.token();
+ state.layerStackSpaceRect = desiredLayerStackRect;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags indicate a transaction is needed
+ EXPECT_EQ(eDisplayTransactionNeeded, flags);
+
+ // The current display state has the new value.
+ EXPECT_EQ(desiredLayerStackRect, display.getCurrentDisplayState().layerStackSpaceRect);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfSizeDidNotChange) {
+ using Case = SimplePrimaryDisplayCase;
+ constexpr uint32_t initialWidth = 1024;
+ constexpr uint32_t initialHeight = 768;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The current display state has a size set
+ display.mutableCurrentDisplayState().width = initialWidth;
+ display.mutableCurrentDisplayState().height = initialHeight;
+
+ // The incoming request sets the same display size
+ DisplayState state;
+ state.what = DisplayState::eDisplaySizeChanged;
+ state.token = display.token();
+ state.width = initialWidth;
+ state.height = initialHeight;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags are empty
+ EXPECT_EQ(0u, flags);
+
+ // The current display state is unchanged
+ EXPECT_EQ(initialWidth, display.getCurrentDisplayState().width);
+ EXPECT_EQ(initialHeight, display.getCurrentDisplayState().height);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfWidthChanged) {
+ using Case = SimplePrimaryDisplayCase;
+ constexpr uint32_t initialWidth = 0;
+ constexpr uint32_t desiredWidth = 1024;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The display does not yet have a width
+ display.mutableCurrentDisplayState().width = initialWidth;
+
+ // The incoming request sets a display width
+ DisplayState state;
+ state.what = DisplayState::eDisplaySizeChanged;
+ state.token = display.token();
+ state.width = desiredWidth;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags indicate a transaction is needed
+ EXPECT_EQ(eDisplayTransactionNeeded, flags);
+
+ // The current display state has the new value.
+ EXPECT_EQ(desiredWidth, display.getCurrentDisplayState().width);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfHeightChanged) {
+ using Case = SimplePrimaryDisplayCase;
+ constexpr uint32_t initialHeight = 0;
+ constexpr uint32_t desiredHeight = 768;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The display does not yet have a height
+ display.mutableCurrentDisplayState().height = initialHeight;
+
+ // The incoming request sets a display height
+ DisplayState state;
+ state.what = DisplayState::eDisplaySizeChanged;
+ state.token = display.token();
+ state.height = desiredHeight;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags indicate a transaction is needed
+ EXPECT_EQ(eDisplayTransactionNeeded, flags);
+
+ // The current display state has the new value.
+ EXPECT_EQ(desiredHeight, display.getCurrentDisplayState().height);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
new file mode 100644
index 0000000..6502420
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
@@ -0,0 +1,498 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace {
+
+// Used when we simulate a display that supports doze.
+template <typename Display>
+struct DozeIsSupportedVariant {
+ static constexpr bool DOZE_SUPPORTED = true;
+ static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE =
+ IComposerClient::PowerMode::DOZE;
+ static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND =
+ IComposerClient::PowerMode::DOZE_SUSPEND;
+
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(
+ std::vector<DisplayCapability>({DisplayCapability::DOZE})),
+ Return(Error::NONE)));
+ }
+};
+
+template <typename Display>
+// Used when we simulate a display that does not support doze.
+struct DozeNotSupportedVariant {
+ static constexpr bool DOZE_SUPPORTED = false;
+ static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE =
+ IComposerClient::PowerMode::ON;
+ static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND =
+ IComposerClient::PowerMode::ON;
+
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})),
+ Return(Error::NONE)));
+ }
+};
+
+struct EventThreadBaseSupportedVariant {
+ static void setupVsyncAndEventThreadNoCallExpectations(DisplayTransactionTest* test) {
+ // The callback should not be notified to toggle VSYNC.
+ EXPECT_CALL(test->mSchedulerCallback, setVsyncEnabled(_)).Times(0);
+
+ // The event thread should not be notified.
+ EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(0);
+ EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(0);
+ }
+};
+
+struct EventThreadNotSupportedVariant : public EventThreadBaseSupportedVariant {
+ static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) {
+ // These calls are only expected for the primary display.
+
+ // Instead expect no calls.
+ setupVsyncAndEventThreadNoCallExpectations(test);
+ }
+
+ static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) {
+ // These calls are only expected for the primary display.
+
+ // Instead expect no calls.
+ setupVsyncAndEventThreadNoCallExpectations(test);
+ }
+};
+
+struct EventThreadIsSupportedVariant : public EventThreadBaseSupportedVariant {
+ static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) {
+ // The callback should be notified to enable VSYNC.
+ EXPECT_CALL(test->mSchedulerCallback, setVsyncEnabled(true)).Times(1);
+
+ // The event thread should be notified that the screen was acquired.
+ EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(1);
+ }
+
+ static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) {
+ // The callback should be notified to disable VSYNC.
+ EXPECT_CALL(test->mSchedulerCallback, setVsyncEnabled(false)).Times(1);
+
+ // The event thread should not be notified that the screen was released.
+ EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(1);
+ }
+};
+
+struct DispSyncIsSupportedVariant {
+ static void setupResetModelCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mVsyncController, startPeriodTransition(DEFAULT_VSYNC_PERIOD)).Times(1);
+ EXPECT_CALL(*test->mVSyncTracker, resetModel()).Times(1);
+ }
+};
+
+struct DispSyncNotSupportedVariant {
+ static void setupResetModelCallExpectations(DisplayTransactionTest* /* test */) {}
+};
+
+// --------------------------------------------------------------------
+// Note:
+//
+// There are a large number of transitions we could test, however we only test a
+// selected subset which provides complete test coverage of the implementation.
+// --------------------------------------------------------------------
+
+template <PowerMode initialPowerMode, PowerMode targetPowerMode>
+struct TransitionVariantCommon {
+ static constexpr auto INITIAL_POWER_MODE = initialPowerMode;
+ static constexpr auto TARGET_POWER_MODE = targetPowerMode;
+
+ static void verifyPostconditions(DisplayTransactionTest*) {}
+};
+
+struct TransitionOffToOnVariant : public TransitionVariantCommon<PowerMode::OFF, PowerMode::ON> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
+ Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+ Case::DispSync::setupResetModelCallExpectations(test);
+ Case::setupRepaintEverythingCallExpectations(test);
+ }
+
+ static void verifyPostconditions(DisplayTransactionTest* test) {
+ EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
+ EXPECT_TRUE(test->mFlinger.getHasPoweredOff());
+ }
+};
+
+struct TransitionOffToDozeSuspendVariant
+ : public TransitionVariantCommon<PowerMode::OFF, PowerMode::DOZE_SUSPEND> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
+ Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+ Case::setupRepaintEverythingCallExpectations(test);
+ }
+
+ static void verifyPostconditions(DisplayTransactionTest* test) {
+ EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
+ EXPECT_TRUE(test->mFlinger.getHasPoweredOff());
+ }
+};
+
+struct TransitionOnToOffVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::OFF> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
+ Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
+ }
+
+ static void verifyPostconditions(DisplayTransactionTest* test) {
+ EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
+ }
+};
+
+struct TransitionDozeSuspendToOffVariant
+ : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::OFF> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+ Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
+ }
+
+ static void verifyPostconditions(DisplayTransactionTest* test) {
+ EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
+ }
+};
+
+struct TransitionOnToDozeVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+ Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
+ }
+};
+
+struct TransitionDozeSuspendToDozeVariant
+ : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::DOZE> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+ Case::DispSync::setupResetModelCallExpectations(test);
+ Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
+ }
+};
+
+struct TransitionDozeToOnVariant : public TransitionVariantCommon<PowerMode::DOZE, PowerMode::ON> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+ Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
+ }
+};
+
+struct TransitionDozeSuspendToOnVariant
+ : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::ON> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+ Case::DispSync::setupResetModelCallExpectations(test);
+ Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
+ }
+};
+
+struct TransitionOnToDozeSuspendVariant
+ : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE_SUSPEND> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
+ Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
+ }
+};
+
+struct TransitionOnToUnknownVariant
+ : public TransitionVariantCommon<PowerMode::ON, static_cast<PowerMode>(POWER_MODE_LEET)> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+ Case::setupNoComposerPowerModeCallExpectations(test);
+ }
+};
+
+// --------------------------------------------------------------------
+// Note:
+//
+// Rather than testing the cartesian product of
+// DozeIsSupported/DozeNotSupported with all other options, we use one for one
+// display type, and the other for another display type.
+// --------------------------------------------------------------------
+
+template <typename DisplayVariant, typename DozeVariant, typename EventThreadVariant,
+ typename DispSyncVariant, typename TransitionVariant>
+struct DisplayPowerCase {
+ using Display = DisplayVariant;
+ using Doze = DozeVariant;
+ using EventThread = EventThreadVariant;
+ using DispSync = DispSyncVariant;
+ using Transition = TransitionVariant;
+
+ static auto injectDisplayWithInitialPowerMode(DisplayTransactionTest* test, PowerMode mode) {
+ Display::injectHwcDisplayWithNoDefaultCapabilities(test);
+ auto display = Display::makeFakeExistingDisplayInjector(test);
+ display.inject();
+ display.mutableDisplayDevice()->setPowerMode(mode);
+ return display;
+ }
+
+ static void setInitialPrimaryHWVsyncEnabled(DisplayTransactionTest* test, bool enabled) {
+ test->mFlinger.scheduler()->mutablePrimaryHWVsyncEnabled() = enabled;
+ }
+
+ static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mMessageQueue, invalidate()).Times(1);
+ }
+
+ static void setupSurfaceInterceptorCallExpectations(DisplayTransactionTest* test,
+ PowerMode mode) {
+ EXPECT_CALL(*test->mSurfaceInterceptor, isEnabled()).WillOnce(Return(true));
+ EXPECT_CALL(*test->mSurfaceInterceptor, savePowerModeUpdate(_, static_cast<int32_t>(mode)))
+ .Times(1);
+ }
+
+ static void setupComposerCallExpectations(DisplayTransactionTest* test, PowerMode mode) {
+ // Any calls to get the active config will return a default value.
+ EXPECT_CALL(*test->mComposer, getActiveConfig(Display::HWC_DISPLAY_ID, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(Display::HWC_ACTIVE_CONFIG_ID),
+ Return(Error::NONE)));
+
+ // Any calls to get whether the display supports dozing will return the value set by the
+ // policy variant.
+ EXPECT_CALL(*test->mComposer, getDozeSupport(Display::HWC_DISPLAY_ID, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(Doze::DOZE_SUPPORTED), Return(Error::NONE)));
+
+ EXPECT_CALL(*test->mComposer, setPowerMode(Display::HWC_DISPLAY_ID, mode)).Times(1);
+ }
+
+ static void setupNoComposerPowerModeCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, setPowerMode(Display::HWC_DISPLAY_ID, _)).Times(0);
+ }
+};
+
+// A sample configuration for the primary display.
+// In addition to having event thread support, we emulate doze support.
+template <typename TransitionVariant>
+using PrimaryDisplayPowerCase =
+ DisplayPowerCase<PrimaryDisplayVariant, DozeIsSupportedVariant<PrimaryDisplayVariant>,
+ EventThreadIsSupportedVariant, DispSyncIsSupportedVariant,
+ TransitionVariant>;
+
+// A sample configuration for the external display.
+// In addition to not having event thread support, we emulate not having doze
+// support.
+template <typename TransitionVariant>
+using ExternalDisplayPowerCase =
+ DisplayPowerCase<ExternalDisplayVariant, DozeNotSupportedVariant<ExternalDisplayVariant>,
+ EventThreadNotSupportedVariant, DispSyncNotSupportedVariant,
+ TransitionVariant>;
+
+class SetPowerModeInternalTest : public DisplayTransactionTest {
+public:
+ template <typename Case>
+ void transitionDisplayCommon();
+};
+
+template <PowerMode PowerMode>
+struct PowerModeInitialVSyncEnabled : public std::false_type {};
+
+template <>
+struct PowerModeInitialVSyncEnabled<PowerMode::ON> : public std::true_type {};
+
+template <>
+struct PowerModeInitialVSyncEnabled<PowerMode::DOZE> : public std::true_type {};
+
+template <typename Case>
+void SetPowerModeInternalTest::transitionDisplayCommon() {
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ Case::Doze::setupComposerCallExpectations(this);
+ auto display =
+ Case::injectDisplayWithInitialPowerMode(this, Case::Transition::INITIAL_POWER_MODE);
+ Case::setInitialPrimaryHWVsyncEnabled(this,
+ PowerModeInitialVSyncEnabled<
+ Case::Transition::INITIAL_POWER_MODE>::value);
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ Case::setupSurfaceInterceptorCallExpectations(this, Case::Transition::TARGET_POWER_MODE);
+ Case::Transition::template setupCallExpectations<Case>(this);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.setPowerModeInternal(display.mutableDisplayDevice(),
+ Case::Transition::TARGET_POWER_MODE);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ Case::Transition::verifyPostconditions(this);
+}
+
+TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfNoChange) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A primary display device is set up
+ Case::Display::injectHwcDisplay(this);
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The display is already set to PowerMode::ON
+ display.mutableDisplayDevice()->setPowerMode(PowerMode::ON);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), PowerMode::ON);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode());
+}
+
+TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfVirtualDisplay) {
+ using Case = HwcVirtualDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // Insert display data so that the HWC thinks it created the virtual display.
+ const auto displayId = Case::Display::DISPLAY_ID::get();
+ ASSERT_TRUE(HalVirtualDisplayId::tryCast(displayId));
+ mFlinger.mutableHwcDisplayData().try_emplace(displayId);
+
+ // A virtual display device is set up
+ Case::Display::injectHwcDisplay(this);
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The display is set to PowerMode::ON
+ getDisplayDevice(display.token())->setPowerMode(PowerMode::ON);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), PowerMode::OFF);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode());
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToDozeSuspendVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToOffVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOffVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozePrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozePrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeSuspendVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToUnknownVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToDozeSuspendVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToOffVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOffVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozeExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeSuspendVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToUnknownVariant>>();
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
new file mode 100644
index 0000000..e32c4bf
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -0,0 +1,335 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayHardware/DisplayMode.h"
+
+#include "DisplayTransactionTestHelpers.h"
+
+namespace android {
+namespace {
+
+using hal::RenderIntent;
+
+// For this variant, SurfaceFlinger should configure itself with wide display
+// support, and the display should respond with an non-empty list of supported
+// color modes. Wide-color support should be configured.
+template <typename Display>
+struct WideColorP3ColorimetricSupportedVariant {
+ static constexpr bool WIDE_COLOR_SUPPORTED = true;
+
+ static void injectConfigChange(DisplayTransactionTest* test) {
+ test->mFlinger.mutableUseColorManagement() = true;
+ test->mFlinger.mutableHasWideColorDisplay() = true;
+ test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
+ }
+
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_DATASPACE)).Times(1);
+
+ EXPECT_CALL(*test->mComposer, getColorModes(Display::HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<ColorMode>({ColorMode::DISPLAY_P3})),
+ Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer,
+ getRenderIntents(Display::HWC_DISPLAY_ID, ColorMode::DISPLAY_P3, _))
+ .WillOnce(DoAll(SetArgPointee<2>(
+ std::vector<RenderIntent>({RenderIntent::COLORIMETRIC})),
+ Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer,
+ setColorMode(Display::HWC_DISPLAY_ID, ColorMode::SRGB,
+ RenderIntent::COLORIMETRIC))
+ .WillOnce(Return(Error::NONE));
+ }
+};
+
+template <typename Display>
+struct Hdr10PlusSupportedVariant {
+ static constexpr bool HDR10_PLUS_SUPPORTED = true;
+ static constexpr bool HDR10_SUPPORTED = true;
+ static constexpr bool HDR_HLG_SUPPORTED = false;
+ static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getHdrCapabilities(_, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({
+ Hdr::HDR10_PLUS,
+ Hdr::HDR10,
+ })),
+ Return(Error::NONE)));
+ }
+};
+
+// For this variant, the composer should respond with a non-empty list of HDR
+// modes containing HDR10, so HDR10 support should be configured.
+template <typename Display>
+struct Hdr10SupportedVariant {
+ static constexpr bool HDR10_PLUS_SUPPORTED = false;
+ static constexpr bool HDR10_SUPPORTED = true;
+ static constexpr bool HDR_HLG_SUPPORTED = false;
+ static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({Hdr::HDR10})),
+ Return(Error::NONE)));
+ }
+};
+
+// For this variant, the composer should respond with a non-empty list of HDR
+// modes containing HLG, so HLG support should be configured.
+template <typename Display>
+struct HdrHlgSupportedVariant {
+ static constexpr bool HDR10_PLUS_SUPPORTED = false;
+ static constexpr bool HDR10_SUPPORTED = false;
+ static constexpr bool HDR_HLG_SUPPORTED = true;
+ static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
+ .WillOnce(
+ DoAll(SetArgPointee<1>(std::vector<Hdr>({Hdr::HLG})), Return(Error::NONE)));
+ }
+};
+
+// For this variant, the composer should respond with a non-empty list of HDR
+// modes containing DOLBY_VISION, so DOLBY_VISION support should be configured.
+template <typename Display>
+struct HdrDolbyVisionSupportedVariant {
+ static constexpr bool HDR10_PLUS_SUPPORTED = false;
+ static constexpr bool HDR10_SUPPORTED = false;
+ static constexpr bool HDR_HLG_SUPPORTED = false;
+ static constexpr bool HDR_DOLBY_VISION_SUPPORTED = true;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({Hdr::DOLBY_VISION})),
+ Return(Error::NONE)));
+ }
+};
+
+template <typename Display>
+struct Smpte2086PerFrameMetadataSupportVariant {
+ static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::SMPTE2086;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
+ .WillOnce(Return(std::vector<PerFrameMetadataKey>({
+ PerFrameMetadataKey::DISPLAY_RED_PRIMARY_X,
+ PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y,
+ PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_X,
+ PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_Y,
+ PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_X,
+ PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_Y,
+ PerFrameMetadataKey::WHITE_POINT_X,
+ PerFrameMetadataKey::WHITE_POINT_Y,
+ PerFrameMetadataKey::MAX_LUMINANCE,
+ PerFrameMetadataKey::MIN_LUMINANCE,
+ })));
+ }
+};
+
+template <typename Display>
+struct Cta861_3_PerFrameMetadataSupportVariant {
+ static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::CTA861_3;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
+ .WillOnce(Return(std::vector<PerFrameMetadataKey>({
+ PerFrameMetadataKey::MAX_CONTENT_LIGHT_LEVEL,
+ PerFrameMetadataKey::MAX_FRAME_AVERAGE_LIGHT_LEVEL,
+ })));
+ }
+};
+
+template <typename Display>
+struct Hdr10_Plus_PerFrameMetadataSupportVariant {
+ static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::HDR10PLUS;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
+ .WillOnce(Return(std::vector<PerFrameMetadataKey>({
+ PerFrameMetadataKey::HDR10_PLUS_SEI,
+ })));
+ }
+};
+
+using WideColorP3ColorimetricDisplayCase =
+ Case<PrimaryDisplayVariant, WideColorP3ColorimetricSupportedVariant<PrimaryDisplayVariant>,
+ HdrNotSupportedVariant<PrimaryDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using Hdr10PlusDisplayCase =
+ Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+ Hdr10SupportedVariant<PrimaryDisplayVariant>,
+ Hdr10_Plus_PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using Hdr10DisplayCase =
+ Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+ Hdr10SupportedVariant<PrimaryDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using HdrHlgDisplayCase =
+ Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+ HdrHlgSupportedVariant<PrimaryDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using HdrDolbyVisionDisplayCase =
+ Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+ HdrDolbyVisionSupportedVariant<PrimaryDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using HdrSmpte2086DisplayCase =
+ Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+ HdrNotSupportedVariant<PrimaryDisplayVariant>,
+ Smpte2086PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using HdrCta861_3_DisplayCase =
+ Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+ HdrNotSupportedVariant<PrimaryDisplayVariant>,
+ Cta861_3_PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+
+class SetupNewDisplayDeviceInternalTest : public DisplayTransactionTest {
+public:
+ template <typename T>
+ void setupNewDisplayDeviceInternalTest();
+};
+
+template <typename Case>
+void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() {
+ const sp<BBinder> displayToken = new BBinder();
+ const sp<compositionengine::mock::DisplaySurface> displaySurface =
+ new compositionengine::mock::DisplaySurface();
+ const sp<mock::GraphicBufferProducer> producer = new mock::GraphicBufferProducer();
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // Wide color displays support is configured appropriately
+ Case::WideColorSupport::injectConfigChange(this);
+
+ // The display is setup with the HWC.
+ Case::Display::injectHwcDisplay(this);
+
+ // SurfaceFlinger will use a test-controlled factory for native window
+ // surfaces.
+ injectFakeNativeWindowSurfaceFactory();
+
+ // A compositionengine::Display has already been created
+ auto compositionDisplay = Case::Display::injectCompositionDisplay(this);
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ // Various native window calls will be made.
+ Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
+ Case::Display::setupHwcGetActiveConfigCallExpectations(this);
+ Case::Display::setupHwcGetConfigsCallExpectations(this);
+ Case::WideColorSupport::setupComposerCallExpectations(this);
+ Case::HdrSupport::setupComposerCallExpectations(this);
+ Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ DisplayDeviceState state;
+ if constexpr (constexpr auto connectionType = Case::Display::CONNECTION_TYPE::value) {
+ const auto displayId = PhysicalDisplayId::tryCast(Case::Display::DISPLAY_ID::get());
+ ASSERT_TRUE(displayId);
+ const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
+ ASSERT_TRUE(hwcDisplayId);
+ mFlinger.getHwComposer().allocatePhysicalDisplay(*hwcDisplayId, *displayId);
+ DisplayModePtr activeMode = DisplayMode::Builder(Case::Display::HWC_ACTIVE_CONFIG_ID)
+ .setWidth(Case::Display::WIDTH)
+ .setHeight(Case::Display::HEIGHT)
+ .setVsyncPeriod(DEFAULT_VSYNC_PERIOD)
+ .setDpiX(DEFAULT_DPI)
+ .setDpiY(DEFAULT_DPI)
+ .setGroup(0)
+ .build();
+ DisplayModes modes{activeMode};
+ state.physical = {.id = *displayId,
+ .type = *connectionType,
+ .hwcDisplayId = *hwcDisplayId,
+ .supportedModes = modes,
+ .activeMode = activeMode};
+ }
+
+ state.isSecure = static_cast<bool>(Case::Display::SECURE);
+
+ auto device = mFlinger.setupNewDisplayDeviceInternal(displayToken, compositionDisplay, state,
+ displaySurface, producer);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ ASSERT_TRUE(device != nullptr);
+ EXPECT_EQ(Case::Display::DISPLAY_ID::get(), device->getId());
+ EXPECT_EQ(Case::Display::CONNECTION_TYPE::value, device->getConnectionType());
+ EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), device->isVirtual());
+ EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
+ EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary());
+ EXPECT_EQ(Case::Display::WIDTH, device->getWidth());
+ EXPECT_EQ(Case::Display::HEIGHT, device->getHeight());
+ EXPECT_EQ(Case::WideColorSupport::WIDE_COLOR_SUPPORTED, device->hasWideColorGamut());
+ EXPECT_EQ(Case::HdrSupport::HDR10_PLUS_SUPPORTED, device->hasHDR10PlusSupport());
+ EXPECT_EQ(Case::HdrSupport::HDR10_SUPPORTED, device->hasHDR10Support());
+ EXPECT_EQ(Case::HdrSupport::HDR_HLG_SUPPORTED, device->hasHLGSupport());
+ EXPECT_EQ(Case::HdrSupport::HDR_DOLBY_VISION_SUPPORTED, device->hasDolbyVisionSupport());
+ EXPECT_EQ(Case::PerFrameMetadataSupport::PER_FRAME_METADATA_KEYS,
+ device->getSupportedPerFrameMetadata());
+
+ if constexpr (Case::Display::CONNECTION_TYPE::value) {
+ EXPECT_EQ(1, device->getSupportedModes().size());
+ EXPECT_NE(nullptr, device->getActiveMode());
+ EXPECT_EQ(Case::Display::HWC_ACTIVE_CONFIG_ID, device->getActiveMode()->getHwcId());
+ }
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createSimplePrimaryDisplay) {
+ setupNewDisplayDeviceInternalTest<SimplePrimaryDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createSimpleExternalDisplay) {
+ setupNewDisplayDeviceInternalTest<SimpleExternalDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createNonHwcVirtualDisplay) {
+ setupNewDisplayDeviceInternalTest<NonHwcVirtualDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createHwcVirtualDisplay) {
+ setupNewDisplayDeviceInternalTest<HwcVirtualDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createWideColorP3Display) {
+ setupNewDisplayDeviceInternalTest<WideColorP3ColorimetricDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createHdr10PlusDisplay) {
+ setupNewDisplayDeviceInternalTest<Hdr10PlusDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createHdr10Display) {
+ setupNewDisplayDeviceInternalTest<Hdr10DisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createHdrHlgDisplay) {
+ setupNewDisplayDeviceInternalTest<HdrHlgDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createHdrDolbyVisionDisplay) {
+ setupNewDisplayDeviceInternalTest<HdrDolbyVisionDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createHdrSmpte2086DisplayCase) {
+ setupNewDisplayDeviceInternalTest<HdrSmpte2086DisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createHdrCta816_3_DisplayCase) {
+ setupNewDisplayDeviceInternalTest<HdrCta861_3_DisplayCase>();
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index d5ecae8..3f9dd01 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -16,67 +16,83 @@
#pragma once
+#include <Scheduler/Scheduler.h>
#include <gmock/gmock.h>
#include <gui/ISurfaceComposer.h>
-#include "Scheduler/DispSync.h"
#include "Scheduler/EventThread.h"
#include "Scheduler/LayerHistory.h"
#include "Scheduler/Scheduler.h"
+#include "Scheduler/VSyncTracker.h"
+#include "Scheduler/VsyncController.h"
+#include "mock/MockVSyncTracker.h"
+#include "mock/MockVsyncController.h"
namespace android {
-class TestableScheduler : public Scheduler, private ISchedulerCallback {
+class TestableScheduler : public Scheduler {
public:
- TestableScheduler(const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2)
- : Scheduler([](bool) {}, configs, *this, useContentDetectionV2, true) {
- if (mUseContentDetectionV2) {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>(configs);
- } else {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
- }
- }
+ TestableScheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback)
+ : TestableScheduler(std::make_unique<mock::VsyncController>(),
+ std::make_unique<mock::VSyncTracker>(), configs, callback) {}
- TestableScheduler(std::unique_ptr<DispSync> primaryDispSync,
- std::unique_ptr<EventControlThread> eventControlThread,
- const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2)
- : Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs, *this,
- useContentDetectionV2, true) {
- if (mUseContentDetectionV2) {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>(configs);
- } else {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
- }
- }
+ TestableScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController,
+ std::unique_ptr<scheduler::VSyncTracker> vsyncTracker,
+ const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback)
+ : Scheduler({std::move(vsyncController), std::move(vsyncTracker), nullptr}, configs,
+ callback, createLayerHistory(configs),
+ {.supportKernelTimer = false, .useContentDetection = true}) {}
// Used to inject mock event thread.
ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
return Scheduler::createConnection(std::move(eventThread));
}
- size_t layerHistorySize() const NO_THREAD_SAFETY_ANALYSIS {
- if (mUseContentDetectionV2) {
- return static_cast<scheduler::impl::LayerHistoryV2*>(mLayerHistory.get())
- ->mLayerInfos.size();
- } else {
- return static_cast<scheduler::impl::LayerHistory*>(mLayerHistory.get())
- ->mLayerInfos.size();
- }
- }
-
/* ------------------------------------------------------------------------
* Read-write access to private data to set up preconditions and assert
* post-conditions.
*/
auto& mutablePrimaryHWVsyncEnabled() { return mPrimaryHWVsyncEnabled; }
- auto& mutableEventControlThread() { return mEventControlThread; }
- auto& mutablePrimaryDispSync() { return mPrimaryDispSync; }
auto& mutableHWVsyncAvailable() { return mHWVsyncAvailable; }
- auto mutableLayerHistory() {
- return static_cast<scheduler::impl::LayerHistory*>(mLayerHistory.get());
+
+ bool hasLayerHistory() const { return static_cast<bool>(mLayerHistory); }
+
+ auto* mutableLayerHistory() { return mLayerHistory.get(); }
+
+ size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS {
+ if (!mLayerHistory) return 0;
+ return mutableLayerHistory()->mLayerInfos.size();
}
- auto mutableLayerHistoryV2() {
- return static_cast<scheduler::impl::LayerHistoryV2*>(mLayerHistory.get());
+
+ void replaceTouchTimer(int64_t millis) {
+ if (mTouchTimer) {
+ mTouchTimer.reset();
+ }
+ mTouchTimer.emplace(
+ "Testable Touch timer", std::chrono::milliseconds(millis),
+ [this] { touchTimerCallback(TimerState::Reset); },
+ [this] { touchTimerCallback(TimerState::Expired); });
+ mTouchTimer->start();
+ }
+
+ bool isTouchActive() {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ return mFeatures.touch == Scheduler::TouchState::Active;
+ }
+
+ void dispatchCachedReportedMode() {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ return Scheduler::dispatchCachedReportedMode();
+ }
+
+ void clearOptionalFieldsInFeatures() {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ mFeatures.cachedModeChangedParams.reset();
+ }
+
+ void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
+ DisplayModeId modeId, nsecs_t vsyncPeriod) {
+ return Scheduler::onNonPrimaryDisplayModeChanged(handle, displayId, modeId, vsyncPeriod);
}
~TestableScheduler() {
@@ -84,15 +100,9 @@
// not report a leaked object, since the Scheduler instance may
// still be referenced by something despite our best efforts to destroy
// it after each test is done.
- mutableEventControlThread().reset();
- mutablePrimaryDispSync().reset();
+ mVsyncSchedule.controller.reset();
mConnections.clear();
}
-
-private:
- void changeRefreshRate(const RefreshRate&, ConfigEvent) override {}
- void repaintEverythingForHWC() override {}
- void kernelTimerChanged(bool /*expired*/) override {}
};
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index cd2a482..d004b9d 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -23,13 +23,15 @@
#include <compositionengine/impl/Display.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <compositionengine/mock/DisplaySurface.h>
+#include <gui/ScreenCaptureResults.h>
#include "BufferQueueLayer.h"
#include "BufferStateLayer.h"
#include "ContainerLayer.h"
#include "DisplayDevice.h"
#include "EffectLayer.h"
-#include "FakePhaseOffsets.h"
+#include "FakeVsyncConfiguration.h"
+#include "FrameTracer/FrameTracer.h"
#include "Layer.h"
#include "NativeWindowSurface.h"
#include "Scheduler/MessageQueue.h"
@@ -39,7 +41,10 @@
#include "SurfaceFlingerDefaultFactory.h"
#include "SurfaceInterceptor.h"
#include "TestableScheduler.h"
-#include "mock/DisplayHardware/MockDisplay.h"
+#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/MockDisplayIdGenerator.h"
+#include "mock/MockFrameTimeline.h"
+#include "mock/MockFrameTracer.h"
namespace android {
@@ -65,15 +70,6 @@
public:
~Factory() = default;
- std::unique_ptr<DispSync> createDispSync(const char*, bool) override {
- return nullptr;
- }
-
- std::unique_ptr<EventControlThread> createEventControlThread(
- std::function<void(bool)>) override {
- return nullptr;
- }
-
std::unique_ptr<HWComposer> createHWComposer(const std::string&) override {
return nullptr;
}
@@ -82,19 +78,18 @@
return std::make_unique<android::impl::MessageQueue>();
}
- std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
- const scheduler::RefreshRateConfigs& /*refreshRateConfigs*/) override {
+ std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
+ Fps /*currentRefreshRate*/) override {
return std::make_unique<scheduler::FakePhaseOffsets>();
}
- std::unique_ptr<Scheduler> createScheduler(std::function<void(bool)>,
- const scheduler::RefreshRateConfigs&,
+ std::unique_ptr<Scheduler> createScheduler(const scheduler::RefreshRateConfigs&,
ISchedulerCallback&) override {
return nullptr;
}
- std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger* flinger) override {
- return std::make_unique<android::impl::SurfaceInterceptor>(flinger);
+ sp<SurfaceInterceptor> createSurfaceInterceptor() override {
+ return new android::impl::SurfaceInterceptor();
}
sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override {
@@ -157,6 +152,15 @@
return nullptr;
}
+ std::unique_ptr<FrameTracer> createFrameTracer() override {
+ return std::make_unique<mock::FrameTracer>();
+ }
+
+ std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
+ std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid = 0) override {
+ return std::make_unique<mock::FrameTimeline>(timeStats, surfaceFlingerPid);
+ }
+
using CreateBufferQueueFunction =
std::function<void(sp<IGraphicBufferProducer>* /* outProducer */,
sp<IGraphicBufferConsumer>* /* outConsumer */,
@@ -175,12 +179,15 @@
} // namespace surfaceflinger::test
-class TestableSurfaceFlinger {
+class TestableSurfaceFlinger final : private ISchedulerCallback {
public:
using HotplugEvent = SurfaceFlinger::HotplugEvent;
SurfaceFlinger* flinger() { return mFlinger.get(); }
TestableScheduler* scheduler() { return mScheduler; }
+ mock::DisplayIdGenerator<GpuVirtualDisplayId>& gpuVirtualDisplayIdGenerator() {
+ return mGpuVirtualDisplayIdGenerator;
+ }
// Extend this as needed for accessing SurfaceFlinger private (and public)
// functions.
@@ -198,41 +205,49 @@
mFlinger->mCompositionEngine->setTimeStats(timeStats);
}
- void setupScheduler(std::unique_ptr<DispSync> primaryDispSync,
- std::unique_ptr<EventControlThread> eventControlThread,
+ // The ISchedulerCallback argument can be nullptr for a no-op implementation.
+ void setupScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController,
+ std::unique_ptr<scheduler::VSyncTracker> vsyncTracker,
std::unique_ptr<EventThread> appEventThread,
std::unique_ptr<EventThread> sfEventThread,
- bool useContentDetectionV2 = false) {
- std::vector<std::shared_ptr<const HWC2::Display::Config>> configs{
- HWC2::Display::Config::Builder(mDisplay, 0)
- .setVsyncPeriod(int32_t(16666667))
- .setConfigGroup(0)
- .build()};
+ ISchedulerCallback* callback = nullptr, bool hasMultipleModes = false) {
+ DisplayModes modes{DisplayMode::Builder(0)
+ .setId(DisplayModeId(0))
+ .setVsyncPeriod(16'666'667)
+ .setGroup(0)
+ .build()};
- mFlinger->mRefreshRateConfigs = std::make_unique<
- scheduler::RefreshRateConfigs>(configs, /*currentConfig=*/HwcConfigIndexType(0));
- mFlinger->mRefreshRateStats = std::make_unique<
- scheduler::RefreshRateStats>(*mFlinger->mRefreshRateConfigs, *mFlinger->mTimeStats,
- /*currentConfig=*/HwcConfigIndexType(0),
- /*powerMode=*/hal::PowerMode::OFF);
- mFlinger->mPhaseConfiguration =
- mFactory.createPhaseConfiguration(*mFlinger->mRefreshRateConfigs);
+ if (hasMultipleModes) {
+ modes.emplace_back(DisplayMode::Builder(1)
+ .setId(DisplayModeId(1))
+ .setVsyncPeriod(11'111'111)
+ .setGroup(0)
+ .build());
+ }
- mScheduler =
- new TestableScheduler(std::move(primaryDispSync), std::move(eventControlThread),
- *mFlinger->mRefreshRateConfigs, useContentDetectionV2);
+ const auto currMode = DisplayModeId(0);
+ mFlinger->mRefreshRateConfigs =
+ std::make_unique<scheduler::RefreshRateConfigs>(modes, currMode);
+ const auto currFps =
+ mFlinger->mRefreshRateConfigs->getRefreshRateFromModeId(currMode).getFps();
+ mFlinger->mRefreshRateStats =
+ std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, currFps,
+ /*powerMode=*/hal::PowerMode::OFF);
+ mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(currFps);
+ mFlinger->mVsyncModulator.emplace(mFlinger->mVsyncConfiguration->getCurrentConfigs());
+
+ mScheduler = new TestableScheduler(std::move(vsyncController), std::move(vsyncTracker),
+ *mFlinger->mRefreshRateConfigs, *(callback ?: this));
mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
resetScheduler(mScheduler);
-
- mFlinger->mVSyncModulator.emplace(*mScheduler, mFlinger->mAppConnectionHandle,
- mFlinger->mSfConnectionHandle,
- mFlinger->mPhaseConfiguration->getCurrentOffsets());
}
void resetScheduler(Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); }
+ TestableScheduler& mutableScheduler() const { return *mScheduler; }
+
using CreateBufferQueueFunction = surfaceflinger::test::Factory::CreateBufferQueueFunction;
void setCreateBufferQueueFunction(CreateBufferQueueFunction f) {
mFactory.mCreateBufferQueue = f;
@@ -277,6 +292,10 @@
layer->mPotentialCursor = potentialCursor;
}
+ static void setLayerDrawingParent(const sp<Layer>& layer, const sp<Layer>& drawingParent) {
+ layer->mDrawingParent = drawingParent;
+ }
+
/* ------------------------------------------------------------------------
* Forwarding for functions being tested
*/
@@ -319,27 +338,31 @@
return mFlinger->onInitializeDisplays();
}
+ auto notifyPowerBoost(int32_t boostId) { return mFlinger->notifyPowerBoost(boostId); }
+
// Allow reading display state without locking, as if called on the SF main thread.
auto setPowerModeInternal(const sp<DisplayDevice>& display,
hal::PowerMode mode) NO_THREAD_SAFETY_ANALYSIS {
return mFlinger->setPowerModeInternal(display, mode);
}
- auto onMessageReceived(int32_t what) { return mFlinger->onMessageReceived(what, systemTime()); }
-
- auto captureScreenImplLocked(const RenderArea& renderArea,
- SurfaceFlinger::TraverseLayersFunction traverseLayers,
- ANativeWindowBuffer* buffer, bool useIdentityTransform,
- bool forSystem, int* outSyncFd, bool regionSampling) {
- bool ignored;
- return mFlinger->captureScreenImplLocked(renderArea, traverseLayers, buffer,
- useIdentityTransform, forSystem, outSyncFd,
- regionSampling, ignored);
+ auto onMessageReceived(int32_t what) {
+ return mFlinger->onMessageReceived(what, /*vsyncId=*/0, systemTime());
}
- auto traverseLayersInDisplay(const sp<const DisplayDevice>& display,
- const LayerVector::Visitor& visitor) {
- return mFlinger->SurfaceFlinger::traverseLayersInDisplay(display, visitor);
+ auto renderScreenImplLocked(const RenderArea& renderArea,
+ SurfaceFlinger::TraverseLayersFunction traverseLayers,
+ const std::shared_ptr<renderengine::ExternalTexture>& buffer,
+ bool forSystem, bool regionSampling) {
+ ScreenCaptureResults captureResults;
+ return mFlinger->renderScreenImplLocked(renderArea, traverseLayers, buffer, forSystem,
+ regionSampling, false /* grayscale */,
+ captureResults);
+ }
+
+ auto traverseLayersInLayerStack(ui::LayerStack layerStack, int32_t uid,
+ const LayerVector::Visitor& visitor) {
+ return mFlinger->SurfaceFlinger::traverseLayersInLayerStack(layerStack, uid, visitor);
}
auto getDisplayNativePrimaries(const sp<IBinder>& displayToken,
@@ -347,18 +370,19 @@
return mFlinger->SurfaceFlinger::getDisplayNativePrimaries(displayToken, primaries);
}
- auto& getTransactionQueue() { return mFlinger->mTransactionQueues; }
+ auto& getTransactionQueue() { return mFlinger->mTransactionQueue; }
+ auto& getPendingTransactionQueue() { return mFlinger->mPendingTransactionQueues; }
- auto setTransactionState(const Vector<ComposerState>& states,
- const Vector<DisplayState>& displays, uint32_t flags,
- const sp<IBinder>& applyToken,
- const InputWindowCommands& inputWindowCommands,
- int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
- bool hasListenerCallbacks,
- std::vector<ListenerCallbacks>& listenerCallbacks) {
- return mFlinger->setTransactionState(states, displays, flags, applyToken,
- inputWindowCommands, desiredPresentTime, uncacheBuffer,
- hasListenerCallbacks, listenerCallbacks);
+ auto setTransactionState(
+ const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& states,
+ const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
+ bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+ std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
+ return mFlinger->setTransactionState(frameTimelineInfo, states, displays, flags, applyToken,
+ inputWindowCommands, desiredPresentTime,
+ isAutoTimestamp, uncacheBuffer, hasListenerCallbacks,
+ listenerCallbacks, transactionId);
}
auto flushTransactionQueues() { return mFlinger->flushTransactionQueues(); };
@@ -367,6 +391,13 @@
return mFlinger->onTransact(code, data, reply, flags);
}
+ auto getGPUContextPriority() { return mFlinger->getGPUContextPriority(); }
+
+ auto calculateExtraBufferCount(Fps maxSupportedRefreshRate,
+ std::chrono::nanoseconds presentLatency) const {
+ return SurfaceFlinger::calculateExtraBufferCount(maxSupportedRefreshRate, presentLatency);
+ }
+
/* ------------------------------------------------------------------------
* Read-only access to private data to assert post-conditions.
*/
@@ -381,6 +412,10 @@
const auto& getCompositorTiming() const { return mFlinger->getBE().mCompositorTiming; }
+ mock::FrameTracer* getFrameTracer() const {
+ return static_cast<mock::FrameTracer*>(mFlinger->mFrameTracer.get());
+ }
+
/* ------------------------------------------------------------------------
* Read-write access to private data to set up preconditions and assert
* post-conditions.
@@ -425,7 +460,7 @@
mutableCurrentState().displays.clear();
mutableDrawingState().displays.clear();
mutableEventQueue().reset();
- mutableInterceptor().reset();
+ mutableInterceptor().clear();
mFlinger->mScheduler.reset();
mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
mFlinger->mCompositionEngine->setRenderEngine(
@@ -447,7 +482,6 @@
}
auto& mutableIsConnected() { return this->mIsConnected; }
- auto& mutableConfigs() { return this->mConfigs; }
auto& mutableLayers() { return this->mLayers; }
};
@@ -456,13 +490,14 @@
static constexpr hal::HWDisplayId DEFAULT_HWC_DISPLAY_ID = 1000;
static constexpr int32_t DEFAULT_WIDTH = 1920;
static constexpr int32_t DEFAULT_HEIGHT = 1280;
- static constexpr int32_t DEFAULT_REFRESH_RATE = 16'666'666;
+ static constexpr int32_t DEFAULT_VSYNC_PERIOD = 16'666'666;
static constexpr int32_t DEFAULT_CONFIG_GROUP = 7;
static constexpr int32_t DEFAULT_DPI = 320;
static constexpr hal::HWConfigId DEFAULT_ACTIVE_CONFIG = 0;
static constexpr hal::PowerMode DEFAULT_POWER_MODE = hal::PowerMode::ON;
- FakeHwcDisplayInjector(DisplayId displayId, hal::DisplayType hwcDisplayType, bool isPrimary)
+ FakeHwcDisplayInjector(HalDisplayId displayId, hal::DisplayType hwcDisplayType,
+ bool isPrimary)
: mDisplayId(displayId), mHwcDisplayType(hwcDisplayType), mIsPrimary(isPrimary) {}
auto& setHwcDisplayId(hal::HWDisplayId displayId) {
@@ -480,8 +515,8 @@
return *this;
}
- auto& setRefreshRate(int32_t refreshRate) {
- mRefreshRate = refreshRate;
+ auto& setVsyncPeriod(int32_t vsyncPeriod) {
+ mVsyncPeriod = vsyncPeriod;
return *this;
}
@@ -510,7 +545,12 @@
return *this;
}
- void inject(TestableSurfaceFlinger* flinger, Hwc2::Composer* composer) {
+ void inject(TestableSurfaceFlinger* flinger, Hwc2::mock::Composer* composer) {
+ using ::testing::_;
+ using ::testing::DoAll;
+ using ::testing::Return;
+ using ::testing::SetArgPointee;
+
static const std::unordered_set<hal::Capability> defaultCapabilities;
if (mCapabilities == nullptr) mCapabilities = &defaultCapabilities;
@@ -521,38 +561,72 @@
auto display = std::make_unique<HWC2Display>(*composer, *mCapabilities, mHwcDisplayId,
mHwcDisplayType);
- auto config = HWC2::Display::Config::Builder(*display, mActiveConfig);
- config.setWidth(mWidth);
- config.setHeight(mHeight);
- config.setVsyncPeriod(mRefreshRate);
- config.setDpiX(mDpiX);
- config.setDpiY(mDpiY);
- config.setConfigGroup(mConfigGroup);
- display->mutableConfigs().emplace(static_cast<int32_t>(mActiveConfig), config.build());
display->mutableIsConnected() = true;
display->setPowerMode(mPowerMode);
-
flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = std::move(display);
+ EXPECT_CALL(*composer, getDisplayConfigs(mHwcDisplayId, _))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<1>(std::vector<hal::HWConfigId>{mActiveConfig}),
+ Return(hal::Error::NONE)));
+
+ EXPECT_CALL(*composer,
+ getDisplayAttribute(mHwcDisplayId, mActiveConfig, hal::Attribute::WIDTH, _))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(mWidth), Return(hal::Error::NONE)));
+
+ EXPECT_CALL(*composer,
+ getDisplayAttribute(mHwcDisplayId, mActiveConfig, hal::Attribute::HEIGHT,
+ _))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(mHeight), Return(hal::Error::NONE)));
+
+ EXPECT_CALL(*composer,
+ getDisplayAttribute(mHwcDisplayId, mActiveConfig,
+ hal::Attribute::VSYNC_PERIOD, _))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<3>(mVsyncPeriod), Return(hal::Error::NONE)));
+
+ EXPECT_CALL(*composer,
+ getDisplayAttribute(mHwcDisplayId, mActiveConfig, hal::Attribute::DPI_X, _))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(mDpiX), Return(hal::Error::NONE)));
+
+ EXPECT_CALL(*composer,
+ getDisplayAttribute(mHwcDisplayId, mActiveConfig, hal::Attribute::DPI_Y, _))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(mDpiY), Return(hal::Error::NONE)));
+
+ EXPECT_CALL(*composer,
+ getDisplayAttribute(mHwcDisplayId, mActiveConfig,
+ hal::Attribute::CONFIG_GROUP, _))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<3>(mConfigGroup), Return(hal::Error::NONE)));
+
if (mHwcDisplayType == hal::DisplayType::PHYSICAL) {
- flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId, mDisplayId);
- (mIsPrimary ? flinger->mutableInternalHwcDisplayId()
- : flinger->mutableExternalHwcDisplayId()) = mHwcDisplayId;
+ const auto physicalId = PhysicalDisplayId::tryCast(mDisplayId);
+ LOG_ALWAYS_FATAL_IF(!physicalId);
+ flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId, *physicalId);
+ if (mIsPrimary) {
+ flinger->mutableInternalHwcDisplayId() = mHwcDisplayId;
+ } else {
+ // If there is an external HWC display there should always be an internal ID
+ // as well. Set it to some arbitrary value.
+ auto& internalId = flinger->mutableInternalHwcDisplayId();
+ if (!internalId) internalId = mHwcDisplayId - 1;
+ flinger->mutableExternalHwcDisplayId() = mHwcDisplayId;
+ }
}
}
private:
- const DisplayId mDisplayId;
+ const HalDisplayId mDisplayId;
const hal::DisplayType mHwcDisplayType;
const bool mIsPrimary;
hal::HWDisplayId mHwcDisplayId = DEFAULT_HWC_DISPLAY_ID;
int32_t mWidth = DEFAULT_WIDTH;
int32_t mHeight = DEFAULT_HEIGHT;
- int32_t mRefreshRate = DEFAULT_REFRESH_RATE;
+ int32_t mVsyncPeriod = DEFAULT_VSYNC_PERIOD;
int32_t mDpiX = DEFAULT_DPI;
- int32_t mConfigGroup = DEFAULT_CONFIG_GROUP;
int32_t mDpiY = DEFAULT_DPI;
+ int32_t mConfigGroup = DEFAULT_CONFIG_GROUP;
hal::HWConfigId mActiveConfig = DEFAULT_ACTIVE_CONFIG;
hal::PowerMode mPowerMode = DEFAULT_POWER_MODE;
const std::unordered_set<hal::Capability>* mCapabilities = nullptr;
@@ -562,13 +636,29 @@
public:
FakeDisplayDeviceInjector(TestableSurfaceFlinger& flinger,
std::shared_ptr<compositionengine::Display> compositionDisplay,
- std::optional<DisplayConnectionType> connectionType,
+ std::optional<ui::DisplayConnectionType> connectionType,
std::optional<hal::HWDisplayId> hwcDisplayId, bool isPrimary)
: mFlinger(flinger),
- mCreationArgs(flinger.mFlinger.get(), mDisplayToken, compositionDisplay),
+ mCreationArgs(flinger.mFlinger.get(), flinger.mFlinger->getHwComposer(),
+ mDisplayToken, compositionDisplay),
mHwcDisplayId(hwcDisplayId) {
mCreationArgs.connectionType = connectionType;
mCreationArgs.isPrimary = isPrimary;
+
+ mActiveModeId = DisplayModeId(0);
+ DisplayModePtr activeMode =
+ DisplayMode::Builder(FakeHwcDisplayInjector::DEFAULT_ACTIVE_CONFIG)
+ .setId(mActiveModeId)
+ .setWidth(FakeHwcDisplayInjector::DEFAULT_WIDTH)
+ .setHeight(FakeHwcDisplayInjector::DEFAULT_HEIGHT)
+ .setVsyncPeriod(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)
+ .setDpiX(FakeHwcDisplayInjector::DEFAULT_DPI)
+ .setDpiY(FakeHwcDisplayInjector::DEFAULT_DPI)
+ .setGroup(0)
+ .build();
+
+ DisplayModes modes{activeMode};
+ mCreationArgs.supportedModes = modes;
}
sp<IBinder> token() const { return mDisplayToken; }
@@ -591,6 +681,16 @@
auto& mutableDisplayDevice() { return mFlinger.mutableDisplays()[mDisplayToken]; }
+ auto& setActiveMode(DisplayModeId mode) {
+ mActiveModeId = mode;
+ return *this;
+ }
+
+ auto& setSupportedModes(DisplayModes mode) {
+ mCreationArgs.supportedModes = mode;
+ return *this;
+ }
+
auto& setNativeWindow(const sp<ANativeWindow>& nativeWindow) {
mCreationArgs.nativeWindow = nativeWindow;
return *this;
@@ -634,13 +734,18 @@
DisplayDeviceState state;
if (const auto type = mCreationArgs.connectionType) {
LOG_ALWAYS_FATAL_IF(!displayId);
+ const auto physicalId = PhysicalDisplayId::tryCast(*displayId);
+ LOG_ALWAYS_FATAL_IF(!physicalId);
LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
- state.physical = {*displayId, *type, *mHwcDisplayId};
+ state.physical = {.id = *physicalId, .type = *type, .hwcDisplayId = *mHwcDisplayId};
}
state.isSecure = mCreationArgs.isSecure;
sp<DisplayDevice> device = new DisplayDevice(mCreationArgs);
+ if (!device->isVirtual()) {
+ device->setActiveMode(mActiveModeId);
+ }
mFlinger.mutableDisplays().emplace(mDisplayToken, device);
mFlinger.mutableCurrentState().displays.add(mDisplayToken, state);
mFlinger.mutableDrawingState().displays.add(mDisplayToken, state);
@@ -657,12 +762,20 @@
sp<BBinder> mDisplayToken = new BBinder();
DisplayDeviceCreationArgs mCreationArgs;
const std::optional<hal::HWDisplayId> mHwcDisplayId;
+ DisplayModeId mActiveModeId;
};
+private:
+ void setVsyncEnabled(bool) override {}
+ void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ModeEvent) override {}
+ void repaintEverythingForHWC() override {}
+ void kernelTimerChanged(bool) override {}
+ void triggerOnFrameRateOverridesChanged() {}
+
surfaceflinger::test::Factory mFactory;
sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization);
TestableScheduler* mScheduler = nullptr;
- Hwc2::mock::Display mDisplay;
+ mock::DisplayIdGenerator<GpuVirtualDisplayId> mGpuVirtualDisplayIdGenerator;
};
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 0a24650..188ea75 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -17,15 +17,16 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
#undef LOG_TAG
#define LOG_TAG "LibSurfaceFlingerUnittests"
#include <TimeStats/TimeStats.h>
-#include <android/util/ProtoOutputStream.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <log/log.h>
+#include <timestatsatomsproto/TimeStatsAtomsProtoHeader.h>
#include <utils/String16.h>
#include <utils/Vector.h>
@@ -47,6 +48,7 @@
using testing::Contains;
using testing::HasSubstr;
using testing::InSequence;
+using testing::Not;
using testing::SizeIs;
using testing::StrEq;
using testing::UnorderedElementsAre;
@@ -54,13 +56,21 @@
using PowerMode = hardware::graphics::composer::V2_4::IComposerClient::PowerMode;
// clang-format off
-#define FMT_PROTO true
-#define FMT_STRING false
-#define LAYER_ID_0 0
-#define LAYER_ID_1 1
-#define LAYER_ID_INVALID -1
-#define NUM_LAYERS 1
-#define NUM_LAYERS_INVALID "INVALID"
+#define FMT_PROTO true
+#define FMT_STRING false
+#define LAYER_ID_0 0
+#define LAYER_ID_1 1
+#define UID_0 123
+#define REFRESH_RATE_0 61
+#define RENDER_RATE_0 31
+#define REFRESH_RATE_BUCKET_0 60
+#define RENDER_RATE_BUCKET_0 30
+#define LAYER_ID_INVALID -1
+#define NUM_LAYERS 1
+#define NUM_LAYERS_INVALID "INVALID"
+
+const constexpr Fps kRefreshRate0 = Fps(static_cast<float>(REFRESH_RATE_0));
+const constexpr Fps kRenderRate0 = Fps(static_cast<float>(RENDER_RATE_0));
enum InputCommand : int32_t {
ENABLE = 0,
@@ -132,57 +142,22 @@
std::string inputCommand(InputCommand cmd, bool useProto);
- void setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts);
+ void setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts,
+ TimeStats::SetFrameRateVote frameRateVote);
int32_t genRandomInt32(int32_t begin, int32_t end);
template <size_t N>
void insertTimeRecord(const TimeStamp (&sequence)[N], int32_t id, uint64_t frameNumber,
- nsecs_t ts) {
+ nsecs_t ts, TimeStats::SetFrameRateVote frameRateVote = {}) {
for (size_t i = 0; i < N; i++, ts += 1000000) {
- setTimeStamp(sequence[i], id, frameNumber, ts);
+ setTimeStamp(sequence[i], id, frameNumber, ts, frameRateVote);
}
}
std::mt19937 mRandomEngine = std::mt19937(std::random_device()());
-
- class FakeStatsEventDelegate : public impl::TimeStats::StatsEventDelegate {
- public:
- FakeStatsEventDelegate() = default;
- ~FakeStatsEventDelegate() override = default;
-
- struct AStatsEvent* addStatsEventToPullData(AStatsEventList*) override {
- return mEvent;
- }
- void setStatsPullAtomCallback(int32_t atom_tag, AStatsManager_PullAtomMetadata*,
- AStatsManager_PullAtomCallback callback,
- void* cookie) override {
- mAtomTags.push_back(atom_tag);
- mCallback = callback;
- mCookie = cookie;
- }
-
- AStatsManager_PullAtomCallbackReturn makePullAtomCallback(int32_t atom_tag, void* cookie) {
- return (*mCallback)(atom_tag, nullptr, cookie);
- }
-
- MOCK_METHOD1(clearStatsPullAtomCallback, void(int32_t));
- MOCK_METHOD2(statsEventSetAtomId, void(AStatsEvent*, uint32_t));
- MOCK_METHOD2(statsEventWriteInt32, void(AStatsEvent*, int32_t));
- MOCK_METHOD2(statsEventWriteInt64, void(AStatsEvent*, int64_t));
- MOCK_METHOD2(statsEventWriteString8, void(AStatsEvent*, const char*));
- MOCK_METHOD3(statsEventWriteByteArray, void(AStatsEvent*, const uint8_t*, size_t));
- MOCK_METHOD1(statsEventBuild, void(AStatsEvent*));
-
- AStatsEvent* mEvent = AStatsEvent_obtain();
- std::vector<int32_t> mAtomTags;
- AStatsManager_PullAtomCallback mCallback = nullptr;
- void* mCookie = nullptr;
- };
- FakeStatsEventDelegate* mDelegate = new FakeStatsEventDelegate;
std::unique_ptr<TimeStats> mTimeStats =
- std::make_unique<impl::TimeStats>(std::unique_ptr<FakeStatsEventDelegate>(mDelegate),
- std::nullopt, std::nullopt);
+ std::make_unique<impl::TimeStats>(std::nullopt, std::nullopt);
};
std::string TimeStatsTest::inputCommand(InputCommand cmd, bool useProto) {
@@ -224,10 +199,12 @@
return (layerId < 0 ? "PopupWindow:b54fcd1#0" : "com.example.fake#") + std::to_string(layerId);
}
-void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts) {
+void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts,
+ TimeStats::SetFrameRateVote frameRateVote) {
switch (type) {
case TimeStamp::POST:
- ASSERT_NO_FATAL_FAILURE(mTimeStats->setPostTime(id, frameNumber, genLayerName(id), ts));
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPostTime(id, frameNumber, genLayerName(id), UID_0, ts));
break;
case TimeStamp::ACQUIRE:
ASSERT_NO_FATAL_FAILURE(mTimeStats->setAcquireTime(id, frameNumber, ts));
@@ -243,11 +220,13 @@
ASSERT_NO_FATAL_FAILURE(mTimeStats->setDesiredTime(id, frameNumber, ts));
break;
case TimeStamp::PRESENT:
- ASSERT_NO_FATAL_FAILURE(mTimeStats->setPresentTime(id, frameNumber, ts));
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->setPresentTime(id, frameNumber, ts, kRefreshRate0,
+ kRenderRate0, frameRateVote));
break;
case TimeStamp::PRESENT_FENCE:
ASSERT_NO_FATAL_FAILURE(
- mTimeStats->setPresentFence(id, frameNumber, std::make_shared<FenceTime>(ts)));
+ mTimeStats->setPresentFence(id, frameNumber, std::make_shared<FenceTime>(ts),
+ kRefreshRate0, kRenderRate0, frameRateVote));
break;
default:
ALOGD("Invalid timestamp type");
@@ -263,21 +242,6 @@
ASSERT_FALSE(mTimeStats->isEnabled());
}
-TEST_F(TimeStatsTest, setsCallbacksAfterBoot) {
- mTimeStats->onBootFinished();
- EXPECT_THAT(mDelegate->mAtomTags,
- UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- android::util::SURFACEFLINGER_STATS_LAYER_INFO));
-}
-
-TEST_F(TimeStatsTest, clearsCallbacksOnDestruction) {
- EXPECT_CALL(*mDelegate,
- clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
- EXPECT_CALL(*mDelegate,
- clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO));
- mTimeStats.reset();
-}
-
TEST_F(TimeStatsTest, canEnableAndDisableTimeStats) {
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
ASSERT_TRUE(mTimeStats->isEnabled());
@@ -349,6 +313,145 @@
EXPECT_THAT(result, HasSubstr(expectedResult));
}
+TEST_F(TimeStatsTest, canIncreaseJankyFramesForLayer) {
+ // this stat is not in the proto so verify by checking the string dump
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::SurfaceFlingerGpuDeadlineMissed, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::DisplayHAL, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::AppDeadlineMissed, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::SurfaceFlingerScheduling, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::PredictionError, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::AppDeadlineMissed | JankType::BufferStuffing, 1, 2,
+ 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::None, 1, 2, 3});
+
+ const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+ std::string expectedResult =
+ "displayRefreshRate = " + std::to_string(REFRESH_RATE_BUCKET_0) + " fps";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "renderRate = " + std::to_string(RENDER_RATE_BUCKET_0) + " fps";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "totalTimelineFrames = " + std::to_string(8);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "jankyFrames = " + std::to_string(7);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfLongCpuJankyFrames = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfLongGpuJankyFrames = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfUnattributedJankyFrames = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "appUnattributedJankyFrames = " + std::to_string(2);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfSchedulingJankyFrames = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfPredictionErrorJankyFrames = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "appBufferStuffingJankyFrames = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
+TEST_F(TimeStatsTest, canCaptureSetFrameRateVote) {
+ // this stat is not in the proto so verify by checking the string dump
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+
+ const auto frameRate60 = TimeStats::SetFrameRateVote{
+ .frameRate = 60.0f,
+ .frameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility::Default,
+ .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::ShouldBeSeamless,
+ };
+ const auto frameRate90 = TimeStats::SetFrameRateVote{
+ .frameRate = 90.0f,
+ .frameRateCompatibility =
+ TimeStats::SetFrameRateVote::FrameRateCompatibility::ExactOrMultiple,
+ .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::NotRequired,
+ };
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate60);
+ std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+ std::string expectedResult = "frameRate = 60.00";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "frameRateCompatibility = Default";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "seamlessness = ShouldBeSeamless";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000, frameRate90);
+ result = inputCommand(InputCommand::DUMP_ALL, FMT_STRING);
+ expectedResult = "frameRate = 90.00";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "frameRateCompatibility = ExactOrMultiple";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "seamlessness = NotRequired";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+
+ insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_0, 4, 4000000, frameRate60);
+ result = inputCommand(InputCommand::DUMP_ALL, FMT_STRING);
+ expectedResult = "frameRate = 60.00";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "frameRateCompatibility = Default";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "seamlessness = ShouldBeSeamless";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
+TEST_F(TimeStatsTest, canCaptureSetFrameRateVoteAfterZeroForLayer) {
+ // this stat is not in the proto so verify by checking the string dump
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+
+ const auto frameRate90 = TimeStats::SetFrameRateVote{
+ .frameRate = 90.0f,
+ .frameRateCompatibility =
+ TimeStats::SetFrameRateVote::FrameRateCompatibility::ExactOrMultiple,
+ .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::NotRequired,
+ };
+ const auto frameRateDefault = TimeStats::SetFrameRateVote{
+ .frameRate = 0.0f,
+ .frameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility::Default,
+ .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::ShouldBeSeamless,
+ };
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate90);
+ std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+ std::string expectedResult = "frameRate = 90.00";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "frameRateCompatibility = ExactOrMultiple";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "seamlessness = NotRequired";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000, frameRateDefault);
+ result = inputCommand(InputCommand::DUMP_ALL, FMT_STRING);
+ expectedResult = "frameRate = 90.00";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "frameRateCompatibility = ExactOrMultiple";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "seamlessness = NotRequired";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+
+ insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_0, 4, 4000000, frameRateDefault);
+ result = inputCommand(InputCommand::DUMP_ALL, FMT_STRING);
+ expectedResult = "frameRate = 90.00";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "frameRateCompatibility = ExactOrMultiple";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "seamlessness = NotRequired";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
TEST_F(TimeStatsTest, canIncreaseClientCompositionReusedFrames) {
// this stat is not in the proto so verify by checking the string dump
constexpr size_t CLIENT_COMPOSITION_REUSED_FRAMES = 2;
@@ -397,14 +500,10 @@
TEST_F(TimeStatsTest, canAverageFrameDuration) {
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
mTimeStats->setPowerMode(PowerMode::ON);
- mTimeStats
- ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count(),
- std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
- .count());
- mTimeStats
- ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count(),
- std::chrono::duration_cast<std::chrono::nanoseconds>(16ms)
- .count());
+ mTimeStats->recordFrameDuration(std::chrono::nanoseconds(1ms).count(),
+ std::chrono::nanoseconds(6ms).count());
+ mTimeStats->recordFrameDuration(std::chrono::nanoseconds(1ms).count(),
+ std::chrono::nanoseconds(16ms).count());
const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
EXPECT_THAT(result, HasSubstr("averageFrameDuration = 10.000 ms"));
@@ -412,22 +511,19 @@
TEST_F(TimeStatsTest, canAverageRenderEngineTimings) {
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
- mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms)
- .count(),
+ mTimeStats->recordRenderEngineDuration(std::chrono::nanoseconds(1ms).count(),
std::make_shared<FenceTime>(
std::chrono::duration_cast<
std::chrono::nanoseconds>(3ms)
.count()));
- mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(4ms)
- .count(),
- std::chrono::duration_cast<std::chrono::nanoseconds>(8ms)
- .count());
+ mTimeStats->recordRenderEngineDuration(std::chrono::nanoseconds(4ms).count(),
+ std::chrono::nanoseconds(8ms).count());
// Push a fake present fence to trigger flushing the RenderEngine timings.
mTimeStats->setPowerMode(PowerMode::ON);
- mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(
- std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count()));
+ mTimeStats->setPresentFenceGlobal(
+ std::make_shared<FenceTime>(std::chrono::nanoseconds(1ms).count()));
const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
EXPECT_THAT(result, HasSubstr("averageRenderEngineTiming = 3.000 ms"));
@@ -466,15 +562,11 @@
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
mTimeStats->setPowerMode(PowerMode::OFF);
- mTimeStats
- ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count(),
- std::chrono::duration_cast<std::chrono::nanoseconds>(5ms)
- .count());
+ mTimeStats->recordFrameDuration(std::chrono::nanoseconds(1ms).count(),
+ std::chrono::nanoseconds(5ms).count());
mTimeStats->setPowerMode(PowerMode::ON);
- mTimeStats
- ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(3ms).count(),
- std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
- .count());
+ mTimeStats->recordFrameDuration(std::chrono::nanoseconds(3ms).count(),
+ std::chrono::nanoseconds(6ms).count());
SFTimeStatsGlobalProto globalProto;
ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
@@ -488,17 +580,14 @@
TEST_F(TimeStatsTest, canInsertGlobalRenderEngineTiming) {
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
- mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms)
- .count(),
+ mTimeStats->recordRenderEngineDuration(std::chrono::nanoseconds(1ms).count(),
std::make_shared<FenceTime>(
std::chrono::duration_cast<
std::chrono::nanoseconds>(3ms)
.count()));
- mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(4ms)
- .count(),
- std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
- .count());
+ mTimeStats->recordRenderEngineDuration(std::chrono::nanoseconds(4ms).count(),
+ std::chrono::nanoseconds(6ms).count());
// First verify that flushing RenderEngine durations did not occur yet.
SFTimeStatsGlobalProto preFlushProto;
@@ -507,8 +596,8 @@
// Push a fake present fence to trigger flushing the RenderEngine timings.
mTimeStats->setPowerMode(PowerMode::ON);
- mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(
- std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count()));
+ mTimeStats->setPresentFenceGlobal(
+ std::make_shared<FenceTime>(std::chrono::nanoseconds(1ms).count()));
// Now we can verify that RenderEngine durations were flushed now.
SFTimeStatsGlobalProto postFlushProto;
@@ -743,14 +832,10 @@
ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementClientCompositionFrames());
ASSERT_NO_FATAL_FAILURE(mTimeStats->setPowerMode(PowerMode::ON));
- mTimeStats
- ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(3ms).count(),
- std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
- .count());
- mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(4ms)
- .count(),
- std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
- .count());
+ mTimeStats->recordFrameDuration(std::chrono::nanoseconds(3ms).count(),
+ std::chrono::nanoseconds(6ms).count());
+ mTimeStats->recordRenderEngineDuration(std::chrono::nanoseconds(4ms).count(),
+ std::chrono::nanoseconds(6ms).count());
ASSERT_NO_FATAL_FAILURE(
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(1000000)));
ASSERT_NO_FATAL_FAILURE(
@@ -779,16 +864,31 @@
ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementRefreshRateSwitches());
ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementCompositionStrategyChanges());
mTimeStats->setPowerMode(PowerMode::ON);
- mTimeStats
- ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count(),
- std::chrono::duration_cast<std::chrono::nanoseconds>(5ms)
- .count());
- mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(4ms)
- .count(),
- std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
- .count());
- mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(
- std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count()));
+ mTimeStats->recordFrameDuration(std::chrono::nanoseconds(1ms).count(),
+ std::chrono::nanoseconds(5ms).count());
+ mTimeStats->recordRenderEngineDuration(std::chrono::nanoseconds(4ms).count(),
+ std::chrono::nanoseconds(6ms).count());
+ mTimeStats->setPresentFenceGlobal(
+ std::make_shared<FenceTime>(std::chrono::nanoseconds(1ms).count()));
+
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::SurfaceFlingerGpuDeadlineMissed, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::DisplayHAL, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::AppDeadlineMissed, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::SurfaceFlingerScheduling, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::PredictionError, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::AppDeadlineMissed | JankType::BufferStuffing, 1, 2,
+ 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::None, 1, 2, 3});
+
EXPECT_TRUE(inputCommand(InputCommand::CLEAR, FMT_STRING).empty());
const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
@@ -797,6 +897,24 @@
EXPECT_THAT(result, HasSubstr("compositionStrategyChanges = 0"));
EXPECT_THAT(result, HasSubstr("averageFrameDuration = 0.000 ms"));
EXPECT_THAT(result, HasSubstr("averageRenderEngineTiming = 0.000 ms"));
+ std::string expectedResult = "totalTimelineFrames = " + std::to_string(0);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "jankyFrames = " + std::to_string(0);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfLongCpuJankyFrames = " + std::to_string(0);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfLongGpuJankyFrames = " + std::to_string(0);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfUnattributedJankyFrames = " + std::to_string(0);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "appUnattributedJankyFrames = " + std::to_string(0);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfSchedulingJankyFrames = " + std::to_string(0);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfPredictionErrorJankyFrames = " + std::to_string(0);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "appBufferStuffingJankyFrames = " + std::to_string(0);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
}
TEST_F(TimeStatsTest, canDumpWithMaxLayers) {
@@ -843,46 +961,49 @@
}
namespace {
-std::string buildExpectedHistogramBytestring(const std::vector<int32_t>& times,
- const std::vector<int32_t>& frameCounts) {
- util::ProtoOutputStream proto;
+FrameTimingHistogram buildExpectedHistogram(const std::vector<int32_t>& times,
+ const std::vector<int32_t>& frameCounts) {
+ FrameTimingHistogram histogram;
for (int i = 0; i < times.size(); i++) {
ALOGE("Writing time: %d", times[i]);
- proto.write(util::FIELD_TYPE_INT32 | util::FIELD_COUNT_REPEATED | 1 /* field id */,
- (int32_t)times[i]);
+ histogram.add_time_millis_buckets(times[i]);
ALOGE("Writing count: %d", frameCounts[i]);
- proto.write(util::FIELD_TYPE_INT64 | util::FIELD_COUNT_REPEATED | 2 /* field id */,
- (int64_t)frameCounts[i]);
+ histogram.add_frame_counts((int64_t)frameCounts[i]);
}
- std::string byteString;
- proto.serializeToString(&byteString);
- return byteString;
+ return histogram;
}
-
-std::string dumpByteStringHex(const std::string& str) {
- std::stringstream ss;
- ss << std::hex;
- for (const char& c : str) {
- ss << (int)c << " ";
- }
-
- return ss.str();
-}
-
} // namespace
-MATCHER_P2(BytesEq, bytes, size, "") {
- std::string expected;
- expected.append((const char*)bytes, size);
- std::string actual;
- actual.append((const char*)arg, size);
+MATCHER_P(HistogramEq, expected, "") {
+ *result_listener << "Histograms are not equal! \n";
- *result_listener << "Bytes are not equal! \n";
- *result_listener << "size: " << size << "\n";
- *result_listener << "expected: " << dumpByteStringHex(expected).c_str() << "\n";
- *result_listener << "actual: " << dumpByteStringHex(actual).c_str() << "\n";
+ if (arg.time_millis_buckets_size() != expected.time_millis_buckets_size()) {
+ *result_listener << "Time millis bucket are different sizes. Expected: "
+ << expected.time_millis_buckets_size() << ". Actual "
+ << arg.time_millis_buckets_size();
+ return false;
+ }
+ if (arg.frame_counts_size() != expected.frame_counts_size()) {
+ *result_listener << "Frame counts are different sizes. Expected: "
+ << expected.frame_counts_size() << ". Actual " << arg.frame_counts_size();
+ return false;
+ }
- return expected == actual;
+ for (int i = 0; i < expected.time_millis_buckets_size(); i++) {
+ if (arg.time_millis_buckets(i) != expected.time_millis_buckets(i)) {
+ *result_listener << "time_millis_bucket[" << i
+ << "] is different. Expected: " << expected.time_millis_buckets(i)
+ << ". Actual: " << arg.time_millis_buckets(i);
+ return false;
+ }
+ if (arg.frame_counts(i) != expected.frame_counts(i)) {
+ *result_listener << "frame_counts[" << i
+ << "] is different. Expected: " << expected.frame_counts(i)
+ << ". Actual: " << arg.frame_counts(i);
+ return false;
+ }
+ }
+ return true;
}
TEST_F(TimeStatsTest, globalStatsCallback) {
@@ -891,7 +1012,6 @@
constexpr size_t CLIENT_COMPOSITION_FRAMES = 3;
constexpr size_t DISPLAY_EVENT_CONNECTIONS = 14;
- mTimeStats->onBootFinished();
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
for (size_t i = 0; i < TOTAL_FRAMES; i++) {
@@ -904,6 +1024,8 @@
mTimeStats->incrementClientCompositionFrames();
}
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+
mTimeStats->recordDisplayEventConnectionCount(DISPLAY_EVENT_CONNECTIONS);
mTimeStats->setPowerMode(PowerMode::ON);
mTimeStats->recordFrameDuration(1000000, 3000000);
@@ -913,42 +1035,55 @@
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
- EXPECT_THAT(mDelegate->mAtomTags,
- UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- android::util::SURFACEFLINGER_STATS_LAYER_INFO));
- EXPECT_NE(nullptr, mDelegate->mCallback);
- EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::SurfaceFlingerGpuDeadlineMissed, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::DisplayHAL, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::AppDeadlineMissed, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::SurfaceFlingerScheduling, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::PredictionError, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::AppDeadlineMissed | JankType::BufferStuffing, 1, 2,
+ 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::BufferStuffing, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::None, 1, 2, 3});
- std::string expectedFrameDuration = buildExpectedHistogramBytestring({2}, {1});
- std::string expectedRenderEngineTiming = buildExpectedHistogramBytestring({1, 2}, {1, 1});
+ std::string pulledData;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10062 /*SURFACEFLINGER_STATS_GLOBAL_INFO*/, &pulledData));
- {
- InSequence seq;
- EXPECT_CALL(*mDelegate,
- statsEventSetAtomId(mDelegate->mEvent,
- android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
- EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, TOTAL_FRAMES));
- EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, MISSED_FRAMES));
- EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, CLIENT_COMPOSITION_FRAMES));
- EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, _));
- EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, 2));
- EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, DISPLAY_EVENT_CONNECTIONS));
- EXPECT_CALL(*mDelegate,
- statsEventWriteByteArray(mDelegate->mEvent,
- BytesEq((const uint8_t*)expectedFrameDuration.c_str(),
- expectedFrameDuration.size()),
- expectedFrameDuration.size()));
- EXPECT_CALL(*mDelegate,
- statsEventWriteByteArray(mDelegate->mEvent,
- BytesEq((const uint8_t*)
- expectedRenderEngineTiming.c_str(),
- expectedRenderEngineTiming.size()),
- expectedRenderEngineTiming.size()));
- EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
- }
- EXPECT_EQ(AStatsManager_PULL_SUCCESS,
- mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- mDelegate->mCookie));
+ android::surfaceflinger::SurfaceflingerStatsGlobalInfoWrapper atomList;
+ ASSERT_TRUE(atomList.ParseFromString(pulledData));
+ ASSERT_EQ(atomList.atom_size(), 1);
+ const android::surfaceflinger::SurfaceflingerStatsGlobalInfo& atom = atomList.atom(0);
+
+ EXPECT_EQ(atom.total_frames(), TOTAL_FRAMES);
+ EXPECT_EQ(atom.missed_frames(), MISSED_FRAMES);
+ EXPECT_EQ(atom.client_composition_frames(), CLIENT_COMPOSITION_FRAMES);
+ // Display on millis is not checked.
+ EXPECT_EQ(atom.animation_millis(), 2);
+ EXPECT_EQ(atom.event_connection_count(), DISPLAY_EVENT_CONNECTIONS);
+ EXPECT_THAT(atom.frame_duration(), HistogramEq(buildExpectedHistogram({2}, {1})));
+ EXPECT_THAT(atom.render_engine_timing(), HistogramEq(buildExpectedHistogram({1, 2}, {1, 1})));
+ EXPECT_EQ(atom.total_timeline_frames(), 9);
+ EXPECT_EQ(atom.total_janky_frames(), 7);
+ EXPECT_EQ(atom.total_janky_frames_with_long_cpu(), 1);
+ EXPECT_EQ(atom.total_janky_frames_with_long_gpu(), 1);
+ EXPECT_EQ(atom.total_janky_frames_sf_unattributed(), 1);
+ EXPECT_EQ(atom.total_janky_frames_app_unattributed(), 2);
+ EXPECT_EQ(atom.total_janky_frames_sf_scheduling(), 1);
+ EXPECT_EQ(atom.total_jank_frames_sf_prediction_error(), 1);
+ EXPECT_EQ(atom.total_jank_frames_app_buffer_stuffing(), 2);
+ EXPECT_EQ(atom.display_refresh_rate_bucket(), REFRESH_RATE_BUCKET_0);
+ EXPECT_THAT(atom.sf_deadline_misses(), HistogramEq(buildExpectedHistogram({1}, {7})));
+ EXPECT_THAT(atom.sf_prediction_errors(), HistogramEq(buildExpectedHistogram({2}, {7})));
+ EXPECT_EQ(atom.render_rate_bucket(), RENDER_RATE_BUCKET_0);
SFTimeStatsGlobalProto globalProto;
ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
@@ -957,6 +1092,46 @@
EXPECT_EQ(0, globalProto.missed_frames());
EXPECT_EQ(0, globalProto.client_composition_frames());
EXPECT_EQ(0, globalProto.present_to_present_size());
+
+ // also check dump-only stats: expect that global stats are indeed dropped but there should
+ // still be stats for the layer
+ const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+ std::string expectedResult = "totalTimelineFrames = " + std::to_string(0);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "totalTimelineFrames = " + std::to_string(9);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "jankyFrames = " + std::to_string(0);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "jankyFrames = " + std::to_string(7);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfLongCpuJankyFrames = " + std::to_string(0);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfLongCpuJankyFrames = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfLongGpuJankyFrames = " + std::to_string(0);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfLongGpuJankyFrames = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfUnattributedJankyFrames = " + std::to_string(0);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfUnattributedJankyFrames = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "appUnattributedJankyFrames = " + std::to_string(0);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "appUnattributedJankyFrames = " + std::to_string(2);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfSchedulingJankyFrames = " + std::to_string(0);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfSchedulingJankyFrames = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfPredictionErrorJankyFrames = " + std::to_string(0);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfPredictionErrorJankyFrames = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "appBufferStuffingJankyFrames = " + std::to_string(0);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "appBufferStuffingJankyFrames = " + std::to_string(2);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
}
TEST_F(TimeStatsTest, layerStatsCallback_pullsAllAndClears) {
@@ -964,8 +1139,6 @@
constexpr size_t BAD_DESIRED_PRESENT_FRAMES = 3;
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
- mTimeStats->onBootFinished();
-
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
for (size_t i = 0; i < LATE_ACQUIRE_FRAMES; i++) {
mTimeStats->incrementLatchSkipped(LAYER_ID_0, TimeStats::LatchSkipReason::LateAcquire);
@@ -973,112 +1146,123 @@
for (size_t i = 0; i < BAD_DESIRED_PRESENT_FRAMES; i++) {
mTimeStats->incrementBadDesiredPresent(LAYER_ID_0);
}
- insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+ const auto frameRate60 = TimeStats::SetFrameRateVote{
+ .frameRate = 60.0f,
+ .frameRateCompatibility =
+ TimeStats::SetFrameRateVote::FrameRateCompatibility::ExactOrMultiple,
+ .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::NotRequired,
+ };
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate60);
- EXPECT_THAT(mDelegate->mAtomTags,
- UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- android::util::SURFACEFLINGER_STATS_LAYER_INFO));
- EXPECT_NE(nullptr, mDelegate->mCallback);
- EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::SurfaceFlingerGpuDeadlineMissed, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::DisplayHAL, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::AppDeadlineMissed, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::SurfaceFlingerScheduling, 1, 2, 2});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::PredictionError, 1, 2, 2});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::AppDeadlineMissed | JankType::BufferStuffing, 1, 2,
+ 2});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ JankType::None, 1, 2, 3});
- std::string expectedPresentToPresent = buildExpectedHistogramBytestring({1}, {1});
- std::string expectedPostToPresent = buildExpectedHistogramBytestring({4}, {1});
- std::string expectedAcquireToPresent = buildExpectedHistogramBytestring({3}, {1});
- std::string expectedLatchToPresent = buildExpectedHistogramBytestring({2}, {1});
- std::string expectedDesiredToPresent = buildExpectedHistogramBytestring({1}, {1});
- std::string expectedPostToAcquire = buildExpectedHistogramBytestring({1}, {1});
- {
- InSequence seq;
- EXPECT_CALL(*mDelegate,
- statsEventSetAtomId(mDelegate->mEvent,
- android::util::SURFACEFLINGER_STATS_LAYER_INFO));
- EXPECT_CALL(*mDelegate,
- statsEventWriteString8(mDelegate->mEvent,
- StrEq(genLayerName(LAYER_ID_0).c_str())));
- EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, 1));
- EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, 0));
- EXPECT_CALL(*mDelegate,
- statsEventWriteByteArray(mDelegate->mEvent,
- BytesEq((const uint8_t*)
- expectedPresentToPresent.c_str(),
- expectedPresentToPresent.size()),
- expectedPresentToPresent.size()));
- EXPECT_CALL(*mDelegate,
- statsEventWriteByteArray(mDelegate->mEvent,
- BytesEq((const uint8_t*)expectedPostToPresent.c_str(),
- expectedPostToPresent.size()),
- expectedPostToPresent.size()));
- EXPECT_CALL(*mDelegate,
- statsEventWriteByteArray(mDelegate->mEvent,
- BytesEq((const uint8_t*)
- expectedAcquireToPresent.c_str(),
- expectedAcquireToPresent.size()),
- expectedAcquireToPresent.size()));
- EXPECT_CALL(*mDelegate,
- statsEventWriteByteArray(mDelegate->mEvent,
- BytesEq((const uint8_t*)expectedLatchToPresent.c_str(),
- expectedLatchToPresent.size()),
- expectedLatchToPresent.size()));
- EXPECT_CALL(*mDelegate,
- statsEventWriteByteArray(mDelegate->mEvent,
- BytesEq((const uint8_t*)
- expectedDesiredToPresent.c_str(),
- expectedDesiredToPresent.size()),
- expectedDesiredToPresent.size()));
- EXPECT_CALL(*mDelegate,
- statsEventWriteByteArray(mDelegate->mEvent,
- BytesEq((const uint8_t*)expectedPostToAcquire.c_str(),
- expectedPostToAcquire.size()),
- expectedPostToAcquire.size()));
- EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, LATE_ACQUIRE_FRAMES));
- EXPECT_CALL(*mDelegate,
- statsEventWriteInt64(mDelegate->mEvent, BAD_DESIRED_PRESENT_FRAMES));
- EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
- }
- EXPECT_EQ(AStatsManager_PULL_SUCCESS,
- mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
- mDelegate->mCookie));
+ std::string pulledData;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+
+ android::surfaceflinger::SurfaceflingerStatsLayerInfoWrapper atomList;
+ ASSERT_TRUE(atomList.ParseFromString(pulledData));
+ ASSERT_EQ(atomList.atom_size(), 1);
+ const android::surfaceflinger::SurfaceflingerStatsLayerInfo& atom = atomList.atom(0);
+
+ EXPECT_EQ(atom.layer_name(), genLayerName(LAYER_ID_0));
+ EXPECT_EQ(atom.total_frames(), 1);
+ EXPECT_EQ(atom.dropped_frames(), 0);
+ EXPECT_THAT(atom.present_to_present(), HistogramEq(buildExpectedHistogram({1}, {1})));
+ EXPECT_THAT(atom.post_to_present(), HistogramEq(buildExpectedHistogram({4}, {1})));
+ EXPECT_THAT(atom.acquire_to_present(), HistogramEq(buildExpectedHistogram({3}, {1})));
+ EXPECT_THAT(atom.latch_to_present(), HistogramEq(buildExpectedHistogram({2}, {1})));
+ EXPECT_THAT(atom.desired_to_present(), HistogramEq(buildExpectedHistogram({1}, {1})));
+ EXPECT_THAT(atom.post_to_acquire(), HistogramEq(buildExpectedHistogram({1}, {1})));
+ EXPECT_EQ(atom.late_acquire_frames(), LATE_ACQUIRE_FRAMES);
+ EXPECT_EQ(atom.bad_desired_present_frames(), BAD_DESIRED_PRESENT_FRAMES);
+ EXPECT_EQ(atom.uid(), UID_0);
+ EXPECT_EQ(atom.total_timeline_frames(), 8);
+ EXPECT_EQ(atom.total_janky_frames(), 7);
+ EXPECT_EQ(atom.total_janky_frames_with_long_cpu(), 1);
+ EXPECT_EQ(atom.total_janky_frames_with_long_gpu(), 1);
+ EXPECT_EQ(atom.total_janky_frames_sf_unattributed(), 1);
+ EXPECT_EQ(atom.total_janky_frames_app_unattributed(), 2);
+ EXPECT_EQ(atom.total_janky_frames_sf_scheduling(), 1);
+ EXPECT_EQ(atom.total_jank_frames_sf_prediction_error(), 1);
+ EXPECT_EQ(atom.total_jank_frames_app_buffer_stuffing(), 1);
+ EXPECT_EQ(atom.display_refresh_rate_bucket(), REFRESH_RATE_BUCKET_0);
+ EXPECT_EQ(atom.render_rate_bucket(), RENDER_RATE_BUCKET_0);
+ EXPECT_THAT(atom.set_frame_rate_vote().frame_rate(), testing::FloatEq(frameRate60.frameRate));
+ EXPECT_EQ((int)atom.set_frame_rate_vote().frame_rate_compatibility(),
+ (int)frameRate60.frameRateCompatibility);
+ EXPECT_EQ((int)atom.set_frame_rate_vote().seamlessness(), (int)frameRate60.seamlessness);
+ EXPECT_THAT(atom.app_deadline_misses(), HistogramEq(buildExpectedHistogram({3, 2}, {4, 3})));
SFTimeStatsGlobalProto globalProto;
ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
EXPECT_EQ(0, globalProto.stats_size());
+
+ // also check dump-only stats: expect that layer stats are indeed dropped but there should still
+ // be global stats
+ const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+ std::string expectedResult = "totalTimelineFrames = " + std::to_string(8);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "jankyFrames = " + std::to_string(7);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfLongCpuJankyFrames = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfLongGpuJankyFrames = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfUnattributedJankyFrames = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "appUnattributedJankyFrames = " + std::to_string(2);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfSchedulingJankyFrames = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfPredictionErrorJankyFrames = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "appBufferStuffingJankyFrames = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+
+ std::string expectedMissing = "uid = " + std::to_string(UID_0);
+ EXPECT_THAT(result, Not(HasSubstr(expectedMissing)));
}
TEST_F(TimeStatsTest, layerStatsCallback_pullsMultipleLayers) {
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
- mTimeStats->onBootFinished();
-
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 1, 2000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 3000000);
- EXPECT_THAT(mDelegate->mAtomTags,
- UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- android::util::SURFACEFLINGER_STATS_LAYER_INFO));
- EXPECT_NE(nullptr, mDelegate->mCallback);
- EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+ std::string pulledData;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
- EXPECT_CALL(*mDelegate,
- statsEventSetAtomId(mDelegate->mEvent,
- android::util::SURFACEFLINGER_STATS_LAYER_INFO))
- .Times(2);
- EXPECT_CALL(*mDelegate,
- statsEventWriteString8(mDelegate->mEvent, StrEq(genLayerName(LAYER_ID_0).c_str())));
- EXPECT_CALL(*mDelegate,
- statsEventWriteString8(mDelegate->mEvent, StrEq(genLayerName(LAYER_ID_1).c_str())));
- EXPECT_EQ(AStatsManager_PULL_SUCCESS,
- mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
- mDelegate->mCookie));
+ android::surfaceflinger::SurfaceflingerStatsLayerInfoWrapper atomList;
+ ASSERT_TRUE(atomList.ParseFromString(pulledData));
+ ASSERT_EQ(atomList.atom_size(), 2);
+ std::vector<std::string> actualLayerNames = {atomList.atom(0).layer_name(),
+ atomList.atom(1).layer_name()};
+ EXPECT_THAT(actualLayerNames,
+ UnorderedElementsAre(genLayerName(LAYER_ID_0), genLayerName(LAYER_ID_1)));
}
TEST_F(TimeStatsTest, layerStatsCallback_pullsMultipleBuckets) {
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
- mTimeStats->onBootFinished();
-
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 4000000);
@@ -1088,102 +1272,53 @@
mTimeStats->setPowerMode(PowerMode::ON);
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
- EXPECT_THAT(mDelegate->mAtomTags,
- UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- android::util::SURFACEFLINGER_STATS_LAYER_INFO));
- EXPECT_NE(nullptr, mDelegate->mCallback);
- EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
- EXPECT_THAT(mDelegate->mAtomTags,
- UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- android::util::SURFACEFLINGER_STATS_LAYER_INFO));
- std::string expectedPresentToPresent = buildExpectedHistogramBytestring({1, 2}, {2, 1});
- {
- InSequence seq;
- EXPECT_CALL(*mDelegate,
- statsEventWriteByteArray(mDelegate->mEvent,
- BytesEq((const uint8_t*)
- expectedPresentToPresent.c_str(),
- expectedPresentToPresent.size()),
- expectedPresentToPresent.size()));
- EXPECT_CALL(*mDelegate, statsEventWriteByteArray(mDelegate->mEvent, _, _))
- .Times(AnyNumber());
- }
- EXPECT_EQ(AStatsManager_PULL_SUCCESS,
- mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
- mDelegate->mCookie));
+ std::string pulledData;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+
+ android::surfaceflinger::SurfaceflingerStatsLayerInfoWrapper atomList;
+ ASSERT_TRUE(atomList.ParseFromString(pulledData));
+ ASSERT_EQ(atomList.atom_size(), 1);
+ const android::surfaceflinger::SurfaceflingerStatsLayerInfo& atom = atomList.atom(0);
+ EXPECT_THAT(atom.present_to_present(), HistogramEq(buildExpectedHistogram({1, 2}, {2, 1})));
}
TEST_F(TimeStatsTest, layerStatsCallback_limitsHistogramBuckets) {
- mDelegate = new FakeStatsEventDelegate;
- mTimeStats =
- std::make_unique<impl::TimeStats>(std::unique_ptr<FakeStatsEventDelegate>(mDelegate),
- std::nullopt, 1);
+ mTimeStats = std::make_unique<impl::TimeStats>(std::nullopt, 1);
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
- mTimeStats->onBootFinished();
-
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 4000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 5000000);
- EXPECT_THAT(mDelegate->mAtomTags,
- UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- android::util::SURFACEFLINGER_STATS_LAYER_INFO));
- EXPECT_NE(nullptr, mDelegate->mCallback);
- EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+ std::string pulledData;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
- EXPECT_THAT(mDelegate->mAtomTags,
- UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- android::util::SURFACEFLINGER_STATS_LAYER_INFO));
- std::string expectedPresentToPresent = buildExpectedHistogramBytestring({1}, {2});
- {
- InSequence seq;
- EXPECT_CALL(*mDelegate,
- statsEventWriteByteArray(mDelegate->mEvent,
- BytesEq((const uint8_t*)
- expectedPresentToPresent.c_str(),
- expectedPresentToPresent.size()),
- expectedPresentToPresent.size()));
- EXPECT_CALL(*mDelegate, statsEventWriteByteArray(mDelegate->mEvent, _, _))
- .Times(AnyNumber());
- }
- EXPECT_EQ(AStatsManager_PULL_SUCCESS,
- mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
- mDelegate->mCookie));
+ android::surfaceflinger::SurfaceflingerStatsLayerInfoWrapper atomList;
+ ASSERT_TRUE(atomList.ParseFromString(pulledData));
+ ASSERT_EQ(atomList.atom_size(), 1);
+ const android::surfaceflinger::SurfaceflingerStatsLayerInfo& atom = atomList.atom(0);
+ EXPECT_THAT(atom.present_to_present(), HistogramEq(buildExpectedHistogram({1}, {2})));
}
TEST_F(TimeStatsTest, layerStatsCallback_limitsLayers) {
- mDelegate = new FakeStatsEventDelegate;
- mTimeStats =
- std::make_unique<impl::TimeStats>(std::unique_ptr<FakeStatsEventDelegate>(mDelegate), 1,
- std::nullopt);
+ mTimeStats = std::make_unique<impl::TimeStats>(1, std::nullopt);
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
- mTimeStats->onBootFinished();
-
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 1, 2000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 3000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 4, 5000000);
- EXPECT_THAT(mDelegate->mAtomTags,
- UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- android::util::SURFACEFLINGER_STATS_LAYER_INFO));
- EXPECT_NE(nullptr, mDelegate->mCallback);
- EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+ std::string pulledData;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
- EXPECT_CALL(*mDelegate,
- statsEventSetAtomId(mDelegate->mEvent,
- android::util::SURFACEFLINGER_STATS_LAYER_INFO))
- .Times(1);
- EXPECT_CALL(*mDelegate,
- statsEventWriteString8(mDelegate->mEvent, StrEq(genLayerName(LAYER_ID_1).c_str())));
- EXPECT_EQ(AStatsManager_PULL_SUCCESS,
- mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
- mDelegate->mCookie));
+ android::surfaceflinger::SurfaceflingerStatsLayerInfoWrapper atomList;
+ ASSERT_TRUE(atomList.ParseFromString(pulledData));
+ ASSERT_EQ(atomList.atom_size(), 1);
+ EXPECT_EQ(atomList.atom(0).layer_name(), genLayerName(LAYER_ID_1));
}
TEST_F(TimeStatsTest, canSurviveMonkey) {
@@ -1209,7 +1344,7 @@
TimeStamp type = static_cast<TimeStamp>(genRandomInt32(TIME_STAMP_BEGIN, TIME_STAMP_END));
const int32_t ts = genRandomInt32(1, 1000000000);
ALOGV("type[%d], layerId[%d], frameNumber[%d], ts[%d]", type, layerId, frameNumber, ts);
- setTimeStamp(type, layerId, frameNumber, ts);
+ setTimeStamp(type, layerId, frameNumber, ts, {});
}
}
@@ -1217,4 +1352,4 @@
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 2a48a22..7c431a0 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
#undef LOG_TAG
#define LOG_TAG "CompositionTest"
@@ -31,10 +28,9 @@
#include "TestableScheduler.h"
#include "TestableSurfaceFlinger.h"
-#include "mock/MockDispSync.h"
-#include "mock/MockEventControlThread.h"
#include "mock/MockEventThread.h"
#include "mock/MockMessageQueue.h"
+#include "mock/MockVsyncController.h"
namespace android {
@@ -66,22 +62,20 @@
EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*eventThread, createEventConnection(_, _))
- .WillOnce(Return(
- new EventThreadConnection(eventThread.get(), ResyncCallback(),
- ISurfaceComposer::eConfigChangedSuppress)));
+ .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+ ResyncCallback())));
EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
- .WillOnce(Return(
- new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
- ISurfaceComposer::eConfigChangedSuppress)));
+ .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+ ResyncCallback())));
- EXPECT_CALL(*mPrimaryDispSync, computeNextRefresh(0, _)).WillRepeatedly(Return(0));
- EXPECT_CALL(*mPrimaryDispSync, getPeriod())
- .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
+ EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*mVSyncTracker, currentPeriod())
+ .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
- mFlinger.setupScheduler(std::unique_ptr<mock::DispSync>(mPrimaryDispSync),
- std::make_unique<mock::EventControlThread>(),
+ mFlinger.setupScheduler(std::unique_ptr<mock::VsyncController>(mVsyncController),
+ std::unique_ptr<mock::VSyncTracker>(mVSyncTracker),
std::move(eventThread), std::move(sfEventThread));
}
@@ -89,10 +83,10 @@
TestableSurfaceFlinger mFlinger;
std::unique_ptr<mock::EventThread> mEventThread = std::make_unique<mock::EventThread>();
- mock::EventControlThread* mEventControlThread = new mock::EventControlThread();
mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
- mock::DispSync* mPrimaryDispSync = new mock::DispSync();
+ mock::VsyncController* mVsyncController = new mock::VsyncController();
+ mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker();
struct TransactionInfo {
Vector<ComposerState> states;
@@ -100,43 +94,51 @@
uint32_t flags = 0;
sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
InputWindowCommands inputWindowCommands;
- int64_t desiredPresentTime = -1;
+ int64_t desiredPresentTime = 0;
+ bool isAutoTimestamp = true;
+ FrameTimelineInfo frameTimelineInfo;
client_cache_t uncacheBuffer;
+ uint64_t id = static_cast<uint64_t>(-1);
+ static_assert(0xffffffffffffffff == static_cast<uint64_t>(-1));
};
void checkEqual(TransactionInfo info, SurfaceFlinger::TransactionState state) {
- EXPECT_EQ(0, info.states.size());
- EXPECT_EQ(0, state.states.size());
+ EXPECT_EQ(0u, info.states.size());
+ EXPECT_EQ(0u, state.states.size());
- EXPECT_EQ(0, info.displays.size());
- EXPECT_EQ(0, state.displays.size());
+ EXPECT_EQ(0u, info.displays.size());
+ EXPECT_EQ(0u, state.displays.size());
EXPECT_EQ(info.flags, state.flags);
EXPECT_EQ(info.desiredPresentTime, state.desiredPresentTime);
}
void setupSingle(TransactionInfo& transaction, uint32_t flags, bool syncInputWindows,
- int64_t desiredPresentTime) {
+ int64_t desiredPresentTime, bool isAutoTimestamp,
+ const FrameTimelineInfo& frameTimelineInfo) {
mTransactionNumber++;
transaction.flags |= flags; // ISurfaceComposer::eSynchronous;
transaction.inputWindowCommands.syncInputWindows = syncInputWindows;
transaction.desiredPresentTime = desiredPresentTime;
+ transaction.isAutoTimestamp = isAutoTimestamp;
+ transaction.frameTimelineInfo = frameTimelineInfo;
}
void NotPlacedOnTransactionQueue(uint32_t flags, bool syncInputWindows) {
- ASSERT_EQ(0, mFlinger.getTransactionQueue().size());
+ ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
// called in SurfaceFlinger::signalTransaction
EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
- EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_)).WillOnce(Return(systemTime()));
TransactionInfo transaction;
setupSingle(transaction, flags, syncInputWindows,
- /*desiredPresentTime*/ -1);
+ /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
+ FrameTimelineInfo{});
nsecs_t applicationTime = systemTime();
- mFlinger.setTransactionState(transaction.states, transaction.displays, transaction.flags,
+ mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states,
+ transaction.displays, transaction.flags,
transaction.applyToken, transaction.inputWindowCommands,
- transaction.desiredPresentTime, transaction.uncacheBuffer,
- mHasListenerCallbacks, mCallbacks);
+ transaction.desiredPresentTime, transaction.isAutoTimestamp,
+ transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+ transaction.id);
- // This transaction should not have been placed on the transaction queue.
// If transaction is synchronous or syncs input windows, SF
// applyTransactionState should time out (5s) wating for SF to commit
// the transaction or to receive a signal that syncInputWindows has
@@ -147,93 +149,102 @@
} else {
EXPECT_LE(returnedTime, applicationTime + s2ns(5));
}
+ // Each transaction should have been placed on the transaction queue
auto transactionQueue = mFlinger.getTransactionQueue();
- EXPECT_EQ(0, transactionQueue.size());
+ EXPECT_EQ(1u, transactionQueue.size());
}
void PlaceOnTransactionQueue(uint32_t flags, bool syncInputWindows) {
- ASSERT_EQ(0, mFlinger.getTransactionQueue().size());
+ ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
// called in SurfaceFlinger::signalTransaction
EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
// first check will see desired present time has not passed,
// but afterwards it will look like the desired present time has passed
nsecs_t time = systemTime();
- EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_))
- .WillOnce(Return(time + nsecs_t(5 * 1e8)));
TransactionInfo transaction;
setupSingle(transaction, flags, syncInputWindows,
- /*desiredPresentTime*/ time + s2ns(1));
+ /*desiredPresentTime*/ time + s2ns(1), false, FrameTimelineInfo{});
nsecs_t applicationSentTime = systemTime();
- mFlinger.setTransactionState(transaction.states, transaction.displays, transaction.flags,
+ mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states,
+ transaction.displays, transaction.flags,
transaction.applyToken, transaction.inputWindowCommands,
- transaction.desiredPresentTime, transaction.uncacheBuffer,
- mHasListenerCallbacks, mCallbacks);
+ transaction.desiredPresentTime, transaction.isAutoTimestamp,
+ transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+ transaction.id);
nsecs_t returnedTime = systemTime();
- EXPECT_LE(returnedTime, applicationSentTime + s2ns(5));
+ if ((flags & ISurfaceComposer::eSynchronous) || syncInputWindows) {
+ EXPECT_GE(systemTime(), applicationSentTime + s2ns(5));
+ } else {
+ EXPECT_LE(returnedTime, applicationSentTime + s2ns(5));
+ }
// This transaction should have been placed on the transaction queue
auto transactionQueue = mFlinger.getTransactionQueue();
- EXPECT_EQ(1, transactionQueue.size());
+ EXPECT_EQ(1u, transactionQueue.size());
}
void BlockedByPriorTransaction(uint32_t flags, bool syncInputWindows) {
- ASSERT_EQ(0, mFlinger.getTransactionQueue().size());
+ ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
// called in SurfaceFlinger::signalTransaction
nsecs_t time = systemTime();
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
- EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_))
- .WillOnce(Return(time + nsecs_t(5 * 1e8)));
+ if (!syncInputWindows) {
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(2);
+ } else {
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+ }
// transaction that should go on the pending thread
TransactionInfo transactionA;
setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false,
- /*desiredPresentTime*/ time + s2ns(1));
+ /*desiredPresentTime*/ time + s2ns(1), false, FrameTimelineInfo{});
// transaction that would not have gone on the pending thread if not
// blocked
TransactionInfo transactionB;
setupSingle(transactionB, flags, syncInputWindows,
- /*desiredPresentTime*/ -1);
+ /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
+ FrameTimelineInfo{});
nsecs_t applicationSentTime = systemTime();
- mFlinger.setTransactionState(transactionA.states, transactionA.displays, transactionA.flags,
+ mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
+ transactionA.displays, transactionA.flags,
transactionA.applyToken, transactionA.inputWindowCommands,
- transactionA.desiredPresentTime, transactionA.uncacheBuffer,
- mHasListenerCallbacks, mCallbacks);
+ transactionA.desiredPresentTime, transactionA.isAutoTimestamp,
+ transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+ transactionA.id);
// This thread should not have been blocked by the above transaction
// (5s is the timeout period that applyTransactionState waits for SF to
// commit the transaction)
EXPECT_LE(systemTime(), applicationSentTime + s2ns(5));
+ // transaction that would goes to pending transaciton queue.
+ mFlinger.flushTransactionQueues();
applicationSentTime = systemTime();
- mFlinger.setTransactionState(transactionB.states, transactionB.displays, transactionB.flags,
+ mFlinger.setTransactionState(transactionB.frameTimelineInfo, transactionB.states,
+ transactionB.displays, transactionB.flags,
transactionB.applyToken, transactionB.inputWindowCommands,
- transactionB.desiredPresentTime, transactionB.uncacheBuffer,
- mHasListenerCallbacks, mCallbacks);
+ transactionB.desiredPresentTime, transactionB.isAutoTimestamp,
+ transactionB.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+ transactionB.id);
// this thread should have been blocked by the above transaction
// if this is an animation, this thread should be blocked for 5s
// in setTransactionState waiting for transactionA to flush. Otherwise,
// the transaction should be placed on the pending queue
- if (flags & ISurfaceComposer::eAnimation) {
+ if (flags & (ISurfaceComposer::eAnimation | ISurfaceComposer::eSynchronous) ||
+ syncInputWindows) {
EXPECT_GE(systemTime(), applicationSentTime + s2ns(5));
} else {
EXPECT_LE(systemTime(), applicationSentTime + s2ns(5));
}
- // check that there is one binder on the pending queue.
- auto transactionQueue = mFlinger.getTransactionQueue();
- EXPECT_EQ(1, transactionQueue.size());
+ // transaction that would goes to pending transaciton queue.
+ mFlinger.flushTransactionQueues();
- auto& [applyToken, transactionStates] = *(transactionQueue.begin());
- EXPECT_EQ(2, transactionStates.size());
-
- auto& transactionStateA = transactionStates.front();
- transactionStates.pop();
- checkEqual(transactionA, transactionStateA);
- auto& transactionStateB = transactionStates.front();
- checkEqual(transactionB, transactionStateB);
+ // check that the transaction was applied.
+ auto transactionQueue = mFlinger.getPendingTransactionQueue();
+ EXPECT_EQ(0u, transactionQueue.size());
}
bool mHasListenerCallbacks = false;
@@ -242,29 +253,23 @@
};
TEST_F(TransactionApplicationTest, Flush_RemovesFromQueue) {
- ASSERT_EQ(0, mFlinger.getTransactionQueue().size());
+ ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
// called in SurfaceFlinger::signalTransaction
EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
- // nsecs_t time = systemTime();
- EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_))
- .WillOnce(Return(nsecs_t(5 * 1e8)))
- .WillOnce(Return(s2ns(2)));
TransactionInfo transactionA; // transaction to go on pending queue
setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false,
- /*desiredPresentTime*/ s2ns(1));
- mFlinger.setTransactionState(transactionA.states, transactionA.displays, transactionA.flags,
- transactionA.applyToken, transactionA.inputWindowCommands,
- transactionA.desiredPresentTime, transactionA.uncacheBuffer,
- mHasListenerCallbacks, mCallbacks);
+ /*desiredPresentTime*/ s2ns(1), false, FrameTimelineInfo{});
+ mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
+ transactionA.displays, transactionA.flags, transactionA.applyToken,
+ transactionA.inputWindowCommands, transactionA.desiredPresentTime,
+ transactionA.isAutoTimestamp, transactionA.uncacheBuffer,
+ mHasListenerCallbacks, mCallbacks, transactionA.id);
auto& transactionQueue = mFlinger.getTransactionQueue();
- ASSERT_EQ(1, transactionQueue.size());
+ ASSERT_EQ(1u, transactionQueue.size());
- auto& [applyToken, transactionStates] = *(transactionQueue.begin());
- ASSERT_EQ(1, transactionStates.size());
-
- auto& transactionState = transactionStates.front();
+ auto& transactionState = transactionQueue.front();
checkEqual(transactionA, transactionState);
// because flushing uses the cached expected present time, we send an empty
@@ -272,15 +277,16 @@
// different process) to re-query and reset the cached expected present time
TransactionInfo empty;
empty.applyToken = sp<IBinder>();
- mFlinger.setTransactionState(empty.states, empty.displays, empty.flags, empty.applyToken,
- empty.inputWindowCommands, empty.desiredPresentTime,
- empty.uncacheBuffer, mHasListenerCallbacks, mCallbacks);
+ mFlinger.setTransactionState(empty.frameTimelineInfo, empty.states, empty.displays, empty.flags,
+ empty.applyToken, empty.inputWindowCommands,
+ empty.desiredPresentTime, empty.isAutoTimestamp,
+ empty.uncacheBuffer, mHasListenerCallbacks, mCallbacks, empty.id);
// flush transaction queue should flush as desiredPresentTime has
// passed
mFlinger.flushTransactionQueues();
- EXPECT_EQ(0, transactionQueue.size());
+ EXPECT_EQ(0u, transactionQueue.size());
}
TEST_F(TransactionApplicationTest, NotPlacedOnTransactionQueue_Synchronous) {
@@ -325,6 +331,3 @@
EXPECT_EQ(nullptr, ret.promote().get());
}
} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
new file mode 100644
index 0000000..25001d3
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/SurfaceComposerClient.h>
+#include <log/log.h>
+#include <renderengine/ExternalTexture.h>
+#include <renderengine/mock/RenderEngine.h>
+#include <utils/String8.h>
+
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/MockEventThread.h"
+#include "mock/MockVsyncController.h"
+
+namespace android {
+
+using testing::_;
+using testing::Mock;
+using testing::Return;
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+using PresentState = frametimeline::SurfaceFrame::PresentState;
+
+class TransactionFrameTracerTest : public testing::Test {
+public:
+ TransactionFrameTracerTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+ setupScheduler();
+ setupComposer(0);
+ }
+
+ ~TransactionFrameTracerTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+
+ sp<BufferStateLayer> createBufferStateLayer() {
+ sp<Client> client;
+ LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 100, 100, 0,
+ LayerMetadata());
+ return new BufferStateLayer(args);
+ }
+
+ void commitTransaction(Layer* layer) {
+ auto c = layer->getCurrentState();
+ layer->commitTransaction(c);
+ }
+
+ void setupScheduler() {
+ auto eventThread = std::make_unique<mock::EventThread>();
+ auto sfEventThread = std::make_unique<mock::EventThread>();
+
+ EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
+ EXPECT_CALL(*eventThread, createEventConnection(_, _))
+ .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+ ResyncCallback())));
+
+ EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
+ EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
+ .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+ ResyncCallback())));
+
+ auto vsyncController = std::make_unique<mock::VsyncController>();
+ auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
+
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*vsyncTracker, currentPeriod())
+ .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
+ std::move(eventThread), std::move(sfEventThread));
+ }
+
+ void setupComposer(uint32_t virtualDisplayCount) {
+ mComposer = new Hwc2::mock::Composer();
+ EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount));
+ mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
+
+ Mock::VerifyAndClear(mComposer);
+ }
+
+ TestableSurfaceFlinger mFlinger;
+ Hwc2::mock::Composer* mComposer = nullptr;
+ renderengine::mock::RenderEngine mRenderEngine;
+ FenceToFenceTimeMap fenceFactory;
+ client_cache_t mClientCache;
+
+ void BLASTTransactionSendsFrameTracerEvents() {
+ sp<BufferStateLayer> layer = createBufferStateLayer();
+
+ sp<Fence> fence(new Fence());
+ const auto buffer = std::make_shared<
+ renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+ 1, 0),
+ mRenderEngine, false);
+ int32_t layerId = layer->getSequence();
+ uint64_t bufferId = buffer->getBuffer()->getId();
+ uint64_t frameNumber = 5;
+ nsecs_t dequeueTime = 10;
+ nsecs_t postTime = 20;
+ EXPECT_CALL(*mFlinger.getFrameTracer(), traceNewLayer(layerId, "buffer-state-layer"));
+ EXPECT_CALL(*mFlinger.getFrameTracer(),
+ traceTimestamp(layerId, bufferId, frameNumber, dequeueTime,
+ FrameTracer::FrameEvent::DEQUEUE, /*duration*/ 0));
+ EXPECT_CALL(*mFlinger.getFrameTracer(),
+ traceTimestamp(layerId, bufferId, frameNumber, postTime,
+ FrameTracer::FrameEvent::QUEUE, /*duration*/ 0));
+ layer->setBuffer(buffer, fence, postTime, /*desiredPresentTime*/ 30, false, mClientCache,
+ frameNumber, dequeueTime, FrameTimelineInfo{},
+ nullptr /* releaseBufferCallback */);
+
+ commitTransaction(layer.get());
+ bool computeVisisbleRegions;
+ nsecs_t latchTime = 25;
+ EXPECT_CALL(*mFlinger.getFrameTracer(),
+ traceFence(layerId, bufferId, frameNumber, _,
+ FrameTracer::FrameEvent::ACQUIRE_FENCE, /*startTime*/ 0));
+ EXPECT_CALL(*mFlinger.getFrameTracer(),
+ traceTimestamp(layerId, bufferId, frameNumber, latchTime,
+ FrameTracer::FrameEvent::LATCH, /*duration*/ 0));
+ layer->updateTexImage(computeVisisbleRegions, latchTime, /*expectedPresentTime*/ 0);
+
+ auto glDoneFence = fenceFactory.createFenceTimeForTest(fence);
+ auto presentFence = fenceFactory.createFenceTimeForTest(fence);
+ CompositorTiming compositorTiming;
+ EXPECT_CALL(*mFlinger.getFrameTracer(),
+ traceFence(layerId, bufferId, frameNumber, presentFence,
+ FrameTracer::FrameEvent::PRESENT_FENCE, /*startTime*/ 0));
+ layer->onPostComposition(nullptr, glDoneFence, presentFence, compositorTiming);
+ }
+};
+
+TEST_F(TransactionFrameTracerTest, BLASTTransactionSendsFrameTracerEvents) {
+ BLASTTransactionSendsFrameTracerEvents();
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
new file mode 100644
index 0000000..b7917aa
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -0,0 +1,514 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/SurfaceComposerClient.h>
+#include <log/log.h>
+#include <renderengine/ExternalTexture.h>
+#include <renderengine/mock/RenderEngine.h>
+#include <utils/String8.h>
+
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/MockEventThread.h"
+#include "mock/MockVsyncController.h"
+
+namespace android {
+
+using testing::_;
+using testing::Mock;
+using testing::Return;
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+using PresentState = frametimeline::SurfaceFrame::PresentState;
+
+class TransactionSurfaceFrameTest : public testing::Test {
+public:
+ TransactionSurfaceFrameTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+ setupScheduler();
+ setupComposer(0);
+ }
+
+ ~TransactionSurfaceFrameTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+
+ sp<BufferStateLayer> createBufferStateLayer() {
+ sp<Client> client;
+ LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 100, 100, 0,
+ LayerMetadata());
+ return new BufferStateLayer(args);
+ }
+
+ void commitTransaction(Layer* layer) {
+ auto c = layer->getCurrentState();
+ layer->commitTransaction(c);
+ }
+
+ void setupScheduler() {
+ auto eventThread = std::make_unique<mock::EventThread>();
+ auto sfEventThread = std::make_unique<mock::EventThread>();
+
+ EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
+ EXPECT_CALL(*eventThread, createEventConnection(_, _))
+ .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+ ResyncCallback())));
+
+ EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
+ EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
+ .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+ ResyncCallback())));
+
+ auto vsyncController = std::make_unique<mock::VsyncController>();
+ auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
+
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*vsyncTracker, currentPeriod())
+ .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
+ std::move(eventThread), std::move(sfEventThread));
+ }
+
+ void setupComposer(uint32_t virtualDisplayCount) {
+ mComposer = new Hwc2::mock::Composer();
+ EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount));
+ mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
+
+ Mock::VerifyAndClear(mComposer);
+ }
+
+ TestableSurfaceFlinger mFlinger;
+ Hwc2::mock::Composer* mComposer = nullptr;
+ renderengine::mock::RenderEngine mRenderEngine;
+ FenceToFenceTimeMap fenceFactory;
+ client_cache_t mClientCache;
+
+ void PresentedSurfaceFrameForBufferlessTransaction() {
+ sp<BufferStateLayer> layer = createBufferStateLayer();
+ layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 1, /*inputEventId*/ 0},
+ 10);
+ EXPECT_EQ(1u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
+ ASSERT_TRUE(layer->mCurrentState.bufferSurfaceFrameTX == nullptr);
+ const auto surfaceFrame = layer->mCurrentState.bufferlessSurfaceFramesTX.at(/*token*/ 1);
+ commitTransaction(layer.get());
+ EXPECT_EQ(1, surfaceFrame->getToken());
+ EXPECT_EQ(false, surfaceFrame->getIsBuffer());
+ EXPECT_EQ(PresentState::Presented, surfaceFrame->getPresentState());
+ }
+
+ void PresentedSurfaceFrameForBufferTransaction() {
+ sp<BufferStateLayer> layer = createBufferStateLayer();
+ sp<Fence> fence(new Fence());
+ auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
+ const auto buffer = std::make_shared<
+ renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+ 1, 0),
+ mRenderEngine, false);
+ layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
+ {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
+ acquireFence->signalForTest(12);
+
+ commitTransaction(layer.get());
+ EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
+ ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
+ const auto& surfaceFrame = layer->mCurrentState.bufferSurfaceFrameTX;
+ // Buffers are presented only at latch time.
+ EXPECT_EQ(PresentState::Unknown, surfaceFrame->getPresentState());
+
+ bool computeVisisbleRegions;
+ layer->updateTexImage(computeVisisbleRegions, 15, 0);
+
+ EXPECT_EQ(1, surfaceFrame->getToken());
+ EXPECT_EQ(true, surfaceFrame->getIsBuffer());
+ EXPECT_EQ(PresentState::Presented, surfaceFrame->getPresentState());
+ }
+
+ void DroppedSurfaceFrameForBufferTransaction() {
+ sp<BufferStateLayer> layer = createBufferStateLayer();
+
+ sp<Fence> fence1(new Fence());
+ auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
+ const auto buffer1 = std::make_shared<
+ renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+ 1, 0),
+ mRenderEngine, false);
+ layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
+ {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
+ EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
+ ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
+ const auto droppedSurfaceFrame = layer->mCurrentState.bufferSurfaceFrameTX;
+
+ sp<Fence> fence2(new Fence());
+ auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2);
+ const auto buffer2 = std::make_shared<
+ renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+ 1, 0),
+ mRenderEngine, false);
+ nsecs_t start = systemTime();
+ layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt,
+ {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
+ nsecs_t end = systemTime();
+ acquireFence2->signalForTest(12);
+
+ EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
+ ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
+ const auto& presentedSurfaceFrame = layer->mCurrentState.bufferSurfaceFrameTX;
+
+ commitTransaction(layer.get());
+ bool computeVisisbleRegions;
+ layer->updateTexImage(computeVisisbleRegions, 15, 0);
+
+ EXPECT_EQ(1, droppedSurfaceFrame->getToken());
+ EXPECT_EQ(true, droppedSurfaceFrame->getIsBuffer());
+ EXPECT_EQ(PresentState::Dropped, droppedSurfaceFrame->getPresentState());
+ EXPECT_EQ(0u, droppedSurfaceFrame->getActuals().endTime);
+ auto dropTime = droppedSurfaceFrame->getDropTime();
+ EXPECT_TRUE(dropTime > start && dropTime < end);
+
+ EXPECT_EQ(1, presentedSurfaceFrame->getToken());
+ EXPECT_EQ(true, presentedSurfaceFrame->getIsBuffer());
+ EXPECT_EQ(PresentState::Presented, presentedSurfaceFrame->getPresentState());
+ }
+
+ void BufferlessSurfaceFramePromotedToBufferSurfaceFrame() {
+ sp<BufferStateLayer> layer = createBufferStateLayer();
+
+ layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 1, /*inputEventId*/ 0},
+ 10);
+
+ EXPECT_EQ(1u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
+ ASSERT_EQ(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
+
+ sp<Fence> fence(new Fence());
+ auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
+ const auto buffer = std::make_shared<
+ renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+ 1, 0),
+ mRenderEngine, false);
+ layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
+ {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
+ acquireFence->signalForTest(12);
+
+ EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
+ ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
+ const auto& surfaceFrame = layer->mCurrentState.bufferSurfaceFrameTX;
+
+ commitTransaction(layer.get());
+ EXPECT_EQ(1, surfaceFrame->getToken());
+ EXPECT_EQ(true, surfaceFrame->getIsBuffer());
+ // Buffers are presented only at latch time.
+ EXPECT_EQ(PresentState::Unknown, surfaceFrame->getPresentState());
+
+ bool computeVisisbleRegions;
+ layer->updateTexImage(computeVisisbleRegions, 15, 0);
+
+ EXPECT_EQ(PresentState::Presented, surfaceFrame->getPresentState());
+ }
+
+ void BufferlessSurfaceFrameNotCreatedIfBufferSufaceFrameExists() {
+ sp<BufferStateLayer> layer = createBufferStateLayer();
+ sp<Fence> fence(new Fence());
+ auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
+ const auto buffer = std::make_shared<
+ renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+ 1, 0),
+ mRenderEngine, false);
+ layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
+ {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
+ EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
+ ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
+
+ layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 1, /*inputEventId*/ 0},
+ 10);
+ EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
+ ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
+ }
+
+ void MultipleSurfaceFramesPresentedTogether() {
+ sp<BufferStateLayer> layer = createBufferStateLayer();
+ layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 1, /*inputEventId*/ 0},
+ 10);
+ EXPECT_EQ(1u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
+ ASSERT_EQ(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
+ const auto bufferlessSurfaceFrame1 =
+ layer->mCurrentState.bufferlessSurfaceFramesTX.at(/*token*/ 1);
+
+ layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 4, /*inputEventId*/ 0},
+ 10);
+ EXPECT_EQ(2u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
+ ASSERT_EQ(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
+ const auto bufferlessSurfaceFrame2 = layer->mCurrentState.bufferlessSurfaceFramesTX[4];
+
+ sp<Fence> fence(new Fence());
+ auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
+ const auto buffer = std::make_shared<
+ renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+ 1, 0),
+ mRenderEngine, false);
+ layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
+ {/*vsyncId*/ 3, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
+ EXPECT_EQ(2u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
+ ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
+ const auto& bufferSurfaceFrameTX = layer->mCurrentState.bufferSurfaceFrameTX;
+
+ acquireFence->signalForTest(12);
+
+ commitTransaction(layer.get());
+
+ EXPECT_EQ(1, bufferlessSurfaceFrame1->getToken());
+ EXPECT_EQ(false, bufferlessSurfaceFrame1->getIsBuffer());
+ EXPECT_EQ(PresentState::Presented, bufferlessSurfaceFrame1->getPresentState());
+
+ EXPECT_EQ(4, bufferlessSurfaceFrame2->getToken());
+ EXPECT_EQ(false, bufferlessSurfaceFrame2->getIsBuffer());
+ EXPECT_EQ(PresentState::Presented, bufferlessSurfaceFrame2->getPresentState());
+
+ EXPECT_EQ(3, bufferSurfaceFrameTX->getToken());
+ EXPECT_EQ(true, bufferSurfaceFrameTX->getIsBuffer());
+ // Buffers are presented only at latch time.
+ EXPECT_EQ(PresentState::Unknown, bufferSurfaceFrameTX->getPresentState());
+
+ bool computeVisisbleRegions;
+ layer->updateTexImage(computeVisisbleRegions, 15, 0);
+
+ EXPECT_EQ(PresentState::Presented, bufferSurfaceFrameTX->getPresentState());
+ }
+
+ void PendingSurfaceFramesRemovedAfterClassification() {
+ sp<BufferStateLayer> layer = createBufferStateLayer();
+
+ sp<Fence> fence1(new Fence());
+ auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
+ const auto buffer1 = std::make_shared<
+ renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+ 1, 0),
+ mRenderEngine, false);
+ layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
+ {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
+ ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
+ const auto droppedSurfaceFrame = layer->mCurrentState.bufferSurfaceFrameTX;
+
+ sp<Fence> fence2(new Fence());
+ auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2);
+ const auto buffer2 = std::make_shared<
+ renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+ 1, 0),
+ mRenderEngine, false);
+ layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt,
+ {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
+ acquireFence2->signalForTest(12);
+
+ ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
+ auto& presentedSurfaceFrame = layer->mCurrentState.bufferSurfaceFrameTX;
+
+ commitTransaction(layer.get());
+ bool computeVisisbleRegions;
+ layer->updateTexImage(computeVisisbleRegions, 15, 0);
+
+ // Both the droppedSurfaceFrame and presentedSurfaceFrame should be in
+ // pendingJankClassifications.
+ EXPECT_EQ(2u, layer->mPendingJankClassifications.size());
+ presentedSurfaceFrame->onPresent(20, JankType::None, Fps::fromPeriodNsecs(11),
+ /*displayDeadlineDelta*/ 0, /*displayPresentDelta*/ 0);
+ layer->releasePendingBuffer(25);
+
+ EXPECT_EQ(0u, layer->mPendingJankClassifications.size());
+ }
+
+ void BufferSurfaceFrame_ReplaceValidTokenBufferWithInvalidTokenBuffer() {
+ sp<BufferStateLayer> layer = createBufferStateLayer();
+
+ sp<Fence> fence1(new Fence());
+ auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
+ const auto buffer1 = std::make_shared<
+ renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+ 1, 0),
+ mRenderEngine, false);
+ layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
+ {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
+ EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
+ ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
+ const auto droppedSurfaceFrame1 = layer->mCurrentState.bufferSurfaceFrameTX;
+
+ sp<Fence> fence2(new Fence());
+ auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2);
+ const auto buffer2 = std::make_shared<
+ renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+ 1, 0),
+ mRenderEngine, false);
+ auto dropStartTime1 = systemTime();
+ layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt,
+ {/*vsyncId*/ FrameTimelineInfo::INVALID_VSYNC_ID, /*inputEventId*/ 0},
+ nullptr /* releaseBufferCallback */);
+ auto dropEndTime1 = systemTime();
+ EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
+ ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
+ const auto droppedSurfaceFrame2 = layer->mCurrentState.bufferSurfaceFrameTX;
+
+ sp<Fence> fence3(new Fence());
+ auto acquireFence3 = fenceFactory.createFenceTimeForTest(fence3);
+ const auto buffer3 = std::make_shared<
+ renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+ 1, 0),
+ mRenderEngine, false);
+ auto dropStartTime2 = systemTime();
+ layer->setBuffer(buffer3, fence3, 10, 20, false, mClientCache, 1, std::nullopt,
+ {/*vsyncId*/ 2, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
+ auto dropEndTime2 = systemTime();
+ acquireFence3->signalForTest(12);
+
+ EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
+ ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
+ const auto& presentedSurfaceFrame = layer->mCurrentState.bufferSurfaceFrameTX;
+
+ commitTransaction(layer.get());
+ bool computeVisisbleRegions;
+ layer->updateTexImage(computeVisisbleRegions, 15, 0);
+
+ EXPECT_EQ(1, droppedSurfaceFrame1->getToken());
+ EXPECT_EQ(true, droppedSurfaceFrame1->getIsBuffer());
+ EXPECT_EQ(PresentState::Dropped, droppedSurfaceFrame1->getPresentState());
+ EXPECT_EQ(0u, droppedSurfaceFrame1->getActuals().endTime);
+ auto dropTime1 = droppedSurfaceFrame1->getDropTime();
+ EXPECT_TRUE(dropTime1 > dropStartTime1 && dropTime1 < dropEndTime1);
+
+ EXPECT_EQ(FrameTimelineInfo::INVALID_VSYNC_ID, droppedSurfaceFrame2->getToken());
+ EXPECT_EQ(true, droppedSurfaceFrame2->getIsBuffer());
+ EXPECT_EQ(PresentState::Dropped, droppedSurfaceFrame2->getPresentState());
+ EXPECT_EQ(0u, droppedSurfaceFrame2->getActuals().endTime);
+ auto dropTime2 = droppedSurfaceFrame2->getDropTime();
+ EXPECT_TRUE(dropTime2 > dropStartTime2 && dropTime2 < dropEndTime2);
+
+ EXPECT_EQ(2, presentedSurfaceFrame->getToken());
+ EXPECT_EQ(true, presentedSurfaceFrame->getIsBuffer());
+ EXPECT_EQ(PresentState::Presented, presentedSurfaceFrame->getPresentState());
+ }
+
+ void MultipleCommitsBeforeLatch() {
+ sp<BufferStateLayer> layer = createBufferStateLayer();
+ uint32_t surfaceFramesPendingClassification = 0;
+ std::vector<std::shared_ptr<frametimeline::SurfaceFrame>> bufferlessSurfaceFrames;
+ for (int i = 0; i < 10; i += 2) {
+ sp<Fence> fence1(new Fence());
+ const auto buffer1 = std::make_shared<
+ renderengine::ExternalTexture>(new GraphicBuffer(1, 1,
+ HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ 0),
+ mRenderEngine, false);
+ layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
+ {/*vsyncId*/ 1, /*inputEventId*/ 0},
+ nullptr /* releaseBufferCallback */);
+ layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 2,
+ /*inputEventId*/ 0},
+ 10);
+ ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
+ EXPECT_EQ(1u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
+ auto& bufferlessSurfaceFrame =
+ layer->mCurrentState.bufferlessSurfaceFramesTX.at(/*vsyncId*/ 2);
+ bufferlessSurfaceFrames.push_back(bufferlessSurfaceFrame);
+
+ commitTransaction(layer.get());
+ surfaceFramesPendingClassification += 2;
+ EXPECT_EQ(surfaceFramesPendingClassification,
+ layer->mPendingJankClassifications.size());
+ }
+
+ auto presentedBufferSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
+ bool computeVisisbleRegions;
+ layer->updateTexImage(computeVisisbleRegions, 15, 0);
+ // BufferlessSurfaceFrames are immediately set to presented and added to the DisplayFrame.
+ // Since we don't have access to DisplayFrame here, trigger an onPresent directly.
+ for (auto& surfaceFrame : bufferlessSurfaceFrames) {
+ surfaceFrame->onPresent(20, JankType::None, Fps::fromPeriodNsecs(11),
+ /*displayDeadlineDelta*/ 0, /*displayPresentDelta*/ 0);
+ }
+ presentedBufferSurfaceFrame->onPresent(20, JankType::None, Fps::fromPeriodNsecs(11),
+ /*displayDeadlineDelta*/ 0,
+ /*displayPresentDelta*/ 0);
+
+ // There should be 10 bufferlessSurfaceFrames and 1 bufferSurfaceFrame
+ ASSERT_EQ(10u, surfaceFramesPendingClassification);
+ ASSERT_EQ(surfaceFramesPendingClassification, layer->mPendingJankClassifications.size());
+
+ // For the frames upto 8, the bufferSurfaceFrame should have been dropped while the
+ // bufferlessSurfaceFrame presented
+ for (uint32_t i = 0; i < 8; i += 2) {
+ auto& bufferSurfaceFrame = layer->mPendingJankClassifications[i];
+ auto& bufferlessSurfaceFrame = layer->mPendingJankClassifications[i + 1];
+ EXPECT_EQ(bufferSurfaceFrame->getPresentState(), PresentState::Dropped);
+ EXPECT_EQ(bufferlessSurfaceFrame->getPresentState(), PresentState::Presented);
+ }
+ {
+ auto& bufferSurfaceFrame = layer->mPendingJankClassifications[8u];
+ auto& bufferlessSurfaceFrame = layer->mPendingJankClassifications[9u];
+ EXPECT_EQ(bufferSurfaceFrame->getPresentState(), PresentState::Presented);
+ EXPECT_EQ(bufferlessSurfaceFrame->getPresentState(), PresentState::Presented);
+ }
+
+ layer->releasePendingBuffer(25);
+
+ // There shouldn't be any pending classifications. Everything should have been cleared.
+ EXPECT_EQ(0u, layer->mPendingJankClassifications.size());
+ }
+};
+
+TEST_F(TransactionSurfaceFrameTest, PresentedBufferlessSurfaceFrame) {
+ PresentedSurfaceFrameForBufferlessTransaction();
+}
+
+TEST_F(TransactionSurfaceFrameTest, PresentedBufferSurfaceFrame) {
+ PresentedSurfaceFrameForBufferTransaction();
+}
+
+TEST_F(TransactionSurfaceFrameTest, DroppedBufferSurfaceFrame) {
+ DroppedSurfaceFrameForBufferTransaction();
+}
+
+TEST_F(TransactionSurfaceFrameTest, BufferlessSurfaceFramePromotedToBufferSurfaceFrame) {
+ BufferlessSurfaceFramePromotedToBufferSurfaceFrame();
+}
+
+TEST_F(TransactionSurfaceFrameTest, BufferlessSurfaceFrameNotCreatedIfBufferSufaceFrameExists) {
+ BufferlessSurfaceFrameNotCreatedIfBufferSufaceFrameExists();
+}
+
+TEST_F(TransactionSurfaceFrameTest, MultipleSurfaceFramesPresentedTogether) {
+ MultipleSurfaceFramesPresentedTogether();
+}
+
+TEST_F(TransactionSurfaceFrameTest, PendingSurfaceFramesRemovedAfterClassification) {
+ PendingSurfaceFramesRemovedAfterClassification();
+}
+
+TEST_F(TransactionSurfaceFrameTest,
+ BufferSurfaceFrame_ReplaceValidTokenBufferWithInvalidTokenBuffer) {
+ BufferSurfaceFrame_ReplaceValidTokenBufferWithInvalidTokenBuffer();
+}
+
+TEST_F(TransactionSurfaceFrameTest, MultipleCommitsBeforeLatch) {
+ MultipleCommitsBeforeLatch();
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index c2a7752..42b1993 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -52,6 +52,7 @@
void setPeriod(nsecs_t) final {}
void resetModel() final {}
bool needsMoreSamples() const final { return false; }
+ bool isVSyncInPhase(nsecs_t, Fps) const final { return false; }
void dump(std::string&) const final {}
private:
@@ -65,7 +66,7 @@
bool addVsyncTimestamp(nsecs_t) final { return true; }
nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t time_point) const final {
- std::lock_guard<decltype(mMutex)> lk(mMutex);
+ std::lock_guard lock(mMutex);
auto const normalized_to_base = time_point - mBase;
auto const floor = (normalized_to_base) % mPeriod;
if (floor == 0) {
@@ -75,19 +76,20 @@
}
void set_interval(nsecs_t interval, nsecs_t last_known) {
- std::lock_guard<decltype(mMutex)> lk(mMutex);
+ std::lock_guard lock(mMutex);
mPeriod = interval;
mBase = last_known;
}
nsecs_t currentPeriod() const final {
- std::lock_guard<decltype(mMutex)> lk(mMutex);
+ std::lock_guard lock(mMutex);
return mPeriod;
}
void setPeriod(nsecs_t) final {}
void resetModel() final {}
bool needsMoreSamples() const final { return false; }
+ bool isVSyncInPhase(nsecs_t, Fps) const final { return false; }
void dump(std::string&) const final {}
private:
@@ -104,30 +106,36 @@
class RepeatingCallbackReceiver {
public:
- RepeatingCallbackReceiver(VSyncDispatch& dispatch, nsecs_t wl)
- : mWorkload(wl),
+ RepeatingCallbackReceiver(VSyncDispatch& dispatch, nsecs_t workload, nsecs_t readyDuration)
+ : mWorkload(workload),
+ mReadyDuration(readyDuration),
mCallback(
- dispatch, [&](auto time, auto) { callback_called(time); }, "repeat0") {}
+ dispatch, [&](auto time, auto, auto) { callback_called(time); }, "repeat0") {}
void repeatedly_schedule(size_t iterations, std::function<void(nsecs_t)> const& onEachFrame) {
mCallbackTimes.reserve(iterations);
- mCallback.schedule(mWorkload, systemTime(SYSTEM_TIME_MONOTONIC) + mWorkload);
+ mCallback.schedule(
+ {.workDuration = mWorkload,
+ .readyDuration = mReadyDuration,
+ .earliestVsync = systemTime(SYSTEM_TIME_MONOTONIC) + mWorkload + mReadyDuration});
for (auto i = 0u; i < iterations - 1; i++) {
- std::unique_lock<decltype(mMutex)> lk(mMutex);
- mCv.wait(lk, [&] { return mCalled; });
+ std::unique_lock lock(mMutex);
+ mCv.wait(lock, [&] { return mCalled; });
mCalled = false;
auto last = mLastTarget;
- lk.unlock();
+ lock.unlock();
onEachFrame(last);
- mCallback.schedule(mWorkload, last + mWorkload);
+ mCallback.schedule({.workDuration = mWorkload,
+ .readyDuration = mReadyDuration,
+ .earliestVsync = last + mWorkload + mReadyDuration});
}
// wait for the last callback.
- std::unique_lock<decltype(mMutex)> lk(mMutex);
- mCv.wait(lk, [&] { return mCalled; });
+ std::unique_lock lock(mMutex);
+ mCv.wait(lock, [&] { return mCalled; });
}
void with_callback_times(std::function<void(std::vector<nsecs_t> const&)> const& fn) const {
@@ -136,7 +144,7 @@
private:
void callback_called(nsecs_t time) {
- std::lock_guard<decltype(mMutex)> lk(mMutex);
+ std::lock_guard lock(mMutex);
mCallbackTimes.push_back(time);
mCalled = true;
mLastTarget = time;
@@ -144,6 +152,7 @@
}
nsecs_t const mWorkload;
+ nsecs_t const mReadyDuration;
VSyncCallbackRegistration mCallback;
std::mutex mMutex;
@@ -160,9 +169,9 @@
static size_t constexpr num_clients = 3;
std::array<RepeatingCallbackReceiver, num_clients>
- cb_receiver{RepeatingCallbackReceiver(dispatch, toNs(1500us)),
- RepeatingCallbackReceiver(dispatch, toNs(0h)),
- RepeatingCallbackReceiver(dispatch, toNs(1ms))};
+ cb_receiver{RepeatingCallbackReceiver(dispatch, toNs(1500us), toNs(2500us)),
+ RepeatingCallbackReceiver(dispatch, toNs(0h), toNs(0h)),
+ RepeatingCallbackReceiver(dispatch, toNs(1ms), toNs(3ms))};
auto const on_each_frame = [](nsecs_t) {};
std::array<std::thread, num_clients> threads{
@@ -187,7 +196,7 @@
VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
mVsyncMoveThreshold);
- RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms));
+ RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms), toNs(5ms));
auto const on_each_frame = [&](nsecs_t last_known) {
tracker.set_interval(next_vsync_interval += toNs(1ms), last_known);
@@ -205,7 +214,7 @@
VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
mVsyncMoveThreshold);
- RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms));
+ RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms), toNs(5ms));
auto jump_frame_counter = 0u;
auto constexpr jump_frame_at = 10u;
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index f630e3b..b64cce9 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -17,6 +17,7 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
#undef LOG_TAG
#define LOG_TAG "LibSurfaceFlingerUnittests"
@@ -48,6 +49,7 @@
MOCK_METHOD1(setPeriod, void(nsecs_t));
MOCK_METHOD0(resetModel, void());
MOCK_CONST_METHOD0(needsMoreSamples, bool());
+ MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
MOCK_CONST_METHOD1(dump, void(std::string&));
nsecs_t nextVSyncTime(nsecs_t timePoint) const {
@@ -109,22 +111,24 @@
CountingCallback(VSyncDispatch& dispatch)
: mDispatch(dispatch),
mToken(dispatch.registerCallback(std::bind(&CountingCallback::counter, this,
- std::placeholders::_1,
- std::placeholders::_2),
+ std::placeholders::_1, std::placeholders::_2,
+ std::placeholders::_3),
"test")) {}
~CountingCallback() { mDispatch.unregisterCallback(mToken); }
operator VSyncDispatch::CallbackToken() const { return mToken; }
- void counter(nsecs_t time, nsecs_t wakeup_time) {
+ void counter(nsecs_t time, nsecs_t wakeup_time, nsecs_t readyTime) {
mCalls.push_back(time);
mWakeupTime.push_back(wakeup_time);
+ mReadyTime.push_back(readyTime);
}
VSyncDispatch& mDispatch;
VSyncDispatch::CallbackToken mToken;
std::vector<nsecs_t> mCalls;
std::vector<nsecs_t> mWakeupTime;
+ std::vector<nsecs_t> mReadyTime;
};
class PausingCallback {
@@ -142,18 +146,18 @@
operator VSyncDispatch::CallbackToken() const { return mToken; }
void pause(nsecs_t, nsecs_t) {
- std::unique_lock<std::mutex> lk(mMutex);
+ std::unique_lock lock(mMutex);
mPause = true;
mCv.notify_all();
- mCv.wait_for(lk, mPauseAmount, [this] { return !mPause; });
+ mCv.wait_for(lock, mPauseAmount, [this] { return !mPause; });
mResourcePresent = (mResource.lock() != nullptr);
}
bool waitForPause() {
- std::unique_lock<std::mutex> lk(mMutex);
- auto waiting = mCv.wait_for(lk, 10s, [this] { return mPause; });
+ std::unique_lock lock(mMutex);
+ auto waiting = mCv.wait_for(lock, 10s, [this] { return mPause; });
return waiting;
}
@@ -162,7 +166,7 @@
bool resourcePresent() { return mResourcePresent; }
void unpause() {
- std::unique_lock<std::mutex> lk(mMutex);
+ std::unique_lock lock(mMutex);
mPause = false;
mCv.notify_all();
}
@@ -228,7 +232,11 @@
VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold,
mVsyncMoveThreshold};
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb, 100, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = 100,
+ .readyDuration = 0,
+ .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
}
}
@@ -237,7 +245,11 @@
EXPECT_CALL(mMockClock, alarmAt(_, 900));
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb, 100, intended), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = 100,
+ .readyDuration = 0,
+ .earliestVsync = intended}),
+ ScheduleResult::Scheduled);
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -249,7 +261,7 @@
EXPECT_CALL(mMockClock, alarmAt(_, 1050));
CountingCallback cb(mDispatch);
- mDispatch.schedule(cb, 100, mPeriod);
+ mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -265,7 +277,11 @@
EXPECT_CALL(mMockClock, alarmAt(_, mPeriod));
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb, workDuration, mPeriod), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = workDuration,
+ .readyDuration = 0,
+ .earliestVsync = mPeriod}),
+ ScheduleResult::Scheduled);
}
TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancel) {
@@ -273,7 +289,11 @@
EXPECT_CALL(mMockClock, alarmCancel());
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = 100,
+ .readyDuration = 0,
+ .earliestVsync = mPeriod}),
+ ScheduleResult::Scheduled);
EXPECT_EQ(mDispatch.cancel(cb), CancelResult::Cancelled);
}
@@ -282,7 +302,11 @@
EXPECT_CALL(mMockClock, alarmCancel());
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = 100,
+ .readyDuration = 0,
+ .earliestVsync = mPeriod}),
+ ScheduleResult::Scheduled);
mMockClock.advanceBy(950);
EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate);
}
@@ -292,7 +316,11 @@
EXPECT_CALL(mMockClock, alarmCancel());
PausingCallback cb(mDispatch, std::chrono::duration_cast<std::chrono::milliseconds>(1s));
- EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = 100,
+ .readyDuration = 0,
+ .earliestVsync = mPeriod}),
+ ScheduleResult::Scheduled);
std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
EXPECT_TRUE(cb.waitForPause());
@@ -309,7 +337,11 @@
PausingCallback cb(mDispatch, 50ms);
cb.stashResource(resource);
- EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = 100,
+ .readyDuration = 0,
+ .earliestVsync = mPeriod}),
+ ScheduleResult::Scheduled);
std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
EXPECT_TRUE(cb.waitForPause());
@@ -339,8 +371,8 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch.schedule(cb0, 100, mPeriod);
- mDispatch.schedule(cb1, 250, mPeriod);
+ mDispatch.schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+ mDispatch.schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod});
advanceToNextCallback();
advanceToNextCallback();
@@ -367,8 +399,9 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch.schedule(cb0, 100, mPeriod * 10);
- mDispatch.schedule(cb1, 250, mPeriod);
+ mDispatch.schedule(cb0,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod * 10});
+ mDispatch.schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod});
mDispatch.cancel(cb1);
}
@@ -380,9 +413,9 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch.schedule(cb0, 400, 1000);
- mDispatch.schedule(cb1, 200, 1000);
- mDispatch.schedule(cb1, 300, 1000);
+ mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch.schedule(cb1, {.workDuration = 300, .readyDuration = 0, .earliestVsync = 1000});
advanceToNextCallback();
}
@@ -395,9 +428,9 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch.schedule(cb0, 400, 1000);
- mDispatch.schedule(cb1, 200, 1000);
- mDispatch.schedule(cb1, 500, 1000);
+ mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch.schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
advanceToNextCallback();
}
@@ -415,9 +448,10 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch.schedule(cb0, 400, 1000);
- mDispatch.schedule(cb1, 200, 1000);
- mDispatch.schedule(cb1, closeOffset, 1000);
+ mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch.schedule(cb1,
+ {.workDuration = closeOffset, .readyDuration = 0, .earliestVsync = 1000});
advanceToNextCallback();
ASSERT_THAT(cb0.mCalls.size(), Eq(1));
@@ -425,8 +459,9 @@
ASSERT_THAT(cb1.mCalls.size(), Eq(1));
EXPECT_THAT(cb1.mCalls[0], Eq(mPeriod));
- mDispatch.schedule(cb0, 400, 2000);
- mDispatch.schedule(cb1, notCloseOffset, 2000);
+ mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 2000});
+ mDispatch.schedule(cb1,
+ {.workDuration = notCloseOffset, .readyDuration = 0, .earliestVsync = 2000});
advanceToNextCallback();
ASSERT_THAT(cb1.mCalls.size(), Eq(2));
EXPECT_THAT(cb1.mCalls[1], Eq(2000));
@@ -446,8 +481,8 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch.schedule(cb0, 100, 1000);
- mDispatch.schedule(cb1, 200, 1000);
+ mDispatch.schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
advanceToNextCallback();
EXPECT_EQ(mDispatch.cancel(cb0), CancelResult::Cancelled);
}
@@ -460,18 +495,18 @@
.WillOnce(Return(2950));
CountingCallback cb(mDispatch);
- mDispatch.schedule(cb, 100, 920);
+ mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 920});
mMockClock.advanceBy(850);
EXPECT_THAT(cb.mCalls.size(), Eq(1));
- mDispatch.schedule(cb, 100, 1900);
+ mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1900});
mMockClock.advanceBy(900);
EXPECT_THAT(cb.mCalls.size(), Eq(1));
mMockClock.advanceBy(125);
EXPECT_THAT(cb.mCalls.size(), Eq(2));
- mDispatch.schedule(cb, 100, 2900);
+ mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2900});
mMockClock.advanceBy(975);
EXPECT_THAT(cb.mCalls.size(), Eq(3));
}
@@ -482,10 +517,16 @@
EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq);
VSyncDispatch::CallbackToken tmp;
- tmp = mDispatch.registerCallback([&](auto, auto) { mDispatch.schedule(tmp, 100, 2000); },
- "o.o");
+ tmp = mDispatch.registerCallback(
+ [&](auto, auto, auto) {
+ mDispatch.schedule(tmp,
+ {.workDuration = 100,
+ .readyDuration = 0,
+ .earliestVsync = 2000});
+ },
+ "o.o");
- mDispatch.schedule(tmp, 100, 1000);
+ mDispatch.schedule(tmp, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
advanceToNextCallback();
}
@@ -493,17 +534,27 @@
VSyncDispatch::CallbackToken tmp;
std::optional<nsecs_t> lastTarget;
tmp = mDispatch.registerCallback(
- [&](auto timestamp, auto) {
- EXPECT_EQ(mDispatch.schedule(tmp, 400, timestamp - mVsyncMoveThreshold),
+ [&](auto timestamp, auto, auto) {
+ EXPECT_EQ(mDispatch.schedule(tmp,
+ {.workDuration = 400,
+ .readyDuration = 0,
+ .earliestVsync = timestamp - mVsyncMoveThreshold}),
ScheduleResult::Scheduled);
- EXPECT_EQ(mDispatch.schedule(tmp, 400, timestamp), ScheduleResult::Scheduled);
- EXPECT_EQ(mDispatch.schedule(tmp, 400, timestamp + mVsyncMoveThreshold),
+ EXPECT_EQ(mDispatch.schedule(tmp,
+ {.workDuration = 400,
+ .readyDuration = 0,
+ .earliestVsync = timestamp}),
+ ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(tmp,
+ {.workDuration = 400,
+ .readyDuration = 0,
+ .earliestVsync = timestamp + mVsyncMoveThreshold}),
ScheduleResult::Scheduled);
lastTarget = timestamp;
},
"oo");
- mDispatch.schedule(tmp, 999, 1000);
+ mDispatch.schedule(tmp, {.workDuration = 999, .readyDuration = 0, .earliestVsync = 1000});
advanceToNextCallback();
EXPECT_THAT(lastTarget, Eq(1000));
@@ -519,16 +570,16 @@
EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq);
CountingCallback cb(mDispatch);
- mDispatch.schedule(cb, 0, 1000);
+ mDispatch.schedule(cb, {.workDuration = 0, .readyDuration = 0, .earliestVsync = 1000});
mMockClock.advanceBy(750);
- mDispatch.schedule(cb, 50, 1000);
+ mDispatch.schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 1000});
advanceToNextCallback();
- mDispatch.schedule(cb, 50, 2000);
+ mDispatch.schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 2000});
mMockClock.advanceBy(800);
- mDispatch.schedule(cb, 100, 2000);
+ mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
}
TEST_F(VSyncDispatchTimerQueueTest, lateModifications) {
@@ -541,12 +592,12 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch.schedule(cb0, 500, 1000);
- mDispatch.schedule(cb1, 100, 1000);
+ mDispatch.schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch.schedule(cb1, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
advanceToNextCallback();
- mDispatch.schedule(cb0, 200, 2000);
- mDispatch.schedule(cb1, 150, 1000);
+ mDispatch.schedule(cb0, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 2000});
+ mDispatch.schedule(cb1, {.workDuration = 150, .readyDuration = 0, .earliestVsync = 1000});
advanceToNextCallback();
advanceToNextCallback();
@@ -558,8 +609,8 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch.schedule(cb0, 500, 1000);
- mDispatch.schedule(cb1, 500, 20000);
+ mDispatch.schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch.schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 20000});
}
TEST_F(VSyncDispatchTimerQueueTest, setsTimerAfterCancellation) {
@@ -569,31 +620,43 @@
EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq);
CountingCallback cb0(mDispatch);
- mDispatch.schedule(cb0, 500, 1000);
+ mDispatch.schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
mDispatch.cancel(cb0);
- mDispatch.schedule(cb0, 100, 1000);
+ mDispatch.schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
}
TEST_F(VSyncDispatchTimerQueueTest, makingUpIdsError) {
VSyncDispatch::CallbackToken token(100);
- EXPECT_THAT(mDispatch.schedule(token, 100, 1000), Eq(ScheduleResult::Error));
+ EXPECT_THAT(mDispatch.schedule(token,
+ {.workDuration = 100,
+ .readyDuration = 0,
+ .earliestVsync = 1000}),
+ Eq(ScheduleResult::Error));
EXPECT_THAT(mDispatch.cancel(token), Eq(CancelResult::Error));
}
TEST_F(VSyncDispatchTimerQueueTest, canMoveCallbackBackwardsInTime) {
CountingCallback cb0(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled);
- EXPECT_EQ(mDispatch.schedule(cb0, 100, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb0,
+ {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb0,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
}
// b/1450138150
TEST_F(VSyncDispatchTimerQueueTest, doesNotMoveCallbackBackwardsAndSkipAScheduledTargetVSync) {
EXPECT_CALL(mMockClock, alarmAt(_, 500));
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb, 500, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
mMockClock.advanceBy(400);
- EXPECT_EQ(mDispatch.schedule(cb, 800, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(1));
}
@@ -604,16 +667,24 @@
.WillOnce(Return(1000))
.WillOnce(Return(1002));
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb, 500, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
mMockClock.advanceBy(400);
- EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
}
TEST_F(VSyncDispatchTimerQueueTest, canScheduleNegativeOffsetAgainstDifferentPeriods) {
CountingCallback cb0(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb0,
+ {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
advanceToNextCallback();
- EXPECT_EQ(mDispatch.schedule(cb0, 1100, 2000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb0,
+ {.workDuration = 1100, .readyDuration = 0, .earliestVsync = 2000}),
+ ScheduleResult::Scheduled);
}
TEST_F(VSyncDispatchTimerQueueTest, canScheduleLargeNegativeOffset) {
@@ -621,18 +692,26 @@
EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq);
EXPECT_CALL(mMockClock, alarmAt(_, 1100)).InSequence(seq);
CountingCallback cb0(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb0,
+ {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
advanceToNextCallback();
- EXPECT_EQ(mDispatch.schedule(cb0, 1900, 2000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb0,
+ {.workDuration = 1900, .readyDuration = 0, .earliestVsync = 2000}),
+ ScheduleResult::Scheduled);
}
TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesNotAffectSchedulingState) {
EXPECT_CALL(mMockClock, alarmAt(_, 600));
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
- EXPECT_EQ(mDispatch.schedule(cb, 1400, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
advanceToNextCallback();
}
@@ -642,12 +721,12 @@
EXPECT_CALL(mMockClock, alarmCancel()).Times(1);
VSyncCallbackRegistration cb(
- mDispatch, [](auto, auto) {}, "");
+ mDispatch, [](auto, auto, auto) {}, "");
VSyncCallbackRegistration cb1(std::move(cb));
- cb.schedule(100, 1000);
+ cb.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
cb.cancel();
- cb1.schedule(500, 1000);
+ cb1.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
cb1.cancel();
}
@@ -656,14 +735,14 @@
EXPECT_CALL(mMockClock, alarmCancel()).Times(1);
VSyncCallbackRegistration cb(
- mDispatch, [](auto, auto) {}, "");
+ mDispatch, [](auto, auto, auto) {}, "");
VSyncCallbackRegistration cb1(
- mDispatch, [](auto, auto) {}, "");
+ mDispatch, [](auto, auto, auto) {}, "");
cb1 = std::move(cb);
- cb.schedule(100, 1000);
+ cb.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
cb.cancel();
- cb1.schedule(500, 1000);
+ cb1.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
cb1.cancel();
}
@@ -675,12 +754,16 @@
CountingCallback cb1(mDispatch);
CountingCallback cb2(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb1, 400, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb1,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
mMockClock.setLag(100);
mMockClock.advanceBy(620);
- EXPECT_EQ(mDispatch.schedule(cb2, 100, 2000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb2,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}),
+ ScheduleResult::Scheduled);
mMockClock.advanceBy(80);
EXPECT_THAT(cb1.mCalls.size(), Eq(1));
@@ -696,12 +779,16 @@
EXPECT_CALL(mMockClock, alarmAt(_, 1630)).InSequence(seq);
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
mMockClock.setLag(100);
mMockClock.advanceBy(620);
- EXPECT_EQ(mDispatch.schedule(cb, 370, 2000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = 370, .readyDuration = 0, .earliestVsync = 2000}),
+ ScheduleResult::Scheduled);
mMockClock.advanceBy(80);
EXPECT_THAT(cb.mCalls.size(), Eq(1));
@@ -715,8 +802,12 @@
CountingCallback cb1(mDispatch);
CountingCallback cb2(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb1, 400, 1000), ScheduleResult::Scheduled);
- EXPECT_EQ(mDispatch.schedule(cb2, 100, 2000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb1,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb2,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}),
+ ScheduleResult::Scheduled);
mMockClock.setLag(100);
mMockClock.advanceBy(620);
@@ -737,8 +828,12 @@
CountingCallback cb1(mDispatch);
CountingCallback cb2(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb1, 400, 1000), ScheduleResult::Scheduled);
- EXPECT_EQ(mDispatch.schedule(cb2, 100, 2000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb1,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb2,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}),
+ ScheduleResult::Scheduled);
mMockClock.setLag(100);
mMockClock.advanceBy(620);
@@ -766,16 +861,44 @@
.InSequence(seq)
.WillOnce(Return(1000));
- EXPECT_EQ(mDispatch.schedule(cb1, 400, 1000), ScheduleResult::Scheduled);
- EXPECT_EQ(mDispatch.schedule(cb2, 390, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb1,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb2,
+ {.workDuration = 390, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
mMockClock.setLag(100);
mMockClock.advanceBy(700);
ASSERT_THAT(cb1.mWakeupTime.size(), Eq(1));
EXPECT_THAT(cb1.mWakeupTime[0], Eq(600));
+ ASSERT_THAT(cb1.mReadyTime.size(), Eq(1));
+ EXPECT_THAT(cb1.mReadyTime[0], Eq(1000));
ASSERT_THAT(cb2.mWakeupTime.size(), Eq(1));
EXPECT_THAT(cb2.mWakeupTime[0], Eq(610));
+ ASSERT_THAT(cb2.mReadyTime.size(), Eq(1));
+ EXPECT_THAT(cb2.mReadyTime[0], Eq(1000));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithReadyDuration) {
+ auto intended = mPeriod - 230;
+ EXPECT_CALL(mMockClock, alarmAt(_, 900));
+
+ CountingCallback cb(mDispatch);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = 70,
+ .readyDuration = 30,
+ .earliestVsync = intended}),
+ ScheduleResult::Scheduled);
+ advanceToNextCallback();
+
+ ASSERT_THAT(cb.mCalls.size(), Eq(1));
+ EXPECT_THAT(cb.mCalls[0], Eq(mPeriod));
+ ASSERT_THAT(cb.mWakeupTime.size(), Eq(1));
+ EXPECT_THAT(cb.mWakeupTime[0], 900);
+ ASSERT_THAT(cb.mReadyTime.size(), Eq(1));
+ EXPECT_THAT(cb.mReadyTime[0], 970);
}
class VSyncDispatchTimerQueueEntryTest : public testing::Test {
@@ -788,7 +911,7 @@
TEST_F(VSyncDispatchTimerQueueEntryTest, stateAfterInitialization) {
std::string name("basicname");
VSyncDispatchTimerQueueEntry entry(
- name, [](auto, auto) {}, mVsyncMoveThreshold);
+ name, [](auto, auto, auto) {}, mVsyncMoveThreshold);
EXPECT_THAT(entry.name(), Eq(name));
EXPECT_FALSE(entry.lastExecutedVsyncTarget());
EXPECT_FALSE(entry.wakeupTime());
@@ -796,10 +919,12 @@
TEST_F(VSyncDispatchTimerQueueEntryTest, stateScheduling) {
VSyncDispatchTimerQueueEntry entry(
- "test", [](auto, auto) {}, mVsyncMoveThreshold);
+ "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.wakeupTime());
- EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0),
+ Eq(ScheduleResult::Scheduled));
auto const wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
EXPECT_THAT(*wakeup, Eq(900));
@@ -816,10 +941,12 @@
.Times(1)
.WillOnce(Return(10000));
VSyncDispatchTimerQueueEntry entry(
- "test", [](auto, auto) {}, mVsyncMoveThreshold);
+ "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.wakeupTime());
- EXPECT_THAT(entry.schedule(500, 994, mStubTracker, now), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 994},
+ mStubTracker, now),
+ Eq(ScheduleResult::Scheduled));
auto const wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
EXPECT_THAT(*wakeup, Eq(9500));
@@ -829,21 +956,29 @@
auto callCount = 0;
auto vsyncCalledTime = 0;
auto wakeupCalledTime = 0;
+ auto readyCalledTime = 0;
VSyncDispatchTimerQueueEntry entry(
"test",
- [&](auto vsyncTime, auto wakeupTime) {
+ [&](auto vsyncTime, auto wakeupTime, auto readyTime) {
callCount++;
vsyncCalledTime = vsyncTime;
wakeupCalledTime = wakeupTime;
+ readyCalledTime = readyTime;
},
mVsyncMoveThreshold);
- EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0),
+ Eq(ScheduleResult::Scheduled));
auto const wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
EXPECT_THAT(*wakeup, Eq(900));
- entry.callback(entry.executing(), *wakeup);
+ auto const ready = entry.readyTime();
+ ASSERT_TRUE(ready);
+ EXPECT_THAT(*ready, Eq(1000));
+
+ entry.callback(entry.executing(), *wakeup, *ready);
EXPECT_THAT(callCount, Eq(1));
EXPECT_THAT(vsyncCalledTime, Eq(mPeriod));
@@ -861,13 +996,15 @@
.WillOnce(Return(1020));
VSyncDispatchTimerQueueEntry entry(
- "test", [](auto, auto) {}, mVsyncMoveThreshold);
+ "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.wakeupTime());
entry.update(mStubTracker, 0);
EXPECT_FALSE(entry.wakeupTime());
- EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0),
+ Eq(ScheduleResult::Scheduled));
auto wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
EXPECT_THAT(wakeup, Eq(900));
@@ -880,8 +1017,10 @@
TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) {
VSyncDispatchTimerQueueEntry entry(
- "test", [](auto, auto) {}, mVsyncMoveThreshold);
- EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
+ EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0),
+ Eq(ScheduleResult::Scheduled));
entry.update(mStubTracker, 0);
auto const wakeup = entry.wakeupTime();
@@ -891,24 +1030,35 @@
TEST_F(VSyncDispatchTimerQueueEntryTest, willSnapToNextTargettableVSync) {
VSyncDispatchTimerQueueEntry entry(
- "test", [](auto, auto) {}, mVsyncMoveThreshold);
- EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
+ EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0),
+ Eq(ScheduleResult::Scheduled));
entry.executing(); // 1000 is executing
// had 1000 not been executing, this could have been scheduled for time 800.
- EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0),
+ Eq(ScheduleResult::Scheduled));
EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
+ EXPECT_THAT(*entry.readyTime(), Eq(2000));
- EXPECT_THAT(entry.schedule(50, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0),
+ Eq(ScheduleResult::Scheduled));
EXPECT_THAT(*entry.wakeupTime(), Eq(1950));
+ EXPECT_THAT(*entry.readyTime(), Eq(2000));
- EXPECT_THAT(entry.schedule(200, 1001, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 1001},
+ mStubTracker, 0),
+ Eq(ScheduleResult::Scheduled));
EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
+ EXPECT_THAT(*entry.readyTime(), Eq(2000));
}
TEST_F(VSyncDispatchTimerQueueEntryTest,
willRequestNextEstimateWhenSnappingToNextTargettableVSync) {
VSyncDispatchTimerQueueEntry entry(
- "test", [](auto, auto) {}, mVsyncMoveThreshold);
+ "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
Sequence seq;
EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(500))
@@ -921,36 +1071,86 @@
.InSequence(seq)
.WillOnce(Return(2000));
- EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0),
+ Eq(ScheduleResult::Scheduled));
entry.executing(); // 1000 is executing
- EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0),
+ Eq(ScheduleResult::Scheduled));
}
TEST_F(VSyncDispatchTimerQueueEntryTest, reportsScheduledIfStillTime) {
VSyncDispatchTimerQueueEntry entry(
- "test", [](auto, auto) {}, mVsyncMoveThreshold);
- EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
- EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
- EXPECT_THAT(entry.schedule(50, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
- EXPECT_THAT(entry.schedule(1200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
+ EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0),
+ Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0),
+ Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0),
+ Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule({.workDuration = 1200, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0),
+ Eq(ScheduleResult::Scheduled));
}
TEST_F(VSyncDispatchTimerQueueEntryTest, storesPendingUpdatesUntilUpdate) {
static constexpr auto effectualOffset = 200;
VSyncDispatchTimerQueueEntry entry(
- "test", [](auto, auto) {}, mVsyncMoveThreshold);
+ "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.hasPendingWorkloadUpdate());
- entry.addPendingWorkloadUpdate(100, 400);
- entry.addPendingWorkloadUpdate(effectualOffset, 700);
+ entry.addPendingWorkloadUpdate({.workDuration = 100, .readyDuration = 0, .earliestVsync = 400});
+ entry.addPendingWorkloadUpdate(
+ {.workDuration = effectualOffset, .readyDuration = 0, .earliestVsync = 400});
EXPECT_TRUE(entry.hasPendingWorkloadUpdate());
entry.update(mStubTracker, 0);
EXPECT_FALSE(entry.hasPendingWorkloadUpdate());
EXPECT_THAT(*entry.wakeupTime(), Eq(mPeriod - effectualOffset));
}
+TEST_F(VSyncDispatchTimerQueueEntryTest, runCallbackWithReadyDuration) {
+ auto callCount = 0;
+ auto vsyncCalledTime = 0;
+ auto wakeupCalledTime = 0;
+ auto readyCalledTime = 0;
+ VSyncDispatchTimerQueueEntry entry(
+ "test",
+ [&](auto vsyncTime, auto wakeupTime, auto readyTime) {
+ callCount++;
+ vsyncCalledTime = vsyncTime;
+ wakeupCalledTime = wakeupTime;
+ readyCalledTime = readyTime;
+ },
+ mVsyncMoveThreshold);
+
+ EXPECT_THAT(entry.schedule({.workDuration = 70, .readyDuration = 30, .earliestVsync = 500},
+ mStubTracker, 0),
+ Eq(ScheduleResult::Scheduled));
+ auto const wakeup = entry.wakeupTime();
+ ASSERT_TRUE(wakeup);
+ EXPECT_THAT(*wakeup, Eq(900));
+
+ auto const ready = entry.readyTime();
+ ASSERT_TRUE(ready);
+ EXPECT_THAT(*ready, Eq(970));
+
+ entry.callback(entry.executing(), *wakeup, *ready);
+
+ EXPECT_THAT(callCount, Eq(1));
+ EXPECT_THAT(vsyncCalledTime, Eq(mPeriod));
+ EXPECT_THAT(wakeupCalledTime, Eq(*wakeup));
+ EXPECT_FALSE(entry.wakeupTime());
+ auto lastCalledTarget = entry.lastExecutedVsyncTarget();
+ ASSERT_TRUE(lastCalledTarget);
+ EXPECT_THAT(*lastCalledTarget, Eq(mPeriod));
+}
+
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/tests/unittests/VSyncModulatorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncModulatorTest.cpp
deleted file mode 100644
index 9c1ec07..0000000
--- a/services/surfaceflinger/tests/unittests/VSyncModulatorTest.cpp
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * Copyright 2020 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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "LibSurfaceFlingerUnittests"
-#define LOG_NDEBUG 0
-
-#include "Scheduler/VSyncModulator.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-using namespace testing;
-
-namespace android::scheduler {
-
-class MockScheduler : public IPhaseOffsetControl {
-public:
- void setPhaseOffset(ConnectionHandle handle, nsecs_t phaseOffset) {
- mPhaseOffset[handle] = phaseOffset;
- }
-
- nsecs_t getOffset(ConnectionHandle handle) { return mPhaseOffset[handle]; }
-
-private:
- std::unordered_map<ConnectionHandle, nsecs_t> mPhaseOffset;
-};
-
-class VSyncModulatorTest : public testing::Test {
-protected:
- static constexpr auto MIN_EARLY_FRAME_COUNT_TRANSACTION =
- VSyncModulator::MIN_EARLY_FRAME_COUNT_TRANSACTION;
- // Add a 1ms slack to avoid strange timer race conditions.
- static constexpr auto MARGIN_FOR_TX_APPLY = VSyncModulator::MARGIN_FOR_TX_APPLY + 1ms;
-
- // Used to enumerate the different offsets we have
- enum {
- SF_LATE,
- APP_LATE,
- SF_EARLY,
- APP_EARLY,
- SF_EARLY_GL,
- APP_EARLY_GL,
- };
-
- std::unique_ptr<VSyncModulator> mVSyncModulator;
- MockScheduler mMockScheduler;
- ConnectionHandle mAppConnection{1};
- ConnectionHandle mSfConnection{2};
- VSyncModulator::OffsetsConfig mOffsets = {{SF_EARLY, APP_EARLY},
- {SF_EARLY_GL, APP_EARLY_GL},
- {SF_LATE, APP_LATE}};
-
- void SetUp() override {
- mVSyncModulator = std::make_unique<VSyncModulator>(mMockScheduler, mAppConnection,
- mSfConnection, mOffsets);
- mVSyncModulator->setPhaseOffsets(mOffsets);
-
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
- };
-
- void TearDown() override { mVSyncModulator.reset(); }
-};
-
-TEST_F(VSyncModulatorTest, Normal) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Normal);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
- }
-}
-
-TEST_F(VSyncModulatorTest, EarlyEnd) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
-}
-
-TEST_F(VSyncModulatorTest, EarlyStart) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
-}
-
-TEST_F(VSyncModulatorTest, EarlyStartWithEarly) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Early);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
-}
-
-TEST_F(VSyncModulatorTest, EarlyStartWithMoreTransactions) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Normal);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
-}
-
-TEST_F(VSyncModulatorTest, EarlyStartAfterEarlyEnd) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
-}
-
-TEST_F(VSyncModulatorTest, EarlyStartAfterEarlyEndWithMoreTransactions) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Normal);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
-}
-
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 5e5d51c..2a658dd 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -17,6 +17,7 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
#undef LOG_TAG
#define LOG_TAG "LibSurfaceFlingerUnittests"
@@ -59,16 +60,16 @@
};
TEST_F(VSyncPredictorTest, reportsAnticipatedPeriod) {
- auto [slope, intercept] = tracker.getVSyncPredictionModel();
+ auto model = tracker.getVSyncPredictionModel();
- EXPECT_THAT(slope, Eq(mPeriod));
- EXPECT_THAT(intercept, Eq(0));
+ EXPECT_THAT(model.slope, Eq(mPeriod));
+ EXPECT_THAT(model.intercept, Eq(0));
auto const changedPeriod = 2000;
tracker.setPeriod(changedPeriod);
- std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
- EXPECT_THAT(slope, Eq(changedPeriod));
- EXPECT_THAT(intercept, Eq(0));
+ model = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(model.slope, Eq(changedPeriod));
+ EXPECT_THAT(model.intercept, Eq(0));
}
TEST_F(VSyncPredictorTest, reportsSamplesNeededWhenHasNoDataPoints) {
@@ -264,17 +265,17 @@
}
auto const mMaxRoundingError = 100;
- auto [slope, intercept] = tracker.getVSyncPredictionModel();
- EXPECT_THAT(slope, IsCloseTo(fastPeriod, mMaxRoundingError));
- EXPECT_THAT(intercept, IsCloseTo(0, mMaxRoundingError));
+ auto model = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(model.slope, IsCloseTo(fastPeriod, mMaxRoundingError));
+ EXPECT_THAT(model.intercept, IsCloseTo(0, mMaxRoundingError));
tracker.setPeriod(slowPeriod);
for (auto const& timestamp : simulatedVsyncsSlow) {
tracker.addVsyncTimestamp(timestamp);
}
- std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
- EXPECT_THAT(slope, IsCloseTo(slowPeriod, mMaxRoundingError));
- EXPECT_THAT(intercept, IsCloseTo(0, mMaxRoundingError));
+ model = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(model.slope, IsCloseTo(slowPeriod, mMaxRoundingError));
+ EXPECT_THAT(model.intercept, IsCloseTo(0, mMaxRoundingError));
}
TEST_F(VSyncPredictorTest, willBeAccurateUsingPriorResultsForRate) {
@@ -296,9 +297,9 @@
for (auto const& timestamp : simulatedVsyncsFast) {
tracker.addVsyncTimestamp(timestamp);
}
- auto [slope, intercept] = tracker.getVSyncPredictionModel();
- EXPECT_THAT(slope, Eq(fastPeriod));
- EXPECT_THAT(intercept, Eq(0));
+ auto model = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(model.slope, Eq(fastPeriod));
+ EXPECT_THAT(model.intercept, Eq(0));
tracker.setPeriod(slowPeriod);
for (auto const& timestamp : simulatedVsyncsSlow) {
@@ -308,16 +309,16 @@
// we had a model for 100ns mPeriod before, use that until the new samples are
// sufficiently built up
tracker.setPeriod(idealPeriod);
- std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
- EXPECT_THAT(slope, Eq(fastPeriod));
- EXPECT_THAT(intercept, Eq(0));
+ model = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(model.slope, Eq(fastPeriod));
+ EXPECT_THAT(model.intercept, Eq(0));
for (auto const& timestamp : simulatedVsyncsFast2) {
tracker.addVsyncTimestamp(timestamp);
}
- std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
- EXPECT_THAT(slope, Eq(fastPeriod2));
- EXPECT_THAT(intercept, Eq(0));
+ model = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(model.slope, Eq(fastPeriod2));
+ EXPECT_THAT(model.intercept, Eq(0));
}
TEST_F(VSyncPredictorTest, idealModelPredictionsBeforeRegressionModelIsBuilt) {
@@ -407,11 +408,9 @@
tracker.addVsyncTimestamp(i * realPeriod);
}
- EXPECT_THAT(std::get<0>(tracker.getVSyncPredictionModel()),
- IsCloseTo(realPeriod, mMaxRoundingError));
+ EXPECT_THAT(tracker.getVSyncPredictionModel().slope, IsCloseTo(realPeriod, mMaxRoundingError));
tracker.resetModel();
- EXPECT_THAT(std::get<0>(tracker.getVSyncPredictionModel()),
- IsCloseTo(idealPeriod, mMaxRoundingError));
+ EXPECT_THAT(tracker.getVSyncPredictionModel().slope, IsCloseTo(idealPeriod, mMaxRoundingError));
}
TEST_F(VSyncPredictorTest, slopeAlwaysValid) {
@@ -450,6 +449,35 @@
EXPECT_THAT(intercept, Eq(0));
}
+TEST_F(VSyncPredictorTest, isVSyncInPhase) {
+ auto last = mNow;
+ auto const bias = 10;
+ for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod));
+ mNow += mPeriod - bias;
+ last = mNow;
+ tracker.addVsyncTimestamp(mNow);
+ mNow += bias;
+ }
+
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod - bias));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod - bias));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 990), Eq(mNow + 2 * mPeriod - bias));
+
+ const auto maxDivider = 5;
+ const auto maxPeriods = 15;
+ for (int divider = 1; divider < maxDivider; divider++) {
+ for (int i = 0; i < maxPeriods; i++) {
+ const bool expectedInPhase = (i % divider) == 0;
+ EXPECT_THAT(expectedInPhase,
+ tracker.isVSyncInPhase(mNow + i * mPeriod - bias,
+ Fps::fromPeriodNsecs(divider * mPeriod)))
+ << "vsync at " << mNow + (i + 1) * mPeriod - bias << " is "
+ << (expectedInPhase ? "not " : "") << "in phase for divider " << divider;
+ }
+ }
+}
+
TEST_F(VSyncPredictorTest, InconsistentVsyncValueIsFlushedEventually) {
EXPECT_TRUE(tracker.addVsyncTimestamp(600));
EXPECT_TRUE(tracker.needsMoreSamples());
@@ -467,4 +495,4 @@
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 6856612..5826a9b 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wextra"
+
#undef LOG_TAG
#define LOG_TAG "LibSurfaceFlingerUnittests"
#define LOG_NDEBUG 0
@@ -42,29 +46,10 @@
MOCK_METHOD1(setPeriod, void(nsecs_t));
MOCK_METHOD0(resetModel, void());
MOCK_CONST_METHOD0(needsMoreSamples, bool());
+ MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
MOCK_CONST_METHOD1(dump, void(std::string&));
};
-class VSyncTrackerWrapper : public VSyncTracker {
-public:
- VSyncTrackerWrapper(std::shared_ptr<VSyncTracker> const& tracker) : mTracker(tracker) {}
-
- bool addVsyncTimestamp(nsecs_t timestamp) final {
- return mTracker->addVsyncTimestamp(timestamp);
- }
- nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final {
- return mTracker->nextAnticipatedVSyncTimeFrom(timePoint);
- }
- nsecs_t currentPeriod() const final { return mTracker->currentPeriod(); }
- void setPeriod(nsecs_t period) final { mTracker->setPeriod(period); }
- void resetModel() final { mTracker->resetModel(); }
- bool needsMoreSamples() const final { return mTracker->needsMoreSamples(); }
- void dump(std::string& result) const final { mTracker->dump(result); }
-
-private:
- std::shared_ptr<VSyncTracker> const mTracker;
-};
-
class MockClock : public Clock {
public:
MOCK_CONST_METHOD0(now, nsecs_t());
@@ -83,89 +68,46 @@
class MockVSyncDispatch : public VSyncDispatch {
public:
MOCK_METHOD2(registerCallback,
- CallbackToken(std::function<void(nsecs_t, nsecs_t)> const&, std::string));
+ CallbackToken(std::function<void(nsecs_t, nsecs_t, nsecs_t)> const&, std::string));
MOCK_METHOD1(unregisterCallback, void(CallbackToken));
- MOCK_METHOD3(schedule, ScheduleResult(CallbackToken, nsecs_t, nsecs_t));
+ MOCK_METHOD2(schedule, ScheduleResult(CallbackToken, ScheduleTiming));
MOCK_METHOD1(cancel, CancelResult(CallbackToken token));
MOCK_CONST_METHOD1(dump, void(std::string&));
};
-class VSyncDispatchWrapper : public VSyncDispatch {
-public:
- VSyncDispatchWrapper(std::shared_ptr<VSyncDispatch> const& dispatch) : mDispatch(dispatch) {}
- CallbackToken registerCallback(std::function<void(nsecs_t, nsecs_t)> const& callbackFn,
- std::string callbackName) final {
- return mDispatch->registerCallback(callbackFn, callbackName);
- }
-
- void unregisterCallback(CallbackToken token) final { mDispatch->unregisterCallback(token); }
-
- ScheduleResult schedule(CallbackToken token, nsecs_t workDuration,
- nsecs_t earliestVsync) final {
- return mDispatch->schedule(token, workDuration, earliestVsync);
- }
-
- CancelResult cancel(CallbackToken token) final { return mDispatch->cancel(token); }
-
- void dump(std::string& result) const final { return mDispatch->dump(result); }
-
-private:
- std::shared_ptr<VSyncDispatch> const mDispatch;
-};
-
-std::shared_ptr<FenceTime> generateInvalidFence() {
+std::shared_ptr<android::FenceTime> generateInvalidFence() {
sp<Fence> fence = new Fence();
- return std::make_shared<FenceTime>(fence);
+ return std::make_shared<android::FenceTime>(fence);
}
-std::shared_ptr<FenceTime> generatePendingFence() {
+std::shared_ptr<android::FenceTime> generatePendingFence() {
sp<Fence> fence = new Fence(dup(fileno(tmpfile())));
- return std::make_shared<FenceTime>(fence);
+ return std::make_shared<android::FenceTime>(fence);
}
-void signalFenceWithTime(std::shared_ptr<FenceTime> const& fence, nsecs_t time) {
- FenceTime::Snapshot snap(time);
+void signalFenceWithTime(std::shared_ptr<android::FenceTime> const& fence, nsecs_t time) {
+ android::FenceTime::Snapshot snap(time);
fence->applyTrustedSnapshot(snap);
}
-std::shared_ptr<FenceTime> generateSignalledFenceWithTime(nsecs_t time) {
+std::shared_ptr<android::FenceTime> generateSignalledFenceWithTime(nsecs_t time) {
sp<Fence> fence = new Fence(dup(fileno(tmpfile())));
- std::shared_ptr<FenceTime> ft = std::make_shared<FenceTime>(fence);
+ std::shared_ptr<android::FenceTime> ft = std::make_shared<android::FenceTime>(fence);
signalFenceWithTime(ft, time);
return ft;
}
-class StubCallback : public DispSync::Callback {
-public:
- void onDispSyncEvent(nsecs_t when, nsecs_t /*expectedVSyncTimestamp*/) final {
- std::lock_guard<std::mutex> lk(mMutex);
- mLastCallTime = when;
- }
- std::optional<nsecs_t> lastCallTime() const {
- std::lock_guard<std::mutex> lk(mMutex);
- return mLastCallTime;
- }
-
-private:
- std::mutex mutable mMutex;
- std::optional<nsecs_t> mLastCallTime GUARDED_BY(mMutex);
-};
-
class VSyncReactorTest : public testing::Test {
protected:
VSyncReactorTest()
- : mMockDispatch(std::make_shared<NiceMock<MockVSyncDispatch>>()),
- mMockTracker(std::make_shared<NiceMock<MockVSyncTracker>>()),
+ : mMockTracker(std::make_shared<NiceMock<MockVSyncTracker>>()),
mMockClock(std::make_shared<NiceMock<MockClock>>()),
- mReactor(std::make_unique<ClockWrapper>(mMockClock),
- std::make_unique<VSyncDispatchWrapper>(mMockDispatch),
- std::make_unique<VSyncTrackerWrapper>(mMockTracker), kPendingLimit,
+ mReactor(std::make_unique<ClockWrapper>(mMockClock), *mMockTracker, kPendingLimit,
false /* supportKernelIdleTimer */) {
ON_CALL(*mMockClock, now()).WillByDefault(Return(mFakeNow));
ON_CALL(*mMockTracker, currentPeriod()).WillByDefault(Return(period));
}
- std::shared_ptr<MockVSyncDispatch> mMockDispatch;
std::shared_ptr<MockVSyncTracker> mMockTracker;
std::shared_ptr<MockClock> mMockClock;
static constexpr size_t kPendingLimit = 3;
@@ -180,7 +122,7 @@
VSyncDispatch::CallbackToken const mFakeToken{2398};
nsecs_t lastCallbackTime = 0;
- StubCallback outerCb;
+ // StubCallback outerCb;
std::function<void(nsecs_t, nsecs_t)> innerCb;
VSyncReactor mReactor;
@@ -215,7 +157,7 @@
}
TEST_F(VSyncReactorTest, limitsPendingFences) {
- std::array<std::shared_ptr<FenceTime>, kPendingLimit * 2> fences;
+ std::array<std::shared_ptr<android::FenceTime>, kPendingLimit * 2> fences;
std::array<nsecs_t, fences.size()> fakeTimes;
std::generate(fences.begin(), fences.end(), [] { return generatePendingFence(); });
std::generate(fakeTimes.begin(), fakeTimes.end(), [i = 10]() mutable {
@@ -256,86 +198,48 @@
mReactor.setIgnorePresentFences(true);
nsecs_t const newPeriod = 5000;
- mReactor.setPeriod(newPeriod);
+ mReactor.startPeriodTransition(newPeriod);
- EXPECT_TRUE(mReactor.addResyncSample(0, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(0, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
- EXPECT_FALSE(mReactor.addResyncSample(newPeriod, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(mReactor.addHwVsyncTimestamp(newPeriod, std::nullopt, &periodFlushed));
EXPECT_TRUE(periodFlushed);
EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
}
-TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshNow) {
- nsecs_t const fakeTimestamp = 4839;
- EXPECT_CALL(*mMockTracker, currentPeriod()).Times(0);
- EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(_))
- .Times(1)
- .WillOnce(Return(fakeTimestamp));
-
- EXPECT_THAT(mReactor.computeNextRefresh(0, mMockClock->now()), Eq(fakeTimestamp));
-}
-
-TEST_F(VSyncReactorTest, queriesTrackerForExpectedPresentTime) {
- nsecs_t const fakeTimestamp = 4839;
- EXPECT_CALL(*mMockTracker, currentPeriod()).Times(0);
- EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(_))
- .Times(1)
- .WillOnce(Return(fakeTimestamp));
-
- EXPECT_THAT(mReactor.expectedPresentTime(mMockClock->now()), Eq(fakeTimestamp));
-}
-
-TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshFuture) {
- nsecs_t const fakeTimestamp = 4839;
- nsecs_t const fakePeriod = 1010;
- nsecs_t const mFakeNow = 2214;
- int const numPeriodsOut = 3;
- EXPECT_CALL(*mMockClock, now()).WillOnce(Return(mFakeNow));
- EXPECT_CALL(*mMockTracker, currentPeriod()).WillOnce(Return(fakePeriod));
- EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(mFakeNow + numPeriodsOut * fakePeriod))
- .WillOnce(Return(fakeTimestamp));
- EXPECT_THAT(mReactor.computeNextRefresh(numPeriodsOut, mMockClock->now()), Eq(fakeTimestamp));
-}
-
-TEST_F(VSyncReactorTest, getPeriod) {
- nsecs_t const fakePeriod = 1010;
- EXPECT_CALL(*mMockTracker, currentPeriod()).WillOnce(Return(fakePeriod));
- EXPECT_THAT(mReactor.getPeriod(), Eq(fakePeriod));
-}
-
TEST_F(VSyncReactorTest, setPeriodCalledOnceConfirmedChange) {
nsecs_t const newPeriod = 5000;
EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0);
- mReactor.setPeriod(newPeriod);
+ mReactor.startPeriodTransition(newPeriod);
bool periodFlushed = true;
- EXPECT_TRUE(mReactor.addResyncSample(10000, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(10000, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
- EXPECT_TRUE(mReactor.addResyncSample(20000, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(20000, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
Mock::VerifyAndClearExpectations(mMockTracker.get());
EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1);
- EXPECT_FALSE(mReactor.addResyncSample(25000, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(mReactor.addHwVsyncTimestamp(25000, std::nullopt, &periodFlushed));
EXPECT_TRUE(periodFlushed);
}
TEST_F(VSyncReactorTest, changingPeriodBackAbortsConfirmationProcess) {
nsecs_t sampleTime = 0;
nsecs_t const newPeriod = 5000;
- mReactor.setPeriod(newPeriod);
+ mReactor.startPeriodTransition(newPeriod);
bool periodFlushed = true;
- EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
- EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
- mReactor.setPeriod(period);
- EXPECT_FALSE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+ mReactor.startPeriodTransition(period);
+ EXPECT_FALSE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
}
@@ -344,16 +248,18 @@
nsecs_t const secondPeriod = 5000;
nsecs_t const thirdPeriod = 2000;
- mReactor.setPeriod(secondPeriod);
+ mReactor.startPeriodTransition(secondPeriod);
bool periodFlushed = true;
- EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
- EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
- mReactor.setPeriod(thirdPeriod);
- EXPECT_TRUE(mReactor.addResyncSample(sampleTime += secondPeriod, std::nullopt, &periodFlushed));
+ mReactor.startPeriodTransition(thirdPeriod);
+ EXPECT_TRUE(
+ mReactor.addHwVsyncTimestamp(sampleTime += secondPeriod, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
- EXPECT_FALSE(mReactor.addResyncSample(sampleTime += thirdPeriod, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(
+ mReactor.addHwVsyncTimestamp(sampleTime += thirdPeriod, std::nullopt, &periodFlushed));
EXPECT_TRUE(periodFlushed);
}
@@ -368,9 +274,10 @@
nsecs_t skewyPeriod = period >> 1;
bool periodFlushed = false;
nsecs_t sampleTime = 0;
- EXPECT_TRUE(mReactor.addResyncSample(sampleTime += skewyPeriod, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(
+ mReactor.addHwVsyncTimestamp(sampleTime += skewyPeriod, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
- EXPECT_FALSE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
}
@@ -388,22 +295,22 @@
TEST_F(VSyncReactorTest, presentFenceAdditionDoesNotInterruptConfirmationProcess) {
nsecs_t const newPeriod = 5000;
- mReactor.setPeriod(newPeriod);
+ mReactor.startPeriodTransition(newPeriod);
EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
}
TEST_F(VSyncReactorTest, setPeriodCalledFirstTwoEventsNewPeriod) {
nsecs_t const newPeriod = 5000;
EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0);
- mReactor.setPeriod(newPeriod);
+ mReactor.startPeriodTransition(newPeriod);
bool periodFlushed = true;
- EXPECT_TRUE(mReactor.addResyncSample(5000, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(5000, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
Mock::VerifyAndClearExpectations(mMockTracker.get());
EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1);
- EXPECT_FALSE(mReactor.addResyncSample(10000, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(mReactor.addHwVsyncTimestamp(10000, std::nullopt, &periodFlushed));
EXPECT_TRUE(periodFlushed);
}
@@ -412,7 +319,7 @@
bool periodFlushed = false;
EXPECT_CALL(*mMockTracker, addVsyncTimestamp(fakeTimestamp));
- EXPECT_FALSE(mReactor.addResyncSample(fakeTimestamp, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(mReactor.addHwVsyncTimestamp(fakeTimestamp, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
}
@@ -420,23 +327,23 @@
bool periodFlushed = false;
nsecs_t const newPeriod = 4000;
- mReactor.setPeriod(newPeriod);
+ mReactor.startPeriodTransition(newPeriod);
auto time = 0;
auto constexpr numTimestampSubmissions = 10;
for (auto i = 0; i < numTimestampSubmissions; i++) {
time += period;
- EXPECT_TRUE(mReactor.addResyncSample(time, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
}
time += newPeriod;
- EXPECT_FALSE(mReactor.addResyncSample(time, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed));
EXPECT_TRUE(periodFlushed);
for (auto i = 0; i < numTimestampSubmissions; i++) {
time += newPeriod;
- EXPECT_FALSE(mReactor.addResyncSample(time, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
}
}
@@ -445,14 +352,14 @@
auto time = 0;
bool periodFlushed = false;
nsecs_t const newPeriod = 4000;
- mReactor.setPeriod(newPeriod);
+ mReactor.startPeriodTransition(newPeriod);
time += period;
- mReactor.addResyncSample(time, std::nullopt, &periodFlushed);
+ mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed);
EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
time += newPeriod;
- mReactor.addResyncSample(time, std::nullopt, &periodFlushed);
+ mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed);
EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
}
@@ -461,7 +368,7 @@
auto time = 0;
bool periodFlushed = false;
nsecs_t const newPeriod = 4000;
- mReactor.setPeriod(newPeriod);
+ mReactor.startPeriodTransition(newPeriod);
static auto constexpr numSamplesWithNewPeriod = 4;
Sequence seq;
@@ -475,20 +382,20 @@
.WillRepeatedly(Return(false));
EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(numSamplesWithNewPeriod);
- EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed));
- EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed));
// confirmed period, but predictor wants numRequest samples. This one and prior are valid.
- EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod, std::nullopt, &periodFlushed));
- EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod, std::nullopt, &periodFlushed));
- EXPECT_FALSE(mReactor.addResyncSample(time += newPeriod, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time += newPeriod, std::nullopt, &periodFlushed));
}
TEST_F(VSyncReactorTest, hwVsyncturnsOffOnConfirmationWhenTrackerDoesntRequest) {
auto time = 0;
bool periodFlushed = false;
nsecs_t const newPeriod = 4000;
- mReactor.setPeriod(newPeriod);
+ mReactor.startPeriodTransition(newPeriod);
Sequence seq;
EXPECT_CALL(*mMockTracker, needsMoreSamples())
@@ -497,9 +404,9 @@
.WillRepeatedly(Return(false));
EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2);
- EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed));
- EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed));
- EXPECT_FALSE(mReactor.addResyncSample(time += newPeriod, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time += newPeriod, std::nullopt, &periodFlushed));
}
TEST_F(VSyncReactorTest, hwVsyncIsRequestedForTrackerMultiplePeriodChanges) {
@@ -508,7 +415,7 @@
nsecs_t const newPeriod1 = 4000;
nsecs_t const newPeriod2 = 7000;
- mReactor.setPeriod(newPeriod1);
+ mReactor.startPeriodTransition(newPeriod1);
Sequence seq;
EXPECT_CALL(*mMockTracker, needsMoreSamples())
@@ -521,208 +428,17 @@
.WillRepeatedly(Return(false));
EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(7);
- EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed));
- EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed));
// confirmed period, but predictor wants numRequest samples. This one and prior are valid.
- EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod1, std::nullopt, &periodFlushed));
- EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod1, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed));
- mReactor.setPeriod(newPeriod2);
- EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod1, std::nullopt, &periodFlushed));
- EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod2, std::nullopt, &periodFlushed));
- EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod2, std::nullopt, &periodFlushed));
- EXPECT_FALSE(mReactor.addResyncSample(time += newPeriod2, std::nullopt, &periodFlushed));
-}
-
-static nsecs_t computeWorkload(nsecs_t period, nsecs_t phase) {
- return period - phase;
-}
-
-TEST_F(VSyncReactorTest, addEventListener) {
- Sequence seq;
- EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
- .InSequence(seq)
- .WillOnce(Return(mFakeToken));
- EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
- .InSequence(seq);
- EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).Times(2).InSequence(seq);
- EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
-
- mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
- mReactor.removeEventListener(&outerCb, &lastCallbackTime);
-}
-
-TEST_F(VSyncReactorTest, addEventListenerTwiceChangesPhase) {
- Sequence seq;
- EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
- .InSequence(seq)
- .WillOnce(Return(mFakeToken));
- EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
- .InSequence(seq);
- EXPECT_CALL(*mMockDispatch,
- schedule(mFakeToken, computeWorkload(period, mAnotherPhase), _)) // mFakeNow))
- .InSequence(seq);
- EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
- EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
-
- mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
- mReactor.addEventListener(mName, mAnotherPhase, &outerCb, lastCallbackTime);
-}
-
-TEST_F(VSyncReactorTest, eventListenerGetsACallbackAndReschedules) {
- Sequence seq;
- EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
- .InSequence(seq)
- .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
- EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
- .InSequence(seq);
- EXPECT_CALL(*mMockDispatch,
- schedule(mFakeToken, computeWorkload(period, mPhase), mFakeVSyncTime))
- .Times(2)
- .InSequence(seq);
- EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
- EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
-
- mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
- ASSERT_TRUE(innerCb);
- innerCb(mFakeVSyncTime, mFakeWakeupTime);
- innerCb(mFakeVSyncTime, mFakeWakeupTime);
-}
-
-TEST_F(VSyncReactorTest, callbackTimestampDistributedIsWakeupTime) {
- Sequence seq;
- EXPECT_CALL(*mMockDispatch, registerCallback(_, _))
- .InSequence(seq)
- .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
- EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
- .InSequence(seq);
- EXPECT_CALL(*mMockDispatch,
- schedule(mFakeToken, computeWorkload(period, mPhase), mFakeVSyncTime))
- .InSequence(seq);
-
- mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
- ASSERT_TRUE(innerCb);
- innerCb(mFakeVSyncTime, mFakeWakeupTime);
- EXPECT_THAT(outerCb.lastCallTime(), Optional(mFakeWakeupTime));
-}
-
-TEST_F(VSyncReactorTest, eventListenersRemovedOnDestruction) {
- Sequence seq;
- EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
- .InSequence(seq)
- .WillOnce(Return(mFakeToken));
- EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
- .InSequence(seq);
- EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
- EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
-
- mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
-}
-
-// b/149221293
-TEST_F(VSyncReactorTest, selfRemovingEventListenerStopsCallbacks) {
- class SelfRemovingCallback : public DispSync::Callback {
- public:
- SelfRemovingCallback(VSyncReactor& vsr) : mVsr(vsr) {}
- void onDispSyncEvent(nsecs_t when, nsecs_t /*expectedVSyncTimestamp*/) final {
- mVsr.removeEventListener(this, &when);
- }
-
- private:
- VSyncReactor& mVsr;
- } selfRemover(mReactor);
-
- Sequence seq;
- EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
- .InSequence(seq)
- .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
- EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
- .InSequence(seq);
- EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).Times(2).InSequence(seq);
- EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
-
- mReactor.addEventListener(mName, mPhase, &selfRemover, lastCallbackTime);
- innerCb(0, 0);
-}
-
-TEST_F(VSyncReactorTest, addEventListenerChangePeriod) {
- Sequence seq;
- EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
- .InSequence(seq)
- .WillOnce(Return(mFakeToken));
- EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
- .InSequence(seq);
- EXPECT_CALL(*mMockDispatch,
- schedule(mFakeToken, computeWorkload(period, mAnotherPhase), mFakeNow))
- .InSequence(seq);
- EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
- EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
-
- mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
- mReactor.addEventListener(mName, mAnotherPhase, &outerCb, lastCallbackTime);
-}
-
-TEST_F(VSyncReactorTest, changingPeriodChangesOffsetsOnNextCb) {
- static constexpr nsecs_t anotherPeriod = 23333;
- Sequence seq;
- EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
- .InSequence(seq)
- .WillOnce(Return(mFakeToken));
- EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
- .InSequence(seq);
- EXPECT_CALL(*mMockTracker, setPeriod(anotherPeriod));
- EXPECT_CALL(*mMockDispatch,
- schedule(mFakeToken, computeWorkload(anotherPeriod, mPhase), mFakeNow))
- .InSequence(seq);
-
- mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
-
- bool periodFlushed = false;
- mReactor.setPeriod(anotherPeriod);
- EXPECT_TRUE(mReactor.addResyncSample(anotherPeriod, std::nullopt, &periodFlushed));
- EXPECT_FALSE(mReactor.addResyncSample(anotherPeriod * 2, std::nullopt, &periodFlushed));
-
- mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
-}
-
-TEST_F(VSyncReactorTest, offsetsAppliedOnNextOpportunity) {
- Sequence seq;
- EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
- .InSequence(seq)
- .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
- EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), _))
- .InSequence(seq)
- .WillOnce(Return(ScheduleResult::Scheduled));
-
- EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mAnotherPhase), _))
- .InSequence(seq)
- .WillOnce(Return(ScheduleResult::Scheduled));
-
- EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mAnotherPhase), _))
- .InSequence(seq)
- .WillOnce(Return(ScheduleResult::Scheduled));
-
- mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
- mReactor.changePhaseOffset(&outerCb, mAnotherPhase);
- ASSERT_TRUE(innerCb);
- innerCb(mFakeVSyncTime, mFakeWakeupTime);
-}
-
-TEST_F(VSyncReactorTest, negativeOffsetsApplied) {
- nsecs_t const negativePhase = -4000;
- Sequence seq;
- EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
- .InSequence(seq)
- .WillOnce(Return(mFakeToken));
- EXPECT_CALL(*mMockDispatch,
- schedule(mFakeToken, computeWorkload(period, negativePhase), mFakeNow))
- .InSequence(seq);
- mReactor.addEventListener(mName, negativePhase, &outerCb, lastCallbackTime);
-}
-
-TEST_F(VSyncReactorTest, beginResyncResetsModel) {
- EXPECT_CALL(*mMockTracker, resetModel());
- mReactor.beginResync();
+ mReactor.startPeriodTransition(newPeriod2);
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod2, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod2, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time += newPeriod2, std::nullopt, &periodFlushed));
}
TEST_F(VSyncReactorTest, periodChangeWithGivenVsyncPeriod) {
@@ -731,13 +447,13 @@
mReactor.setIgnorePresentFences(true);
nsecs_t const newPeriod = 5000;
- mReactor.setPeriod(newPeriod);
+ mReactor.startPeriodTransition(newPeriod);
- EXPECT_TRUE(mReactor.addResyncSample(0, 0, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(0, 0, &periodFlushed));
EXPECT_FALSE(periodFlushed);
- EXPECT_TRUE(mReactor.addResyncSample(newPeriod, 0, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(newPeriod, 0, &periodFlushed));
EXPECT_FALSE(periodFlushed);
- EXPECT_FALSE(mReactor.addResyncSample(newPeriod, newPeriod, &periodFlushed));
+ EXPECT_FALSE(mReactor.addHwVsyncTimestamp(newPeriod, newPeriod, &periodFlushed));
EXPECT_TRUE(periodFlushed);
EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
@@ -745,9 +461,7 @@
TEST_F(VSyncReactorTest, periodIsMeasuredIfIgnoringComposer) {
// Create a reactor which supports the kernel idle timer
- auto idleReactor = VSyncReactor(std::make_unique<ClockWrapper>(mMockClock),
- std::make_unique<VSyncDispatchWrapper>(mMockDispatch),
- std::make_unique<VSyncTrackerWrapper>(mMockTracker),
+ auto idleReactor = VSyncReactor(std::make_unique<ClockWrapper>(mMockClock), *mMockTracker,
kPendingLimit, true /* supportKernelIdleTimer */);
bool periodFlushed = true;
@@ -756,66 +470,31 @@
// First, set the same period, which should only be confirmed when we receive two
// matching callbacks
- idleReactor.setPeriod(10000);
- EXPECT_TRUE(idleReactor.addResyncSample(0, 0, &periodFlushed));
+ idleReactor.startPeriodTransition(10000);
+ EXPECT_TRUE(idleReactor.addHwVsyncTimestamp(0, 0, &periodFlushed));
EXPECT_FALSE(periodFlushed);
// Correct period but incorrect timestamp delta
- EXPECT_TRUE(idleReactor.addResyncSample(0, 10000, &periodFlushed));
+ EXPECT_TRUE(idleReactor.addHwVsyncTimestamp(0, 10000, &periodFlushed));
EXPECT_FALSE(periodFlushed);
// Correct period and correct timestamp delta
- EXPECT_FALSE(idleReactor.addResyncSample(10000, 10000, &periodFlushed));
+ EXPECT_FALSE(idleReactor.addHwVsyncTimestamp(10000, 10000, &periodFlushed));
EXPECT_TRUE(periodFlushed);
// Then, set a new period, which should be confirmed as soon as we receive a callback
// reporting the new period
nsecs_t const newPeriod = 5000;
- idleReactor.setPeriod(newPeriod);
+ idleReactor.startPeriodTransition(newPeriod);
// Incorrect timestamp delta and period
- EXPECT_TRUE(idleReactor.addResyncSample(20000, 10000, &periodFlushed));
+ EXPECT_TRUE(idleReactor.addHwVsyncTimestamp(20000, 10000, &periodFlushed));
EXPECT_FALSE(periodFlushed);
// Incorrect timestamp delta but correct period
- EXPECT_FALSE(idleReactor.addResyncSample(20000, 5000, &periodFlushed));
+ EXPECT_FALSE(idleReactor.addHwVsyncTimestamp(20000, 5000, &periodFlushed));
EXPECT_TRUE(periodFlushed);
EXPECT_TRUE(idleReactor.addPresentFence(generateSignalledFenceWithTime(0)));
}
-using VSyncReactorDeathTest = VSyncReactorTest;
-TEST_F(VSyncReactorDeathTest, invalidRemoval) {
- mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
- mReactor.removeEventListener(&outerCb, &lastCallbackTime);
- EXPECT_DEATH(mReactor.removeEventListener(&outerCb, &lastCallbackTime), ".*");
-}
-
-TEST_F(VSyncReactorDeathTest, invalidChange) {
- EXPECT_DEATH(mReactor.changePhaseOffset(&outerCb, mPhase), ".*");
-
- // the current DispSync-interface usage pattern has evolved around an implementation quirk,
- // which is a callback is assumed to always exist, and it is valid api usage to change the
- // offset of an object that is in the removed state.
- mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
- mReactor.removeEventListener(&outerCb, &lastCallbackTime);
- mReactor.changePhaseOffset(&outerCb, mPhase);
-}
-
-TEST_F(VSyncReactorDeathTest, cannotScheduleOnRegistration) {
- ON_CALL(*mMockDispatch, schedule(_, _, _))
- .WillByDefault(Return(ScheduleResult::CannotSchedule));
- EXPECT_DEATH(mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime), ".*");
-}
-
-TEST_F(VSyncReactorDeathTest, cannotScheduleOnCallback) {
- EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
- .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
- EXPECT_CALL(*mMockDispatch, schedule(_, _, _)).WillOnce(Return(ScheduleResult::Scheduled));
-
- mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
- ASSERT_TRUE(innerCb);
- Mock::VerifyAndClearExpectations(mMockDispatch.get());
-
- ON_CALL(*mMockDispatch, schedule(_, _, _))
- .WillByDefault(Return(ScheduleResult::CannotSchedule));
- EXPECT_DEATH(innerCb(mFakeVSyncTime, mFakeWakeupTime), ".*");
-}
-
} // namespace android::scheduler
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp b/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
new file mode 100644
index 0000000..bb7578d
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
@@ -0,0 +1,308 @@
+/*
+ * Copyright 2019 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SchedulerUnittests"
+
+#include <gmock/gmock.h>
+#include <log/log.h>
+#include <chrono>
+#include <thread>
+
+#include "Scheduler/VsyncConfiguration.h"
+
+using namespace testing;
+
+namespace android::scheduler {
+
+using namespace std::chrono_literals;
+
+class TestableWorkDuration : public impl::WorkDuration {
+public:
+ TestableWorkDuration(Fps currentFps, nsecs_t sfDuration, nsecs_t appDuration,
+ nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
+ nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration)
+ : impl::WorkDuration(currentFps, sfDuration, appDuration, sfEarlyDuration,
+ appEarlyDuration, sfEarlyGlDuration, appEarlyGlDuration) {}
+};
+
+class WorkDurationTest : public testing::Test {
+protected:
+ WorkDurationTest()
+ : mWorkDuration(Fps(60.0f), 10'500'000, 20'500'000, 16'000'000, 16'500'000, 13'500'000,
+ 21'000'000) {}
+
+ ~WorkDurationTest() = default;
+
+ TestableWorkDuration mWorkDuration;
+};
+
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_F(WorkDurationTest, getConfigsForRefreshRate_60Hz) {
+ mWorkDuration.setRefreshRateFps(Fps(60.0f));
+ auto currentOffsets = mWorkDuration.getCurrentConfigs();
+ auto offsets = mWorkDuration.getConfigsForRefreshRate(Fps(60.0f));
+
+ EXPECT_EQ(currentOffsets, offsets);
+ EXPECT_EQ(offsets.late.sfOffset, 6'166'667);
+ EXPECT_EQ(offsets.late.appOffset, 2'333'334);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 10'500'000ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 20'500'000ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, 666'667);
+ EXPECT_EQ(offsets.early.appOffset, 833'334);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 16'000'000ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 16'500'000ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 3'166'667);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 15'500'001);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 13'500'000ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 21'000'000ns);
+}
+
+TEST_F(WorkDurationTest, getConfigsForRefreshRate_90Hz) {
+ mWorkDuration.setRefreshRateFps(Fps(90.0f));
+ auto currentOffsets = mWorkDuration.getCurrentConfigs();
+ auto offsets = mWorkDuration.getConfigsForRefreshRate(Fps(90.0f));
+
+ EXPECT_EQ(currentOffsets, offsets);
+ EXPECT_EQ(offsets.late.sfOffset, 611'111);
+ EXPECT_EQ(offsets.late.appOffset, 2'333'333);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 10'500'000ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 20'500'000ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, -4'888'889);
+ EXPECT_EQ(offsets.early.appOffset, 833'333);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 16'000'000ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 16'500'000ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, -2'388'889);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 9'944'444);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 13'500'000ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 21'000'000ns);
+}
+
+TEST_F(WorkDurationTest, getConfigsForRefreshRate_DefaultOffsets) {
+ TestableWorkDuration phaseOffsetsWithDefaultValues(Fps(60.0f), -1, -1, -1, -1, -1, -1);
+
+ auto validateOffsets = [](const auto& offsets, std::chrono::nanoseconds vsyncPeriod) {
+ EXPECT_EQ(offsets.late.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.late.appOffset, 1'000'000);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, vsyncPeriod - 1'000'000ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, vsyncPeriod);
+
+ EXPECT_EQ(offsets.early.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.early.appOffset, 1'000'000);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, vsyncPeriod - 1'000'000ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, vsyncPeriod);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 1'000'000);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, vsyncPeriod - 1'000'000ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, vsyncPeriod);
+ };
+
+ const auto testForRefreshRate = [&](Fps refreshRate) {
+ phaseOffsetsWithDefaultValues.setRefreshRateFps(refreshRate);
+ auto currentOffsets = phaseOffsetsWithDefaultValues.getCurrentConfigs();
+ auto offsets = phaseOffsetsWithDefaultValues.getConfigsForRefreshRate(refreshRate);
+ EXPECT_EQ(currentOffsets, offsets);
+ validateOffsets(offsets, std::chrono::nanoseconds(refreshRate.getPeriodNsecs()));
+ };
+
+ testForRefreshRate(Fps(90.0f));
+ testForRefreshRate(Fps(60.0f));
+}
+
+TEST_F(WorkDurationTest, getConfigsForRefreshRate_unknownRefreshRate) {
+ auto offsets = mWorkDuration.getConfigsForRefreshRate(Fps(14.7f));
+
+ EXPECT_EQ(offsets.late.sfOffset, 57'527'208);
+ EXPECT_EQ(offsets.late.appOffset, 37'027'208);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 10'500'000ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 20'500'000ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, 52'027'208);
+ EXPECT_EQ(offsets.early.appOffset, 35'527'208);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 16'000'000ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 16'500'000ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 54'527'208);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 33'527'208);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 13'500'000ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 21'000'000ns);
+}
+
+class TestablePhaseOffsets : public impl::PhaseOffsets {
+public:
+ TestablePhaseOffsets(nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
+ std::optional<nsecs_t> earlySfOffsetNs,
+ std::optional<nsecs_t> earlyGpuSfOffsetNs,
+ std::optional<nsecs_t> earlyAppOffsetNs,
+ std::optional<nsecs_t> earlyGpuAppOffsetNs,
+ nsecs_t highFpsVsyncPhaseOffsetNs, nsecs_t highFpsSfVSyncPhaseOffsetNs,
+ std::optional<nsecs_t> highFpsEarlySfOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyGpuSfOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyAppOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyGpuAppOffsetNs,
+ nsecs_t thresholdForNextVsync)
+ : impl::PhaseOffsets(Fps(60.0f), vsyncPhaseOffsetNs, sfVSyncPhaseOffsetNs,
+ earlySfOffsetNs, earlyGpuSfOffsetNs, earlyAppOffsetNs,
+ earlyGpuAppOffsetNs, highFpsVsyncPhaseOffsetNs,
+ highFpsSfVSyncPhaseOffsetNs, highFpsEarlySfOffsetNs,
+ highFpsEarlyGpuSfOffsetNs, highFpsEarlyAppOffsetNs,
+ highFpsEarlyGpuAppOffsetNs, thresholdForNextVsync) {}
+};
+
+class PhaseOffsetsTest : public testing::Test {
+protected:
+ PhaseOffsetsTest() = default;
+ ~PhaseOffsetsTest() = default;
+
+ TestablePhaseOffsets mPhaseOffsets{2'000'000, 6'000'000, 7'000'000, 8'000'000, 3'000'000,
+ 4'000'000, 2'000'000, 1'000'000, 2'000'000, 3'000'000,
+ 3'000'000, 4'000'000, 10'000'000};
+};
+
+TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_unknownRefreshRate) {
+ auto offsets = mPhaseOffsets.getConfigsForRefreshRate(Fps(14.7f));
+
+ EXPECT_EQ(offsets.late.sfOffset, 6'000'000);
+ EXPECT_EQ(offsets.late.appOffset, 2'000'000);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 62'027'208ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 72'027'208ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, 7'000'000);
+ EXPECT_EQ(offsets.early.appOffset, 3'000'000);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 61'027'208ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 72'027'208ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 8'000'000);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 4'000'000);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 60'027'208ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 72'027'208ns);
+}
+
+TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_60Hz) {
+ auto offsets = mPhaseOffsets.getConfigsForRefreshRate(Fps(60.0f));
+
+ EXPECT_EQ(offsets.late.sfOffset, 6'000'000);
+ EXPECT_EQ(offsets.late.appOffset, 2'000'000);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 10'666'667ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 20'666'667ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, 7'000'000);
+ EXPECT_EQ(offsets.early.appOffset, 3'000'000);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 9'666'667ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 20'666'667ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 8'000'000);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 4'000'000);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 8'666'667ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 20'666'667ns);
+}
+
+TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_90Hz) {
+ auto offsets = mPhaseOffsets.getConfigsForRefreshRate(Fps(90.0f));
+
+ EXPECT_EQ(offsets.late.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.late.appOffset, 2'000'000);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 10'111'111ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 21'222'222ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, 2'000'000);
+ EXPECT_EQ(offsets.early.appOffset, 3'000'000);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 9'111'111ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 21'222'222ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 3'000'000);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 4'000'000);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 8'111'111ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 21'222'222ns);
+}
+
+TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_DefaultValues_60Hz) {
+ TestablePhaseOffsets phaseOffsets{1'000'000, 1'000'000, {}, {}, {}, {}, 2'000'000,
+ 1'000'000, {}, {}, {}, {}, 10'000'000};
+ auto offsets = phaseOffsets.getConfigsForRefreshRate(Fps(60.0f));
+
+ EXPECT_EQ(offsets.late.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.late.appOffset, 1'000'000);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 15'666'667ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 16'666'667ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.early.appOffset, 1'000'000);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 15'666'667ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 16'666'667ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 1'000'000);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 15'666'667ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 16'666'667ns);
+}
+
+TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_DefaultValues_90Hz) {
+ TestablePhaseOffsets phaseOffsets{1'000'000, 1'000'000, {}, {}, {}, {}, 2'000'000,
+ 1'000'000, {}, {}, {}, {}, 10'000'000};
+ auto offsets = phaseOffsets.getConfigsForRefreshRate(Fps(90.0f));
+
+ EXPECT_EQ(offsets.late.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.late.appOffset, 2'000'000);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 10'111'111ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 21'222'222ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.early.appOffset, 2'000'000);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 10'111'111ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 21'222'222ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 2'000'000);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 10'111'111ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 21'222'222ns);
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp b/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
new file mode 100644
index 0000000..17648d5
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2020 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "Scheduler/VsyncModulator.h"
+
+namespace android::scheduler {
+
+class VsyncModulatorTest : public testing::Test {
+ enum {
+ SF_OFFSET_LATE,
+ APP_OFFSET_LATE,
+ SF_DURATION_LATE,
+ APP_DURATION_LATE,
+ SF_OFFSET_EARLY,
+ APP_OFFSET_EARLY,
+ SF_DURATION_EARLY,
+ APP_DURATION_EARLY,
+ SF_OFFSET_EARLY_GPU,
+ APP_OFFSET_EARLY_GPU,
+ SF_DURATION_EARLY_GPU,
+ APP_DURATION_EARLY_GPU,
+ };
+
+ static VsyncModulator::TimePoint Now() {
+ static VsyncModulator::TimePoint now;
+ return now += VsyncModulator::MIN_EARLY_TRANSACTION_TIME;
+ }
+
+protected:
+ static constexpr auto MIN_EARLY_TRANSACTION_FRAMES =
+ VsyncModulator::MIN_EARLY_TRANSACTION_FRAMES;
+
+ using Schedule = scheduler::TransactionSchedule;
+ using nanos = std::chrono::nanoseconds;
+ const VsyncModulator::VsyncConfig kEarly{SF_OFFSET_EARLY, APP_OFFSET_EARLY,
+ nanos(SF_DURATION_LATE), nanos(APP_DURATION_LATE)};
+ const VsyncModulator::VsyncConfig kEarlyGpu{SF_OFFSET_EARLY_GPU, APP_OFFSET_EARLY_GPU,
+ nanos(SF_DURATION_EARLY),
+ nanos(APP_DURATION_EARLY)};
+ const VsyncModulator::VsyncConfig kLate{SF_OFFSET_LATE, APP_OFFSET_LATE,
+ nanos(SF_DURATION_EARLY_GPU),
+ nanos(APP_DURATION_EARLY_GPU)};
+
+ const VsyncModulator::VsyncConfigSet mOffsets = {kEarly, kEarlyGpu, kLate};
+ VsyncModulator mVsyncModulator{mOffsets, Now};
+
+ void SetUp() override { EXPECT_EQ(kLate, mVsyncModulator.setVsyncConfigSet(mOffsets)); }
+};
+
+#define CHECK_COMMIT(result, configs) \
+ EXPECT_EQ(result, mVsyncModulator.onTransactionCommit()); \
+ EXPECT_EQ(configs, mVsyncModulator.getVsyncConfig());
+
+#define CHECK_REFRESH(N, result, configs) \
+ for (int i = 0; i < N; i++) { \
+ EXPECT_EQ(result, mVsyncModulator.onDisplayRefresh(false)); \
+ EXPECT_EQ(configs, mVsyncModulator.getVsyncConfig()); \
+ }
+
+TEST_F(VsyncModulatorTest, Late) {
+ EXPECT_FALSE(mVsyncModulator.setTransactionSchedule(Schedule::Late));
+
+ CHECK_COMMIT(std::nullopt, kLate);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kLate);
+}
+
+TEST_F(VsyncModulatorTest, EarlyEnd) {
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+ CHECK_REFRESH(1, kLate, kLate);
+}
+
+TEST_F(VsyncModulatorTest, EarlyStart) {
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(5 * MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kEarly);
+
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+ CHECK_REFRESH(1, kLate, kLate);
+}
+
+TEST_F(VsyncModulatorTest, EarlyStartWithMoreTransactions) {
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart));
+
+ CHECK_COMMIT(kEarly, kEarly);
+
+ for (int i = 0; i < 5 * MIN_EARLY_TRANSACTION_FRAMES; i++) {
+ EXPECT_FALSE(mVsyncModulator.setTransactionSchedule(Schedule::Late));
+ CHECK_REFRESH(1, std::nullopt, kEarly);
+ }
+
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+ CHECK_REFRESH(1, kLate, kLate);
+}
+
+TEST_F(VsyncModulatorTest, EarlyStartAfterEarlyEnd) {
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(1, kEarly, kEarly);
+ CHECK_REFRESH(5 * MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kEarly);
+
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+ CHECK_REFRESH(1, kLate, kLate);
+}
+
+TEST_F(VsyncModulatorTest, EarlyStartAfterEarlyEndWithMoreTransactions) {
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(1, kEarly, kEarly);
+
+ for (int i = 0; i < 5 * MIN_EARLY_TRANSACTION_FRAMES; i++) {
+ EXPECT_FALSE(mVsyncModulator.setTransactionSchedule(Schedule::Late));
+ CHECK_REFRESH(1, std::nullopt, kEarly);
+ }
+
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+ CHECK_REFRESH(1, kLate, kLate);
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/fake/FakeClock.h b/services/surfaceflinger/tests/unittests/fake/FakeClock.h
new file mode 100644
index 0000000..6d9c764
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/fake/FakeClock.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#pragma once
+
+#include "../../Clock.h"
+
+namespace android::fake {
+
+class FakeClock : public Clock {
+public:
+ virtual ~FakeClock() = default;
+ std::chrono::steady_clock::time_point now() const override { return mNow; }
+
+ void advanceTime(std::chrono::nanoseconds delta) { mNow += delta; }
+
+private:
+ std::chrono::steady_clock::time_point mNow;
+};
+
+} // namespace android::fake
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp
index 0780af1..7de1872 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp
@@ -18,19 +18,16 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#define LOG_TAG "MockComposer"
#include "mock/DisplayHardware/MockComposer.h"
-namespace android {
-namespace Hwc2 {
-namespace mock {
+namespace android::Hwc2::mock {
// Explicit default instantiation is recommended.
Composer::Composer() = default;
Composer::~Composer() = default;
-} // namespace mock
-} // namespace Hwc2
-} // namespace android
+} // namespace android::Hwc2::mock
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index cd9b87a..1ba3c0f 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -24,8 +24,7 @@
class GraphicBuffer;
-namespace Hwc2 {
-namespace mock {
+namespace Hwc2::mock {
using android::hardware::graphics::common::V1_0::ColorTransform;
using android::hardware::graphics::common::V1_0::Transform;
@@ -140,6 +139,5 @@
MOCK_METHOD2(getClientTargetProperty, Error(Display, IComposerClient::ClientTargetProperty*));
};
-} // namespace mock
-} // namespace Hwc2
+} // namespace Hwc2::mock
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp
deleted file mode 100644
index 2ec37c1..0000000
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2019 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 "mock/DisplayHardware/MockDisplay.h"
-
-namespace android {
-namespace Hwc2 {
-namespace mock {
-
-// Explicit default instantiation is recommended.
-Display::Display() = default;
-Display::~Display() = default;
-
-} // namespace mock
-} // namespace Hwc2
-} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
deleted file mode 100644
index fe99e77..0000000
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright 2019 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.
- */
-
-#pragma once
-
-#include <gmock/gmock.h>
-
-#include "DisplayHardware/HWC2.h"
-
-using android::HWC2::Layer;
-
-namespace android {
-namespace Hwc2 {
-namespace mock {
-
-namespace hal = android::hardware::graphics::composer::hal;
-
-class Display : public HWC2::Display {
-public:
- using Layer = ::Layer;
-
- Display();
- ~Display();
-
- MOCK_CONST_METHOD0(getId, hal::HWDisplayId());
- MOCK_CONST_METHOD0(isConnected, bool());
- MOCK_METHOD1(setConnected, void(bool));
- MOCK_CONST_METHOD0(getCapabilities, const std::unordered_set<hal::DisplayCapability>&());
-
- MOCK_METHOD0(acceptChanges, hal::Error());
- MOCK_METHOD1(createLayer, hal::Error(Layer**));
- MOCK_METHOD1(destroyLayer, hal::Error(Layer*));
- MOCK_CONST_METHOD1(getActiveConfig, hal::Error(std::shared_ptr<const Config>*));
- MOCK_CONST_METHOD1(getActiveConfigIndex, hal::Error(int* outIndex));
- MOCK_METHOD1(getChangedCompositionTypes,
- hal::Error(std::unordered_map<Layer*, hal::Composition>*));
- MOCK_CONST_METHOD1(getColorModes, hal::Error(std::vector<hal::ColorMode>*));
-
- MOCK_CONST_METHOD0(getSupportedPerFrameMetadata, int32_t());
- MOCK_CONST_METHOD2(getRenderIntents,
- hal::Error(hal::ColorMode, std::vector<hal::RenderIntent>*));
- MOCK_METHOD2(getDataspaceSaturationMatrix, hal::Error(hal::Dataspace, android::mat4*));
- MOCK_CONST_METHOD0(getConfigs, std::vector<std::shared_ptr<const Config>>());
-
- MOCK_CONST_METHOD1(getName, hal::Error(std::string*));
- MOCK_METHOD2(getRequests,
- hal::Error(hal::DisplayRequest*, std::unordered_map<Layer*, hal::LayerRequest>*));
- MOCK_CONST_METHOD1(getType, hal::Error(hal::DisplayType*));
- MOCK_CONST_METHOD1(supportsDoze, hal::Error(bool*));
- MOCK_CONST_METHOD1(getHdrCapabilities, hal::Error(android::HdrCapabilities*));
- MOCK_CONST_METHOD3(getDisplayedContentSamplingAttributes,
- hal::Error(hal::PixelFormat*, hal::Dataspace*, uint8_t*));
- MOCK_CONST_METHOD3(setDisplayContentSamplingEnabled, hal::Error(bool, uint8_t, uint64_t));
- MOCK_CONST_METHOD3(getDisplayedContentSample,
- hal::Error(uint64_t, uint64_t, android::DisplayedFrameStats*));
- MOCK_CONST_METHOD1(
- getReleaseFences,
- hal::Error(std::unordered_map<Layer*, android::sp<android::Fence>>* outFences));
- MOCK_METHOD1(present, hal::Error(android::sp<android::Fence>*));
- MOCK_METHOD1(setActiveConfig, hal::Error(const std::shared_ptr<const HWC2::Display::Config>&));
- MOCK_METHOD4(setClientTarget,
- hal::Error(uint32_t, const android::sp<android::GraphicBuffer>&,
- const android::sp<android::Fence>&, hal::Dataspace));
- MOCK_METHOD2(setColorMode, hal::Error(hal::ColorMode, hal::RenderIntent));
- MOCK_METHOD2(setColorTransform, hal::Error(const android::mat4&, hal::ColorTransform));
- MOCK_METHOD2(setOutputBuffer,
- hal::Error(const android::sp<android::GraphicBuffer>&,
- const android::sp<android::Fence>&));
- MOCK_METHOD1(setPowerMode, hal::Error(hal::PowerMode));
- MOCK_METHOD1(setVsyncEnabled, hal::Error(hal::Vsync));
- MOCK_METHOD2(validate, hal::Error(uint32_t*, uint32_t*));
- MOCK_METHOD4(presentOrValidate,
- hal::Error(uint32_t*, uint32_t*, android::sp<android::Fence>*, uint32_t*));
- MOCK_METHOD1(setDisplayBrightness, std::future<hal::Error>(float));
- MOCK_CONST_METHOD1(getDisplayVsyncPeriod, hal::Error(nsecs_t*));
- MOCK_METHOD3(setActiveConfigWithConstraints,
- hal::Error(const std::shared_ptr<const HWC2::Display::Config>&,
- const hal::VsyncPeriodChangeConstraints&,
- hal::VsyncPeriodChangeTimeline*));
- MOCK_METHOD1(setAutoLowLatencyMode, hal::Error(bool on));
- MOCK_CONST_METHOD1(getSupportedContentTypes, hal::Error(std::vector<hal::ContentType>*));
- MOCK_METHOD1(setContentType, hal::Error(hal::ContentType));
- MOCK_METHOD1(getClientTargetProperty, hal::Error(hal::ClientTargetProperty*));
- MOCK_CONST_METHOD1(getConnectionType, hal::Error(android::DisplayConnectionType*));
- MOCK_CONST_METHOD0(isVsyncPeriodSwitchSupported, bool());
-};
-
-} // namespace mock
-} // namespace Hwc2
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp
index 8be7077..1ba38a8 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp
@@ -16,14 +16,10 @@
#include "MockPowerAdvisor.h"
-namespace android {
-namespace Hwc2 {
-namespace mock {
+namespace android::Hwc2::mock {
// Explicit default instantiation is recommended.
PowerAdvisor::PowerAdvisor() = default;
PowerAdvisor::~PowerAdvisor() = default;
-} // namespace mock
-} // namespace Hwc2
-} // namespace android
+} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
index e22d0cf..7450b5d 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -20,9 +20,7 @@
#include "DisplayHardware/PowerAdvisor.h"
-namespace android {
-namespace Hwc2 {
-namespace mock {
+namespace android::Hwc2::mock {
class PowerAdvisor : public android::Hwc2::PowerAdvisor {
public:
@@ -34,6 +32,4 @@
MOCK_METHOD0(notifyDisplayUpdateImminent, void());
};
-} // namespace mock
-} // namespace Hwc2
-} // namespace android
+} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp b/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp
deleted file mode 100644
index 1c8c44d..0000000
--- a/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2018 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 "mock/MockDispSync.h"
-#include <thread>
-
-using namespace std::chrono_literals;
-namespace android {
-namespace mock {
-
-// Explicit default instantiation is recommended.
-DispSync::DispSync() = default;
-DispSync::~DispSync() = default;
-
-status_t DispSync::addEventListener(const char* /*name*/, nsecs_t phase, Callback* callback,
- nsecs_t /*lastCallbackTime*/) {
- if (mCallback.callback != nullptr) {
- return BAD_VALUE;
- }
-
- mCallback = {callback, phase};
- return NO_ERROR;
-}
-status_t DispSync::removeEventListener(Callback* callback, nsecs_t* /*outLastCallback*/) {
- if (mCallback.callback != callback) {
- return BAD_VALUE;
- }
-
- mCallback = {nullptr, 0};
- return NO_ERROR;
-}
-
-status_t DispSync::changePhaseOffset(Callback* callback, nsecs_t phase) {
- if (mCallback.callback != callback) {
- return BAD_VALUE;
- }
-
- mCallback.phase = phase;
- return NO_ERROR;
-}
-
-void DispSync::triggerCallback() {
- if (mCallback.callback == nullptr) return;
-
- const std::chrono::nanoseconds now = std::chrono::steady_clock::now().time_since_epoch();
- const auto expectedVSyncTime = now + 16ms;
- mCallback.callback->onDispSyncEvent(now.count(), expectedVSyncTime.count());
-}
-
-} // namespace mock
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
deleted file mode 100644
index b39487c..0000000
--- a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#pragma once
-
-#include <gmock/gmock.h>
-
-#include "Scheduler/DispSync.h"
-
-namespace android {
-namespace mock {
-
-class DispSync : public android::DispSync {
-public:
- DispSync();
- ~DispSync() override;
-
- MOCK_METHOD0(reset, void());
- MOCK_METHOD1(addPresentFence, bool(const std::shared_ptr<FenceTime>&));
- MOCK_METHOD0(beginResync, void());
- MOCK_METHOD3(addResyncSample, bool(nsecs_t, std::optional<nsecs_t>, bool*));
- MOCK_METHOD0(endResync, void());
- MOCK_METHOD1(setPeriod, void(nsecs_t));
- MOCK_METHOD0(getPeriod, nsecs_t());
- MOCK_METHOD0(getIntendedPeriod, nsecs_t());
- MOCK_METHOD1(setRefreshSkipCount, void(int));
- MOCK_CONST_METHOD2(computeNextRefresh, nsecs_t(int, nsecs_t));
- MOCK_METHOD1(setIgnorePresentFences, void(bool));
- MOCK_METHOD1(expectedPresentTime, nsecs_t(nsecs_t));
-
- MOCK_CONST_METHOD1(dump, void(std::string&));
-
- status_t addEventListener(const char* name, nsecs_t phase, Callback* callback,
- nsecs_t lastCallbackTime) override;
- status_t removeEventListener(Callback* callback, nsecs_t* outLastCallback) override;
- status_t changePhaseOffset(Callback* callback, nsecs_t phase) override;
-
- nsecs_t getCallbackPhase() { return mCallback.phase; }
-
- void triggerCallback();
-
-private:
- struct CallbackType {
- Callback* callback = nullptr;
- nsecs_t phase;
- };
- CallbackType mCallback;
-};
-
-} // namespace mock
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/MockDisplayIdGenerator.h b/services/surfaceflinger/tests/unittests/mock/MockDisplayIdGenerator.h
new file mode 100644
index 0000000..cfc37ea
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockDisplayIdGenerator.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "DisplayIdGenerator.h"
+
+namespace android::mock {
+
+template <typename T>
+class DisplayIdGenerator : public android::DisplayIdGenerator<T> {
+public:
+ // Explicit default instantiation is recommended.
+ DisplayIdGenerator() = default;
+ virtual ~DisplayIdGenerator() = default;
+
+ MOCK_METHOD0(nextId, std::optional<T>());
+ MOCK_METHOD1(markUnused, void(T));
+};
+
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h
deleted file mode 100644
index 6ef352a..0000000
--- a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#pragma once
-
-#include <gmock/gmock.h>
-
-#include "Scheduler/EventControlThread.h"
-
-namespace android {
-namespace mock {
-
-class EventControlThread : public android::EventControlThread {
-public:
- EventControlThread();
- ~EventControlThread() override;
-
- MOCK_METHOD1(setVsyncEnabled, void(bool));
-};
-
-} // namespace mock
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.cpp b/services/surfaceflinger/tests/unittests/mock/MockEventThread.cpp
index 408cd35..302dc01 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.cpp
@@ -16,12 +16,10 @@
#include "mock/MockEventThread.h"
-namespace android {
-namespace mock {
+namespace android::mock {
// Explicit default instantiation is recommended.
EventThread::EventThread() = default;
EventThread::~EventThread() = default;
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 054aaf8..485b4ac 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -20,8 +20,7 @@
#include "Scheduler/EventThread.h"
-namespace android {
-namespace mock {
+namespace android::mock {
class EventThread : public android::EventThread {
public:
@@ -29,13 +28,18 @@
~EventThread() override;
MOCK_CONST_METHOD2(createEventConnection,
- sp<EventThreadConnection>(ResyncCallback, ISurfaceComposer::ConfigChanged));
+ sp<EventThreadConnection>(ResyncCallback,
+ ISurfaceComposer::EventRegistrationFlags));
MOCK_METHOD0(onScreenReleased, void());
MOCK_METHOD0(onScreenAcquired, void());
MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool));
- MOCK_METHOD3(onConfigChanged, void(PhysicalDisplayId, HwcConfigIndexType, nsecs_t));
+ MOCK_METHOD3(onModeChanged, void(PhysicalDisplayId, DisplayModeId, nsecs_t));
+ MOCK_METHOD2(onFrameRateOverridesChanged,
+ void(PhysicalDisplayId, std::vector<FrameRateOverride>));
MOCK_CONST_METHOD1(dump, void(std::string&));
- MOCK_METHOD1(setPhaseOffset, void(nsecs_t phaseOffset));
+ MOCK_METHOD2(setDuration,
+ void(std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration));
MOCK_METHOD1(registerDisplayEventConnection,
status_t(const sp<android::EventThreadConnection> &));
MOCK_METHOD2(setVsyncRate, void(uint32_t, const sp<android::EventThreadConnection> &));
@@ -45,5 +49,4 @@
MOCK_METHOD0(getEventThreadConnectionCount, size_t());
};
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.cpp b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.cpp
similarity index 63%
copy from services/surfaceflinger/tests/unittests/mock/MockEventControlThread.cpp
copy to services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.cpp
index f9bacc8..ff005a0 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2020 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.
@@ -14,14 +14,13 @@
* limitations under the License.
*/
-#include "mock/MockEventControlThread.h"
+#include "mock/MockFrameTimeline.h"
-namespace android {
-namespace mock {
+namespace android::mock {
// Explicit default instantiation is recommended.
-EventControlThread::EventControlThread() = default;
-EventControlThread::~EventControlThread() = default;
+FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid)
+ : android::frametimeline::impl::FrameTimeline(timeStats, surfaceFlingerPid) {}
+FrameTimeline::~FrameTimeline() = default;
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
new file mode 100644
index 0000000..5dc48c3
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "FrameTimeline/FrameTimeline.h"
+
+namespace android::mock {
+
+class FrameTimeline : public android::frametimeline::impl::FrameTimeline {
+ // No need to create mocks for SurfaceFrame and TokenManager yet. They are very small components
+ // and do not have external dependencies like perfetto.
+public:
+ FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid);
+ ~FrameTimeline();
+
+ MOCK_METHOD0(onBootFinished, void());
+ MOCK_METHOD1(addSurfaceFrame, void(std::shared_ptr<frametimeline::SurfaceFrame>));
+ MOCK_METHOD3(setSfWakeUp, void(int64_t, nsecs_t, Fps));
+ MOCK_METHOD3(setSfPresent,
+ void(nsecs_t, const std::shared_ptr<FenceTime>&,
+ const std::shared_ptr<FenceTime>&));
+ MOCK_METHOD1(computeFps, float(const std::unordered_set<int32_t>&));
+};
+
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.cpp b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.cpp
index 358dfdb..417dcb0 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.cpp
@@ -16,12 +16,10 @@
#include "mock/MockFrameTracer.h"
-namespace android {
-namespace mock {
+namespace android::mock {
// Explicit default instantiation is recommended.
FrameTracer::FrameTracer() = default;
FrameTracer::~FrameTracer() = default;
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.h b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.h
index f768b81..305cb1c 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.h
@@ -20,8 +20,7 @@
#include "FrameTracer/FrameTracer.h"
-namespace android {
-namespace mock {
+namespace android::mock {
class FrameTracer : public android::FrameTracer {
public:
@@ -39,5 +38,4 @@
MOCK_METHOD0(miniDump, std::string());
};
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 078d8e0..ba2e4db 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -33,6 +33,7 @@
MOCK_CONST_METHOD0(isVisible, bool());
MOCK_METHOD0(createClone, sp<Layer>());
MOCK_CONST_METHOD0(getFrameRateForLayerTree, FrameRate());
+ MOCK_CONST_METHOD0(getOwnerUid, uid_t());
};
} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
index a82b583..453c93a 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
@@ -18,6 +18,7 @@
#include <gmock/gmock.h>
+#include "FrameTimeline.h"
#include "Scheduler/EventThread.h"
#include "Scheduler/MessageQueue.h"
@@ -29,11 +30,15 @@
~MessageQueue() override;
MOCK_METHOD1(init, void(const sp<SurfaceFlinger>&));
- MOCK_METHOD1(setEventConnection, void(const sp<EventThreadConnection>& connection));
+ MOCK_METHOD1(setInjector, void(sp<EventThreadConnection>));
MOCK_METHOD0(waitMessage, void());
MOCK_METHOD1(postMessage, void(sp<MessageHandler>&&));
MOCK_METHOD0(invalidate, void());
MOCK_METHOD0(refresh, void());
+ MOCK_METHOD3(initVsync,
+ void(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
+ std::chrono::nanoseconds));
+ MOCK_METHOD1(setDuration, void(std::chrono::nanoseconds workDuration));
};
} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
new file mode 100644
index 0000000..ab19886
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "Scheduler/Scheduler.h"
+
+namespace android::mock {
+
+struct SchedulerCallback final : ISchedulerCallback {
+ MOCK_METHOD1(setVsyncEnabled, void(bool));
+ MOCK_METHOD2(changeRefreshRate,
+ void(const scheduler::RefreshRateConfigs::RefreshRate&,
+ scheduler::RefreshRateConfigEvent));
+ MOCK_METHOD0(repaintEverythingForHWC, void());
+ MOCK_METHOD1(kernelTimerChanged, void(bool));
+ MOCK_METHOD0(triggerOnFrameRateOverridesChanged, void());
+};
+
+struct NoOpSchedulerCallback final : ISchedulerCallback {
+ void setVsyncEnabled(bool) override {}
+ void changeRefreshRate(const scheduler::RefreshRateConfigs::RefreshRate&,
+ scheduler::RefreshRateConfigEvent) override {}
+ void repaintEverythingForHWC() override {}
+ void kernelTimerChanged(bool) override {}
+ void triggerOnFrameRateOverridesChanged() {}
+};
+
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp
index 7e925b9..0a0e7b5 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp
@@ -20,15 +20,13 @@
#include "mock/MockSurfaceInterceptor.h"
-namespace android {
-namespace mock {
+namespace android::mock {
// Explicit default instantiation is recommended.
SurfaceInterceptor::SurfaceInterceptor() = default;
SurfaceInterceptor::~SurfaceInterceptor() = default;
-} // namespace mock
-} // namespace android
+} // namespace android::mock
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
index 5beee1c..b085027 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
@@ -20,8 +20,7 @@
#include "SurfaceInterceptor.h"
-namespace android {
-namespace mock {
+namespace android::mock {
class SurfaceInterceptor : public android::SurfaceInterceptor {
public:
@@ -33,10 +32,12 @@
const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>&));
MOCK_METHOD0(disable, void());
MOCK_METHOD0(isEnabled, bool());
- MOCK_METHOD4(saveTransaction,
+ MOCK_METHOD1(addTransactionTraceListener, void(const sp<gui::ITransactionTraceListener>&));
+ MOCK_METHOD1(binderDied, void(const wp<IBinder>&));
+ MOCK_METHOD7(saveTransaction,
void(const Vector<ComposerState>&,
const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>&,
- const Vector<DisplayState>&, uint32_t));
+ const Vector<DisplayState>&, uint32_t, int, int, uint64_t));
MOCK_METHOD1(saveSurfaceCreation, void(const sp<const Layer>&));
MOCK_METHOD1(saveSurfaceDeletion, void(const sp<const Layer>&));
MOCK_METHOD4(saveBufferUpdate, void(int32_t, uint32_t, uint32_t, uint64_t));
@@ -46,5 +47,4 @@
MOCK_METHOD1(saveVSyncEvent, void(nsecs_t));
};
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp
index d686939..f8e76b2 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp
@@ -16,12 +16,10 @@
#include "mock/MockTimeStats.h"
-namespace android {
-namespace mock {
+namespace android::mock {
// Explicit default instantiation is recommended.
TimeStats::TimeStats() = default;
TimeStats::~TimeStats() = default;
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index 4186e2b..526a847 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -20,15 +20,14 @@
#include "TimeStats/TimeStats.h"
-namespace android {
-namespace mock {
+namespace android::mock {
class TimeStats : public android::TimeStats {
public:
TimeStats();
~TimeStats() override;
- MOCK_METHOD0(onBootFinished, void());
+ MOCK_METHOD2(onPullAtom, bool(const int, std::string*));
MOCK_METHOD3(parseArgs, void(bool, const Vector<String16>&, std::string&));
MOCK_METHOD0(isEnabled, bool());
MOCK_METHOD0(miniDump, std::string());
@@ -42,15 +41,19 @@
MOCK_METHOD2(recordFrameDuration, void(nsecs_t, nsecs_t));
MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, nsecs_t));
MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, const std::shared_ptr<FenceTime>&));
- MOCK_METHOD4(setPostTime, void(int32_t, uint64_t, const std::string&, nsecs_t));
+ MOCK_METHOD5(setPostTime, void(int32_t, uint64_t, const std::string&, uid_t, nsecs_t));
MOCK_METHOD2(incrementLatchSkipped, void(int32_t layerId, LatchSkipReason reason));
MOCK_METHOD1(incrementBadDesiredPresent, void(int32_t layerId));
MOCK_METHOD3(setLatchTime, void(int32_t, uint64_t, nsecs_t));
MOCK_METHOD3(setDesiredTime, void(int32_t, uint64_t, nsecs_t));
MOCK_METHOD3(setAcquireTime, void(int32_t, uint64_t, nsecs_t));
MOCK_METHOD3(setAcquireFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&));
- MOCK_METHOD3(setPresentTime, void(int32_t, uint64_t, nsecs_t));
- MOCK_METHOD3(setPresentFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&));
+ MOCK_METHOD6(setPresentTime,
+ void(int32_t, uint64_t, nsecs_t, Fps, std::optional<Fps>, SetFrameRateVote));
+ MOCK_METHOD6(setPresentFence,
+ void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&, Fps, std::optional<Fps>,
+ SetFrameRateVote));
+ MOCK_METHOD1(incrementJankyFrames, void(const JankyFramesInfo&));
MOCK_METHOD1(onDestroy, void(int32_t));
MOCK_METHOD2(removeTimeRecord, void(int32_t, uint64_t));
MOCK_METHOD1(setPowerMode,
@@ -59,5 +62,4 @@
MOCK_METHOD1(setPresentFenceGlobal, void(const std::shared_ptr<FenceTime>&));
};
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.cpp b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.cpp
similarity index 74%
rename from services/surfaceflinger/tests/unittests/mock/MockEventControlThread.cpp
rename to services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.cpp
index f9bacc8..bcccae5 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.cpp
@@ -14,14 +14,12 @@
* limitations under the License.
*/
-#include "mock/MockEventControlThread.h"
+#include "mock/MockVSyncTracker.h"
-namespace android {
-namespace mock {
+namespace android::mock {
// Explicit default instantiation is recommended.
-EventControlThread::EventControlThread() = default;
-EventControlThread::~EventControlThread() = default;
+VSyncTracker::VSyncTracker() = default;
+VSyncTracker::~VSyncTracker() = default;
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
new file mode 100644
index 0000000..5b0c1f3
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "Scheduler/VSyncTracker.h"
+
+namespace android::mock {
+
+class VSyncTracker : public android::scheduler::VSyncTracker {
+public:
+ VSyncTracker();
+ ~VSyncTracker() override;
+
+ MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t));
+ MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
+ MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
+ MOCK_METHOD1(setPeriod, void(nsecs_t));
+ MOCK_METHOD0(resetModel, void());
+ MOCK_CONST_METHOD0(needsMoreSamples, bool());
+ MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
+ MOCK_CONST_METHOD1(dump, void(std::string&));
+};
+
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.cpp b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.cpp
similarity index 73%
copy from services/surfaceflinger/tests/unittests/mock/MockEventControlThread.cpp
copy to services/surfaceflinger/tests/unittests/mock/MockVsyncController.cpp
index f9bacc8..25ae1bd 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.cpp
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-#include "mock/MockEventControlThread.h"
+#include "mock/MockVsyncController.h"
+#include <thread>
-namespace android {
-namespace mock {
+using namespace std::chrono_literals;
+namespace android::mock {
// Explicit default instantiation is recommended.
-EventControlThread::EventControlThread() = default;
-EventControlThread::~EventControlThread() = default;
+VsyncController::VsyncController() = default;
+VsyncController::~VsyncController() = default;
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
new file mode 100644
index 0000000..94d9966
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "Scheduler/VsyncController.h"
+
+namespace android::mock {
+
+class VsyncController : public android::scheduler::VsyncController {
+public:
+ VsyncController();
+ ~VsyncController() override;
+
+ MOCK_METHOD1(addPresentFence, bool(const std::shared_ptr<FenceTime>&));
+ MOCK_METHOD3(addHwVsyncTimestamp, bool(nsecs_t, std::optional<nsecs_t>, bool*));
+ MOCK_METHOD1(startPeriodTransition, void(nsecs_t));
+ MOCK_METHOD1(setIgnorePresentFences, void(bool));
+
+ MOCK_CONST_METHOD1(dump, void(std::string&));
+};
+
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/utils/CallbackUtils.h b/services/surfaceflinger/tests/utils/CallbackUtils.h
index 1318deb..459b35c 100644
--- a/services/surfaceflinger/tests/utils/CallbackUtils.h
+++ b/services/surfaceflinger/tests/utils/CallbackUtils.h
@@ -81,6 +81,10 @@
mExpectedPresentTime = expectedPresentTime;
}
+ void addExpectedPresentTimeForVsyncId(nsecs_t expectedPresentTime) {
+ mExpectedPresentTimeForVsyncId = expectedPresentTime;
+ }
+
void verifyCallbackData(const CallbackData& callbackData) const {
const auto& [latchTime, presentFence, surfaceControlStats] = callbackData;
if (mTransactionResult == ExpectedResult::Transaction::PRESENTED) {
@@ -93,6 +97,11 @@
// misses vsync and we have to wait another 33.3ms
ASSERT_LE(presentFence->getSignalTime(),
mExpectedPresentTime + nsecs_t(66.666666 * 1e6));
+ } else if (mExpectedPresentTimeForVsyncId >= 0) {
+ ASSERT_EQ(presentFence->wait(3000), NO_ERROR);
+ // We give 4ms for prediction error
+ ASSERT_GE(presentFence->getSignalTime(),
+ mExpectedPresentTimeForVsyncId - 4'000'000);
}
} else {
ASSERT_EQ(presentFence, nullptr) << "transaction shouldn't have been presented";
@@ -151,6 +160,7 @@
};
ExpectedResult::Transaction mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED;
nsecs_t mExpectedPresentTime = -1;
+ nsecs_t mExpectedPresentTimeForVsyncId = -1;
std::unordered_map<sp<SurfaceControl>, ExpectedSurfaceResult, SCHash> mExpectedSurfaceResults;
};
diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
index 5480b00..ddaa5a1 100644
--- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h
+++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
@@ -15,8 +15,10 @@
*/
#pragma once
+#include <gui/SyncScreenCaptureListener.h>
#include <ui/Rect.h>
#include <utils/String8.h>
+#include <functional>
#include "TransactionUtils.h"
namespace android {
@@ -27,59 +29,69 @@
// individual pixel values for testing purposes.
class ScreenCapture : public RefBase {
public:
+ static status_t captureDisplay(DisplayCaptureArgs& captureArgs,
+ ScreenCaptureResults& captureResults) {
+ const auto sf = ComposerService::getComposerService();
+ SurfaceComposerClient::Transaction().apply(true);
+
+ captureArgs.dataspace = ui::Dataspace::V0_SRGB;
+ const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
+ status_t status = sf->captureDisplay(captureArgs, captureListener);
+
+ if (status != NO_ERROR) {
+ return status;
+ }
+ captureResults = captureListener->waitForResults();
+ return captureResults.result;
+ }
+
static void captureScreen(std::unique_ptr<ScreenCapture>* sc) {
captureScreen(sc, SurfaceComposerClient::getInternalDisplayToken());
}
static void captureScreen(std::unique_ptr<ScreenCapture>* sc, sp<IBinder> displayToken) {
+ DisplayCaptureArgs args;
+ args.displayToken = displayToken;
+ captureDisplay(sc, args);
+ }
+
+ static void captureDisplay(std::unique_ptr<ScreenCapture>* sc,
+ DisplayCaptureArgs& captureArgs) {
+ ScreenCaptureResults captureResults;
+ ASSERT_EQ(NO_ERROR, captureDisplay(captureArgs, captureResults));
+ *sc = std::make_unique<ScreenCapture>(captureResults.buffer);
+ }
+
+ static status_t captureLayers(LayerCaptureArgs& captureArgs,
+ ScreenCaptureResults& captureResults) {
const auto sf = ComposerService::getComposerService();
SurfaceComposerClient::Transaction().apply(true);
- sp<GraphicBuffer> outBuffer;
- ASSERT_EQ(NO_ERROR, sf->captureScreen(displayToken, &outBuffer, Rect(), 0, 0, false));
- *sc = std::make_unique<ScreenCapture>(outBuffer);
+ captureArgs.dataspace = ui::Dataspace::V0_SRGB;
+ const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
+ status_t status = sf->captureLayers(captureArgs, captureListener);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ captureResults = captureListener->waitForResults();
+ return captureResults.result;
}
- static void captureLayers(std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
- Rect crop = Rect::EMPTY_RECT, float frameScale = 1.0) {
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- SurfaceComposerClient::Transaction().apply(true);
-
- sp<GraphicBuffer> outBuffer;
- ASSERT_EQ(NO_ERROR, sf->captureLayers(parentHandle, &outBuffer, crop, frameScale));
- *sc = std::make_unique<ScreenCapture>(outBuffer);
- }
-
- static void captureChildLayers(std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
- Rect crop = Rect::EMPTY_RECT, float frameScale = 1.0) {
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- SurfaceComposerClient::Transaction().apply(true);
-
- sp<GraphicBuffer> outBuffer;
- ASSERT_EQ(NO_ERROR, sf->captureLayers(parentHandle, &outBuffer, crop, frameScale, true));
- *sc = std::make_unique<ScreenCapture>(outBuffer);
- }
-
- static void captureChildLayersExcluding(
- std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
- std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> excludeLayers) {
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- SurfaceComposerClient::Transaction().apply(true);
-
- sp<GraphicBuffer> outBuffer;
- ASSERT_EQ(NO_ERROR,
- sf->captureLayers(parentHandle, &outBuffer, ui::Dataspace::V0_SRGB,
- ui::PixelFormat::RGBA_8888, Rect::EMPTY_RECT, excludeLayers,
- 1.0f, true));
- *sc = std::make_unique<ScreenCapture>(outBuffer);
+ static void captureLayers(std::unique_ptr<ScreenCapture>* sc, LayerCaptureArgs& captureArgs) {
+ ScreenCaptureResults captureResults;
+ ASSERT_EQ(NO_ERROR, captureLayers(captureArgs, captureResults));
+ *sc = std::make_unique<ScreenCapture>(captureResults.buffer);
}
void expectColor(const Rect& rect, const Color& color, uint8_t tolerance = 0) {
+ ASSERT_NE(nullptr, mOutBuffer);
+ ASSERT_NE(nullptr, mPixels);
ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
TransactionUtils::expectBufferColor(mOutBuffer, mPixels, rect, color, tolerance);
}
void expectBorder(const Rect& rect, const Color& color, uint8_t tolerance = 0) {
+ ASSERT_NE(nullptr, mOutBuffer);
ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
const bool leftBorder = rect.left > 0;
const bool topBorder = rect.top > 0;
@@ -137,6 +149,7 @@
}
void checkPixel(uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b) {
+ ASSERT_NE(nullptr, mOutBuffer);
ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
const uint8_t* pixel = mPixels + (4 * (y * mOutBuffer->getStride() + x));
if (r != pixel[0] || g != pixel[1] || b != pixel[2]) {
@@ -147,6 +160,15 @@
}
}
+ Color getPixelColor(uint32_t x, uint32_t y) {
+ if (!mOutBuffer || mOutBuffer->getPixelFormat() != HAL_PIXEL_FORMAT_RGBA_8888) {
+ return {0, 0, 0, 0};
+ }
+
+ const uint8_t* pixel = mPixels + (4 * (y * mOutBuffer->getStride() + x));
+ return {pixel[0], pixel[1], pixel[2], pixel[3]};
+ }
+
void expectFGColor(uint32_t x, uint32_t y) { checkPixel(x, y, 195, 63, 63); }
void expectBGColor(uint32_t x, uint32_t y) { checkPixel(x, y, 63, 63, 195); }
@@ -154,10 +176,14 @@
void expectChildColor(uint32_t x, uint32_t y) { checkPixel(x, y, 200, 200, 200); }
explicit ScreenCapture(const sp<GraphicBuffer>& outBuffer) : mOutBuffer(outBuffer) {
- mOutBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, reinterpret_cast<void**>(&mPixels));
+ if (mOutBuffer) {
+ mOutBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, reinterpret_cast<void**>(&mPixels));
+ }
}
- ~ScreenCapture() { mOutBuffer->unlock(); }
+ ~ScreenCapture() {
+ if (mOutBuffer) mOutBuffer->unlock();
+ }
private:
sp<GraphicBuffer> mOutBuffer;
diff --git a/services/surfaceflinger/tests/utils/TransactionUtils.h b/services/surfaceflinger/tests/utils/TransactionUtils.h
index 8e1f943..8c448e2 100644
--- a/services/surfaceflinger/tests/utils/TransactionUtils.h
+++ b/services/surfaceflinger/tests/utils/TransactionUtils.h
@@ -16,6 +16,10 @@
#pragma once
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
+
#include <chrono>
#include <android/native_window.h>
@@ -122,7 +126,7 @@
const uint8_t* src = pixels + (outBuffer->getStride() * (y + j) + x) * 4;
for (int32_t i = 0; i < width; i++) {
const uint8_t expected[4] = {color.r, color.g, color.b, color.a};
- EXPECT_TRUE(std::equal(src, src + 4, expected, colorCompare))
+ ASSERT_TRUE(std::equal(src, src + 4, expected, colorCompare))
<< "pixel @ (" << x + i << ", " << y + j << "): "
<< "expected (" << color << "), "
<< "got (" << Color{src[0], src[1], src[2], src[3]} << ")";
@@ -157,6 +161,22 @@
ASSERT_EQ(NO_ERROR, s->unlockAndPost());
}
}
+
+ static void setFrame(Transaction& t, const sp<SurfaceControl>& sc, Rect source, Rect dest,
+ int32_t transform = 0) {
+ uint32_t sourceWidth = source.getWidth();
+ uint32_t sourceHeight = source.getHeight();
+
+ if (transform & ui::Transform::ROT_90) {
+ std::swap(sourceWidth, sourceHeight);
+ }
+
+ float dsdx = dest.getWidth() / static_cast<float>(sourceWidth);
+ float dsdy = dest.getHeight() / static_cast<float>(sourceHeight);
+
+ t.setMatrix(sc, dsdx, 0, 0, dsdy);
+ t.setPosition(sc, dest.left, dest.top);
+ }
};
enum class RenderPath { SCREENSHOT, VIRTUAL_DISPLAY };
@@ -181,3 +201,6 @@
};
} // namespace
} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
\ No newline at end of file
diff --git a/services/vibratorservice/Android.bp b/services/vibratorservice/Android.bp
new file mode 100644
index 0000000..2002bdf
--- /dev/null
+++ b/services/vibratorservice/Android.bp
@@ -0,0 +1,65 @@
+// Copyright (C) 2020 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"],
+}
+
+cc_library_shared {
+ name: "libvibratorservice",
+
+ srcs: [
+ "VibratorCallbackScheduler.cpp",
+ "VibratorHalController.cpp",
+ "VibratorHalWrapper.cpp",
+ "VibratorManagerHalController.cpp",
+ "VibratorManagerHalWrapper.cpp",
+ ],
+
+ aidl: {
+ local_include_dirs: ["include"],
+ include_dirs: [
+ "hardware/interfaces/vibrator/aidl/android/hardware/vibrator",
+ ],
+ export_aidl_headers: true
+ },
+
+ shared_libs: [
+ "libbinder",
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ "android.hardware.vibrator-V2-cpp",
+ "android.hardware.vibrator@1.0",
+ "android.hardware.vibrator@1.1",
+ "android.hardware.vibrator@1.2",
+ "android.hardware.vibrator@1.3",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+
+ local_include_dirs: ["include"],
+
+ export_include_dirs: ["include"],
+}
diff --git a/services/vibratorservice/TEST_MAPPING b/services/vibratorservice/TEST_MAPPING
new file mode 100644
index 0000000..b033adb
--- /dev/null
+++ b/services/vibratorservice/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libvibratorservice_test"
+ }
+ ]
+}
diff --git a/services/vibratorservice/VibratorCallbackScheduler.cpp b/services/vibratorservice/VibratorCallbackScheduler.cpp
new file mode 100644
index 0000000..f2870b0
--- /dev/null
+++ b/services/vibratorservice/VibratorCallbackScheduler.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2020 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 <chrono>
+#include <thread>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+
+namespace android {
+
+namespace vibrator {
+
+// -------------------------------------------------------------------------------------------------
+
+bool DelayedCallback::isExpired() const {
+ return mExpiration <= std::chrono::steady_clock::now();
+}
+
+DelayedCallback::Timestamp DelayedCallback::getExpiration() const {
+ return mExpiration;
+}
+
+void DelayedCallback::run() const {
+ mCallback();
+}
+
+bool DelayedCallback::operator<(const DelayedCallback& other) const {
+ return mExpiration < other.mExpiration;
+}
+
+bool DelayedCallback::operator>(const DelayedCallback& other) const {
+ return mExpiration > other.mExpiration;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+CallbackScheduler::~CallbackScheduler() {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mFinished = true;
+ }
+ mCondition.notify_all();
+ if (mCallbackThread && mCallbackThread->joinable()) {
+ mCallbackThread->join();
+ }
+}
+
+void CallbackScheduler::schedule(std::function<void()> callback, std::chrono::milliseconds delay) {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mCallbackThread == nullptr) {
+ mCallbackThread = std::make_unique<std::thread>(&CallbackScheduler::loop, this);
+ }
+ mQueue.emplace(DelayedCallback(callback, delay));
+ }
+ mCondition.notify_all();
+}
+
+void CallbackScheduler::loop() {
+ while (true) {
+ std::unique_lock<std::mutex> lock(mMutex);
+ if (mFinished) {
+ // Destructor was called, so let the callback thread die.
+ break;
+ }
+ while (!mQueue.empty() && mQueue.top().isExpired()) {
+ DelayedCallback callback = mQueue.top();
+ mQueue.pop();
+ lock.unlock();
+ callback.run();
+ lock.lock();
+ }
+ if (mQueue.empty()) {
+ // Wait until a new callback is scheduled.
+ mCondition.wait(mMutex);
+ } else {
+ // Wait until next callback expires, or a new one is scheduled.
+ mCondition.wait_until(mMutex, mQueue.top().getExpiration());
+ }
+ }
+}
+
+// -------------------------------------------------------------------------------------------------
+
+}; // namespace vibrator
+
+}; // namespace android
diff --git a/services/vibratorservice/VibratorHalController.cpp b/services/vibratorservice/VibratorHalController.cpp
new file mode 100644
index 0000000..c1795f5
--- /dev/null
+++ b/services/vibratorservice/VibratorHalController.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "VibratorHalController"
+
+#include <android/hardware/vibrator/1.3/IVibrator.h>
+#include <android/hardware/vibrator/IVibrator.h>
+#include <binder/IServiceManager.h>
+#include <hardware/vibrator.h>
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalController.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::CompositePrimitive;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+
+using std::chrono::milliseconds;
+
+namespace V1_0 = android::hardware::vibrator::V1_0;
+namespace V1_1 = android::hardware::vibrator::V1_1;
+namespace V1_2 = android::hardware::vibrator::V1_2;
+namespace V1_3 = android::hardware::vibrator::V1_3;
+namespace Aidl = android::hardware::vibrator;
+
+namespace android {
+
+namespace vibrator {
+
+// -------------------------------------------------------------------------------------------------
+
+std::shared_ptr<HalWrapper> connectHal(std::shared_ptr<CallbackScheduler> scheduler) {
+ static bool gHalExists = true;
+ if (!gHalExists) {
+ // We already tried to connect to all of the vibrator HAL versions and none was available.
+ return nullptr;
+ }
+
+ sp<Aidl::IVibrator> aidlHal = waitForVintfService<Aidl::IVibrator>();
+ if (aidlHal) {
+ ALOGV("Successfully connected to Vibrator HAL AIDL service.");
+ return std::make_shared<AidlHalWrapper>(std::move(scheduler), aidlHal);
+ }
+
+ sp<V1_0::IVibrator> halV1_0 = V1_0::IVibrator::getService();
+ if (halV1_0 == nullptr) {
+ ALOGV("Vibrator HAL service not available.");
+ gHalExists = false;
+ return nullptr;
+ }
+
+ sp<V1_3::IVibrator> halV1_3 = V1_3::IVibrator::castFrom(halV1_0);
+ if (halV1_3) {
+ ALOGV("Successfully connected to Vibrator HAL v1.3 service.");
+ return std::make_shared<HidlHalWrapperV1_3>(std::move(scheduler), halV1_3);
+ }
+ sp<V1_2::IVibrator> halV1_2 = V1_2::IVibrator::castFrom(halV1_0);
+ if (halV1_2) {
+ ALOGV("Successfully connected to Vibrator HAL v1.2 service.");
+ return std::make_shared<HidlHalWrapperV1_2>(std::move(scheduler), halV1_2);
+ }
+ sp<V1_1::IVibrator> halV1_1 = V1_1::IVibrator::castFrom(halV1_0);
+ if (halV1_1) {
+ ALOGV("Successfully connected to Vibrator HAL v1.1 service.");
+ return std::make_shared<HidlHalWrapperV1_1>(std::move(scheduler), halV1_1);
+ }
+ ALOGV("Successfully connected to Vibrator HAL v1.0 service.");
+ return std::make_shared<HidlHalWrapperV1_0>(std::move(scheduler), halV1_0);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+bool HalController::init() {
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ if (mConnectedHal == nullptr) {
+ mConnectedHal = mConnector(mCallbackScheduler);
+ }
+ return mConnectedHal != nullptr;
+}
+
+void HalController::tryReconnect() {
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ if (mConnectedHal == nullptr) {
+ mConnectedHal = mConnector(mCallbackScheduler);
+ } else {
+ mConnectedHal->tryReconnect();
+ }
+}
+
+}; // namespace vibrator
+
+}; // namespace android
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
new file mode 100644
index 0000000..1010aa5
--- /dev/null
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -0,0 +1,628 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "VibratorHalWrapper"
+
+#include <android/hardware/vibrator/1.3/IVibrator.h>
+#include <android/hardware/vibrator/IVibrator.h>
+#include <hardware/vibrator.h>
+#include <cmath>
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+using android::hardware::vibrator::Braking;
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::CompositePrimitive;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+using android::hardware::vibrator::PrimitivePwle;
+
+using std::chrono::milliseconds;
+
+namespace V1_0 = android::hardware::vibrator::V1_0;
+namespace V1_1 = android::hardware::vibrator::V1_1;
+namespace V1_2 = android::hardware::vibrator::V1_2;
+namespace V1_3 = android::hardware::vibrator::V1_3;
+namespace Aidl = android::hardware::vibrator;
+
+namespace android {
+
+namespace vibrator {
+
+// -------------------------------------------------------------------------------------------------
+
+template <class T>
+bool isStaticCastValid(Effect effect) {
+ T castEffect = static_cast<T>(effect);
+ auto iter = hardware::hidl_enum_range<T>();
+ return castEffect >= *iter.begin() && castEffect <= *std::prev(iter.end());
+}
+
+// -------------------------------------------------------------------------------------------------
+
+const constexpr char* STATUS_T_ERROR_MESSAGE_PREFIX = "status_t = ";
+const constexpr char* STATUS_V_1_0_ERROR_MESSAGE_PREFIX =
+ "android::hardware::vibrator::V1_0::Status = ";
+
+template <typename T>
+HalResult<T> HalResult<T>::fromStatus(V1_0::Status status, T data) {
+ switch (status) {
+ case V1_0::Status::OK:
+ return HalResult<T>::ok(data);
+ case V1_0::Status::UNSUPPORTED_OPERATION:
+ return HalResult<T>::unsupported();
+ default:
+ return HalResult<T>::failed(STATUS_V_1_0_ERROR_MESSAGE_PREFIX + toString(status));
+ }
+}
+
+template <typename T>
+template <typename R>
+HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, T data) {
+ return ret.isOk() ? HalResult<T>::ok(data) : HalResult<T>::failed(ret.description());
+}
+
+template <typename T>
+template <typename R>
+HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, V1_0::Status status, T data) {
+ return ret.isOk() ? HalResult<T>::fromStatus(status, data)
+ : HalResult<T>::failed(ret.description());
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<void> HalResult<void>::fromStatus(status_t status) {
+ if (status == android::OK) {
+ return HalResult<void>::ok();
+ }
+ return HalResult<void>::failed(STATUS_T_ERROR_MESSAGE_PREFIX + statusToString(status));
+}
+
+HalResult<void> HalResult<void>::fromStatus(binder::Status status) {
+ if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+ return HalResult<void>::unsupported();
+ }
+ if (status.isOk()) {
+ return HalResult<void>::ok();
+ }
+ return HalResult<void>::failed(std::string(status.toString8().c_str()));
+}
+
+HalResult<void> HalResult<void>::fromStatus(V1_0::Status status) {
+ switch (status) {
+ case V1_0::Status::OK:
+ return HalResult<void>::ok();
+ case V1_0::Status::UNSUPPORTED_OPERATION:
+ return HalResult<void>::unsupported();
+ default:
+ return HalResult<void>::failed(STATUS_V_1_0_ERROR_MESSAGE_PREFIX + toString(status));
+ }
+}
+
+template <typename R>
+HalResult<void> HalResult<void>::fromReturn(hardware::Return<R>& ret) {
+ return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed(ret.description());
+}
+
+// -------------------------------------------------------------------------------------------------
+
+Info HalWrapper::getInfo() {
+ getCapabilities();
+ getPrimitiveDurations();
+ std::lock_guard<std::mutex> lock(mInfoMutex);
+ if (mInfoCache.mSupportedEffects.isFailed()) {
+ mInfoCache.mSupportedEffects = getSupportedEffectsInternal();
+ }
+ if (mInfoCache.mSupportedBraking.isFailed()) {
+ mInfoCache.mSupportedBraking = getSupportedBrakingInternal();
+ }
+ if (mInfoCache.mMinFrequency.isFailed()) {
+ mInfoCache.mMinFrequency = getMinFrequencyInternal();
+ }
+ if (mInfoCache.mResonantFrequency.isFailed()) {
+ mInfoCache.mResonantFrequency = getResonantFrequencyInternal();
+ }
+ if (mInfoCache.mFrequencyResolution.isFailed()) {
+ mInfoCache.mFrequencyResolution = getFrequencyResolutionInternal();
+ }
+ if (mInfoCache.mQFactor.isFailed()) {
+ mInfoCache.mQFactor = getQFactorInternal();
+ }
+ if (mInfoCache.mMaxAmplitudes.isFailed()) {
+ mInfoCache.mMaxAmplitudes = getMaxAmplitudesInternal();
+ }
+ return mInfoCache.get();
+}
+
+HalResult<milliseconds> HalWrapper::performComposedEffect(const std::vector<CompositeEffect>&,
+ const std::function<void()>&) {
+ ALOGV("Skipped performComposedEffect because it's not available in Vibrator HAL");
+ return HalResult<milliseconds>::unsupported();
+}
+
+HalResult<void> HalWrapper::performPwleEffect(const std::vector<PrimitivePwle>&,
+ const std::function<void()>&) {
+ ALOGV("Skipped performPwleEffect because it's not available in Vibrator HAL");
+ return HalResult<void>::unsupported();
+}
+
+HalResult<Capabilities> HalWrapper::getCapabilities() {
+ std::lock_guard<std::mutex> lock(mInfoMutex);
+ if (mInfoCache.mCapabilities.isFailed()) {
+ mInfoCache.mCapabilities = getCapabilitiesInternal();
+ }
+ return mInfoCache.mCapabilities;
+}
+
+HalResult<std::vector<milliseconds>> HalWrapper::getPrimitiveDurations() {
+ std::lock_guard<std::mutex> lock(mInfoMutex);
+ if (mInfoCache.mSupportedPrimitives.isFailed()) {
+ mInfoCache.mSupportedPrimitives = getSupportedPrimitivesInternal();
+ if (mInfoCache.mSupportedPrimitives.isUnsupported()) {
+ mInfoCache.mPrimitiveDurations = HalResult<std::vector<milliseconds>>::unsupported();
+ }
+ }
+ if (mInfoCache.mPrimitiveDurations.isFailed() && mInfoCache.mSupportedPrimitives.isOk()) {
+ mInfoCache.mPrimitiveDurations =
+ getPrimitiveDurationsInternal(mInfoCache.mSupportedPrimitives.value());
+ }
+ return mInfoCache.mPrimitiveDurations;
+}
+
+HalResult<std::vector<Effect>> HalWrapper::getSupportedEffectsInternal() {
+ ALOGV("Skipped getSupportedEffects because it's not available in Vibrator HAL");
+ return HalResult<std::vector<Effect>>::unsupported();
+}
+
+HalResult<std::vector<Braking>> HalWrapper::getSupportedBrakingInternal() {
+ ALOGV("Skipped getSupportedBraking because it's not available in Vibrator HAL");
+ return HalResult<std::vector<Braking>>::unsupported();
+}
+
+HalResult<std::vector<CompositePrimitive>> HalWrapper::getSupportedPrimitivesInternal() {
+ ALOGV("Skipped getSupportedPrimitives because it's not available in Vibrator HAL");
+ return HalResult<std::vector<CompositePrimitive>>::unsupported();
+}
+
+HalResult<std::vector<milliseconds>> HalWrapper::getPrimitiveDurationsInternal(
+ const std::vector<CompositePrimitive>&) {
+ ALOGV("Skipped getPrimitiveDurations because it's not available in Vibrator HAL");
+ return HalResult<std::vector<milliseconds>>::unsupported();
+}
+
+HalResult<float> HalWrapper::getMinFrequencyInternal() {
+ ALOGV("Skipped getMinFrequency because it's not available in Vibrator HAL");
+ return HalResult<float>::unsupported();
+}
+
+HalResult<float> HalWrapper::getResonantFrequencyInternal() {
+ ALOGV("Skipped getResonantFrequency because it's not available in Vibrator HAL");
+ return HalResult<float>::unsupported();
+}
+
+HalResult<float> HalWrapper::getFrequencyResolutionInternal() {
+ ALOGV("Skipped getFrequencyResolution because it's not available in Vibrator HAL");
+ return HalResult<float>::unsupported();
+}
+
+HalResult<float> HalWrapper::getQFactorInternal() {
+ ALOGV("Skipped getQFactor because it's not available in Vibrator HAL");
+ return HalResult<float>::unsupported();
+}
+
+HalResult<std::vector<float>> HalWrapper::getMaxAmplitudesInternal() {
+ ALOGV("Skipped getMaxAmplitudes because it's not available in Vibrator HAL");
+ return HalResult<std::vector<float>>::unsupported();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<void> AidlHalWrapper::ping() {
+ return HalResult<void>::fromStatus(IInterface::asBinder(getHal())->pingBinder());
+}
+
+void AidlHalWrapper::tryReconnect() {
+ auto result = mReconnectFn();
+ if (!result.isOk()) {
+ return;
+ }
+ sp<Aidl::IVibrator> newHandle = result.value();
+ if (newHandle) {
+ std::lock_guard<std::mutex> lock(mHandleMutex);
+ mHandle = std::move(newHandle);
+ }
+}
+
+HalResult<void> AidlHalWrapper::on(milliseconds timeout,
+ const std::function<void()>& completionCallback) {
+ HalResult<Capabilities> capabilities = getCapabilities();
+ bool supportsCallback = capabilities.isOk() &&
+ static_cast<int32_t>(capabilities.value() & Capabilities::ON_CALLBACK);
+ auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr;
+
+ auto ret = HalResult<void>::fromStatus(getHal()->on(timeout.count(), cb));
+ if (!supportsCallback && ret.isOk()) {
+ mCallbackScheduler->schedule(completionCallback, timeout);
+ }
+
+ return ret;
+}
+
+HalResult<void> AidlHalWrapper::off() {
+ return HalResult<void>::fromStatus(getHal()->off());
+}
+
+HalResult<void> AidlHalWrapper::setAmplitude(float amplitude) {
+ return HalResult<void>::fromStatus(getHal()->setAmplitude(amplitude));
+}
+
+HalResult<void> AidlHalWrapper::setExternalControl(bool enabled) {
+ return HalResult<void>::fromStatus(getHal()->setExternalControl(enabled));
+}
+
+HalResult<void> AidlHalWrapper::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) {
+ return HalResult<void>::fromStatus(getHal()->alwaysOnEnable(id, effect, strength));
+}
+
+HalResult<void> AidlHalWrapper::alwaysOnDisable(int32_t id) {
+ return HalResult<void>::fromStatus(getHal()->alwaysOnDisable(id));
+}
+
+HalResult<milliseconds> AidlHalWrapper::performEffect(
+ Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+ HalResult<Capabilities> capabilities = getCapabilities();
+ bool supportsCallback = capabilities.isOk() &&
+ static_cast<int32_t>(capabilities.value() & Capabilities::PERFORM_CALLBACK);
+ auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr;
+
+ int32_t lengthMs;
+ auto result = getHal()->perform(effect, strength, cb, &lengthMs);
+ milliseconds length = milliseconds(lengthMs);
+
+ auto ret = HalResult<milliseconds>::fromStatus(result, length);
+ if (!supportsCallback && ret.isOk()) {
+ mCallbackScheduler->schedule(completionCallback, length);
+ }
+
+ return ret;
+}
+
+HalResult<milliseconds> AidlHalWrapper::performComposedEffect(
+ const std::vector<CompositeEffect>& primitives,
+ const std::function<void()>& completionCallback) {
+ // This method should always support callbacks, so no need to double check.
+ auto cb = new HalCallbackWrapper(completionCallback);
+
+ auto durations = getPrimitiveDurations().valueOr({});
+ milliseconds duration(0);
+ for (const auto& effect : primitives) {
+ auto primitiveIdx = static_cast<size_t>(effect.primitive);
+ if (primitiveIdx < durations.size()) {
+ duration += durations[primitiveIdx];
+ } else {
+ // Make sure the returned duration is positive to indicate successful vibration.
+ duration += milliseconds(1);
+ }
+ duration += milliseconds(effect.delayMs);
+ }
+
+ return HalResult<milliseconds>::fromStatus(getHal()->compose(primitives, cb), duration);
+}
+
+HalResult<void> AidlHalWrapper::performPwleEffect(const std::vector<PrimitivePwle>& primitives,
+ const std::function<void()>& completionCallback) {
+ // This method should always support callbacks, so no need to double check.
+ auto cb = new HalCallbackWrapper(completionCallback);
+ return HalResult<void>::fromStatus(getHal()->composePwle(primitives, cb));
+}
+
+HalResult<Capabilities> AidlHalWrapper::getCapabilitiesInternal() {
+ int32_t capabilities = 0;
+ auto result = getHal()->getCapabilities(&capabilities);
+ return HalResult<Capabilities>::fromStatus(result, static_cast<Capabilities>(capabilities));
+}
+
+HalResult<std::vector<Effect>> AidlHalWrapper::getSupportedEffectsInternal() {
+ std::vector<Effect> supportedEffects;
+ auto result = getHal()->getSupportedEffects(&supportedEffects);
+ return HalResult<std::vector<Effect>>::fromStatus(result, supportedEffects);
+}
+
+HalResult<std::vector<Braking>> AidlHalWrapper::getSupportedBrakingInternal() {
+ std::vector<Braking> supportedBraking;
+ auto result = getHal()->getSupportedBraking(&supportedBraking);
+ return HalResult<std::vector<Braking>>::fromStatus(result, supportedBraking);
+}
+
+HalResult<std::vector<CompositePrimitive>> AidlHalWrapper::getSupportedPrimitivesInternal() {
+ std::vector<CompositePrimitive> supportedPrimitives;
+ auto result = getHal()->getSupportedPrimitives(&supportedPrimitives);
+ return HalResult<std::vector<CompositePrimitive>>::fromStatus(result, supportedPrimitives);
+}
+
+HalResult<std::vector<milliseconds>> AidlHalWrapper::getPrimitiveDurationsInternal(
+ const std::vector<CompositePrimitive>& supportedPrimitives) {
+ std::vector<milliseconds> durations;
+ constexpr auto primitiveRange = enum_range<CompositePrimitive>();
+ constexpr auto primitiveCount = std::distance(primitiveRange.begin(), primitiveRange.end());
+ durations.resize(primitiveCount);
+
+ for (auto primitive : supportedPrimitives) {
+ auto primitiveIdx = static_cast<size_t>(primitive);
+ if (primitiveIdx >= durations.size()) {
+ // Safety check, should not happen if enum_range is correct.
+ continue;
+ }
+ int32_t duration = 0;
+ auto status = getHal()->getPrimitiveDuration(primitive, &duration);
+ if (!status.isOk()) {
+ return HalResult<std::vector<milliseconds>>::failed(status.toString8().c_str());
+ }
+ durations[primitiveIdx] = milliseconds(duration);
+ }
+
+ return HalResult<std::vector<milliseconds>>::ok(durations);
+}
+
+HalResult<float> AidlHalWrapper::getMinFrequencyInternal() {
+ float minFrequency = 0;
+ auto result = getHal()->getFrequencyMinimum(&minFrequency);
+ return HalResult<float>::fromStatus(result, minFrequency);
+}
+
+HalResult<float> AidlHalWrapper::getResonantFrequencyInternal() {
+ float f0 = 0;
+ auto result = getHal()->getResonantFrequency(&f0);
+ return HalResult<float>::fromStatus(result, f0);
+}
+
+HalResult<float> AidlHalWrapper::getFrequencyResolutionInternal() {
+ float frequencyResolution = 0;
+ auto result = getHal()->getFrequencyResolution(&frequencyResolution);
+ return HalResult<float>::fromStatus(result, frequencyResolution);
+}
+
+HalResult<float> AidlHalWrapper::getQFactorInternal() {
+ float qFactor = 0;
+ auto result = getHal()->getQFactor(&qFactor);
+ return HalResult<float>::fromStatus(result, qFactor);
+}
+
+HalResult<std::vector<float>> AidlHalWrapper::getMaxAmplitudesInternal() {
+ std::vector<float> amplitudes;
+ auto result = getHal()->getBandwidthAmplitudeMap(&litudes);
+ return HalResult<std::vector<float>>::fromStatus(result, amplitudes);
+}
+
+sp<Aidl::IVibrator> AidlHalWrapper::getHal() {
+ std::lock_guard<std::mutex> lock(mHandleMutex);
+ return mHandle;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::ping() {
+ auto result = getHal()->ping();
+ return HalResult<void>::fromReturn(result);
+}
+
+template <typename I>
+void HidlHalWrapper<I>::tryReconnect() {
+ sp<I> newHandle = I::tryGetService();
+ if (newHandle) {
+ std::lock_guard<std::mutex> lock(mHandleMutex);
+ mHandle = std::move(newHandle);
+ }
+}
+
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::on(milliseconds timeout,
+ const std::function<void()>& completionCallback) {
+ auto result = getHal()->on(timeout.count());
+ auto ret = HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
+ if (ret.isOk()) {
+ mCallbackScheduler->schedule(completionCallback, timeout);
+ }
+ return ret;
+}
+
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::off() {
+ auto result = getHal()->off();
+ return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
+}
+
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::setAmplitude(float amplitude) {
+ uint8_t amp = static_cast<uint8_t>(amplitude * std::numeric_limits<uint8_t>::max());
+ auto result = getHal()->setAmplitude(amp);
+ return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
+}
+
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::setExternalControl(bool) {
+ ALOGV("Skipped setExternalControl because Vibrator HAL does not support it");
+ return HalResult<void>::unsupported();
+}
+
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::alwaysOnEnable(int32_t, Effect, EffectStrength) {
+ ALOGV("Skipped alwaysOnEnable because Vibrator HAL AIDL is not available");
+ return HalResult<void>::unsupported();
+}
+
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::alwaysOnDisable(int32_t) {
+ ALOGV("Skipped alwaysOnDisable because Vibrator HAL AIDL is not available");
+ return HalResult<void>::unsupported();
+}
+
+template <typename I>
+HalResult<Capabilities> HidlHalWrapper<I>::getCapabilitiesInternal() {
+ hardware::Return<bool> result = getHal()->supportsAmplitudeControl();
+ Capabilities capabilities =
+ result.withDefault(false) ? Capabilities::AMPLITUDE_CONTROL : Capabilities::NONE;
+ return HalResult<Capabilities>::fromReturn(result, capabilities);
+}
+
+template <typename I>
+template <typename T>
+HalResult<milliseconds> HidlHalWrapper<I>::performInternal(
+ perform_fn<T> performFn, sp<I> handle, T effect, EffectStrength strength,
+ const std::function<void()>& completionCallback) {
+ V1_0::Status status;
+ int32_t lengthMs;
+ auto effectCallback = [&status, &lengthMs](V1_0::Status retStatus, uint32_t retLengthMs) {
+ status = retStatus;
+ lengthMs = retLengthMs;
+ };
+
+ V1_0::EffectStrength effectStrength = static_cast<V1_0::EffectStrength>(strength);
+ auto result = std::invoke(performFn, handle, effect, effectStrength, effectCallback);
+ milliseconds length = milliseconds(lengthMs);
+
+ auto ret = HalResult<milliseconds>::fromReturn(result, status, length);
+ if (ret.isOk()) {
+ mCallbackScheduler->schedule(completionCallback, length);
+ }
+
+ return ret;
+}
+
+template <typename I>
+sp<I> HidlHalWrapper<I>::getHal() {
+ std::lock_guard<std::mutex> lock(mHandleMutex);
+ return mHandle;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<milliseconds> HidlHalWrapperV1_0::performEffect(
+ Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+ if (isStaticCastValid<V1_0::Effect>(effect)) {
+ return performInternal(&V1_0::IVibrator::perform, getHal(),
+ static_cast<V1_0::Effect>(effect), strength, completionCallback);
+ }
+
+ ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
+ Aidl::toString(effect).c_str());
+ return HalResult<milliseconds>::unsupported();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<milliseconds> HidlHalWrapperV1_1::performEffect(
+ Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+ if (isStaticCastValid<V1_0::Effect>(effect)) {
+ return performInternal(&V1_1::IVibrator::perform, getHal(),
+ static_cast<V1_0::Effect>(effect), strength, completionCallback);
+ }
+ if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
+ return performInternal(&V1_1::IVibrator::perform_1_1, getHal(),
+ static_cast<V1_1::Effect_1_1>(effect), strength, completionCallback);
+ }
+
+ ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
+ Aidl::toString(effect).c_str());
+ return HalResult<milliseconds>::unsupported();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<milliseconds> HidlHalWrapperV1_2::performEffect(
+ Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+ if (isStaticCastValid<V1_0::Effect>(effect)) {
+ return performInternal(&V1_2::IVibrator::perform, getHal(),
+ static_cast<V1_0::Effect>(effect), strength, completionCallback);
+ }
+ if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
+ return performInternal(&V1_2::IVibrator::perform_1_1, getHal(),
+ static_cast<V1_1::Effect_1_1>(effect), strength, completionCallback);
+ }
+ if (isStaticCastValid<V1_2::Effect>(effect)) {
+ return performInternal(&V1_2::IVibrator::perform_1_2, getHal(),
+ static_cast<V1_2::Effect>(effect), strength, completionCallback);
+ }
+
+ ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
+ Aidl::toString(effect).c_str());
+ return HalResult<milliseconds>::unsupported();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<void> HidlHalWrapperV1_3::setExternalControl(bool enabled) {
+ auto result = getHal()->setExternalControl(static_cast<uint32_t>(enabled));
+ return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
+}
+
+HalResult<milliseconds> HidlHalWrapperV1_3::performEffect(
+ Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+ if (isStaticCastValid<V1_0::Effect>(effect)) {
+ return performInternal(&V1_3::IVibrator::perform, getHal(),
+ static_cast<V1_0::Effect>(effect), strength, completionCallback);
+ }
+ if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
+ return performInternal(&V1_3::IVibrator::perform_1_1, getHal(),
+ static_cast<V1_1::Effect_1_1>(effect), strength, completionCallback);
+ }
+ if (isStaticCastValid<V1_2::Effect>(effect)) {
+ return performInternal(&V1_3::IVibrator::perform_1_2, getHal(),
+ static_cast<V1_2::Effect>(effect), strength, completionCallback);
+ }
+ if (isStaticCastValid<V1_3::Effect>(effect)) {
+ return performInternal(&V1_3::IVibrator::perform_1_3, getHal(),
+ static_cast<V1_3::Effect>(effect), strength, completionCallback);
+ }
+
+ ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
+ Aidl::toString(effect).c_str());
+ return HalResult<milliseconds>::unsupported();
+}
+
+HalResult<Capabilities> HidlHalWrapperV1_3::getCapabilitiesInternal() {
+ Capabilities capabilities = Capabilities::NONE;
+
+ sp<V1_3::IVibrator> hal = getHal();
+ auto amplitudeResult = hal->supportsAmplitudeControl();
+ if (!amplitudeResult.isOk()) {
+ return HalResult<Capabilities>::fromReturn(amplitudeResult, capabilities);
+ }
+
+ auto externalControlResult = hal->supportsExternalControl();
+ if (amplitudeResult.withDefault(false)) {
+ capabilities |= Capabilities::AMPLITUDE_CONTROL;
+ }
+ if (externalControlResult.withDefault(false)) {
+ capabilities |= Capabilities::EXTERNAL_CONTROL;
+
+ if (amplitudeResult.withDefault(false)) {
+ capabilities |= Capabilities::EXTERNAL_AMPLITUDE_CONTROL;
+ }
+ }
+
+ return HalResult<Capabilities>::fromReturn(externalControlResult, capabilities);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+}; // namespace vibrator
+
+}; // namespace android
diff --git a/services/vibratorservice/VibratorManagerHalController.cpp b/services/vibratorservice/VibratorManagerHalController.cpp
new file mode 100644
index 0000000..6bf6581
--- /dev/null
+++ b/services/vibratorservice/VibratorManagerHalController.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "VibratorManagerHalController"
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorManagerHalController.h>
+
+namespace Aidl = android::hardware::vibrator;
+
+namespace android {
+
+namespace vibrator {
+
+std::shared_ptr<ManagerHalWrapper> connectManagerHal(std::shared_ptr<CallbackScheduler> scheduler) {
+ static bool gHalExists = true;
+ if (gHalExists) {
+ sp<Aidl::IVibratorManager> hal = waitForVintfService<Aidl::IVibratorManager>();
+ if (hal) {
+ ALOGV("Successfully connected to VibratorManager HAL AIDL service.");
+ return std::make_shared<AidlManagerHalWrapper>(std::move(scheduler), hal);
+ }
+ }
+
+ gHalExists = false;
+ return std::make_shared<LegacyManagerHalWrapper>();
+}
+
+static constexpr int MAX_RETRIES = 1;
+
+template <typename T>
+HalResult<T> ManagerHalController::processHalResult(HalResult<T> result, const char* functionName) {
+ if (result.isFailed()) {
+ ALOGE("%s failed: %s", functionName, result.errorMessage());
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ mConnectedHal->tryReconnect();
+ }
+ return result;
+}
+
+template <typename T>
+HalResult<T> ManagerHalController::apply(ManagerHalController::hal_fn<T>& halFn,
+ const char* functionName) {
+ std::shared_ptr<ManagerHalWrapper> hal = nullptr;
+ {
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ if (mConnectedHal == nullptr) {
+ // Init was never called, so connect to HAL for the first time during this call.
+ mConnectedHal = mConnector(mCallbackScheduler);
+
+ if (mConnectedHal == nullptr) {
+ ALOGV("Skipped %s because VibratorManager HAL is not available", functionName);
+ return HalResult<T>::unsupported();
+ }
+ }
+ hal = mConnectedHal;
+ }
+
+ HalResult<T> ret = processHalResult(halFn(hal), functionName);
+ for (int i = 0; i < MAX_RETRIES && ret.isFailed(); i++) {
+ ret = processHalResult(halFn(hal), functionName);
+ }
+
+ return ret;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void ManagerHalController::init() {
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ if (mConnectedHal == nullptr) {
+ mConnectedHal = mConnector(mCallbackScheduler);
+ }
+}
+
+HalResult<void> ManagerHalController::ping() {
+ hal_fn<void> pingFn = [](std::shared_ptr<ManagerHalWrapper> hal) { return hal->ping(); };
+ return apply(pingFn, "ping");
+}
+
+void ManagerHalController::tryReconnect() {
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ if (mConnectedHal == nullptr) {
+ mConnectedHal = mConnector(mCallbackScheduler);
+ } else {
+ mConnectedHal->tryReconnect();
+ }
+}
+
+HalResult<ManagerCapabilities> ManagerHalController::getCapabilities() {
+ hal_fn<ManagerCapabilities> getCapabilitiesFn = [](std::shared_ptr<ManagerHalWrapper> hal) {
+ return hal->getCapabilities();
+ };
+ return apply(getCapabilitiesFn, "getCapabilities");
+}
+
+HalResult<std::vector<int32_t>> ManagerHalController::getVibratorIds() {
+ hal_fn<std::vector<int32_t>> getVibratorIdsFn = [](std::shared_ptr<ManagerHalWrapper> hal) {
+ return hal->getVibratorIds();
+ };
+ return apply(getVibratorIdsFn, "getVibratorIds");
+}
+
+HalResult<std::shared_ptr<HalController>> ManagerHalController::getVibrator(int32_t id) {
+ hal_fn<std::shared_ptr<HalController>> getVibratorFn =
+ [&](std::shared_ptr<ManagerHalWrapper> hal) { return hal->getVibrator(id); };
+ return apply(getVibratorFn, "getVibrator");
+}
+
+HalResult<void> ManagerHalController::prepareSynced(const std::vector<int32_t>& ids) {
+ hal_fn<void> prepareSyncedFn = [&](std::shared_ptr<ManagerHalWrapper> hal) {
+ return hal->prepareSynced(ids);
+ };
+ return apply(prepareSyncedFn, "prepareSynced");
+}
+
+HalResult<void> ManagerHalController::triggerSynced(
+ const std::function<void()>& completionCallback) {
+ hal_fn<void> triggerSyncedFn = [&](std::shared_ptr<ManagerHalWrapper> hal) {
+ return hal->triggerSynced(completionCallback);
+ };
+ return apply(triggerSyncedFn, "triggerSynced");
+}
+
+HalResult<void> ManagerHalController::cancelSynced() {
+ hal_fn<void> cancelSyncedFn = [](std::shared_ptr<ManagerHalWrapper> hal) {
+ return hal->cancelSynced();
+ };
+ return apply(cancelSyncedFn, "cancelSynced");
+}
+
+}; // namespace vibrator
+
+}; // namespace android
diff --git a/services/vibratorservice/VibratorManagerHalWrapper.cpp b/services/vibratorservice/VibratorManagerHalWrapper.cpp
new file mode 100644
index 0000000..6e660e7
--- /dev/null
+++ b/services/vibratorservice/VibratorManagerHalWrapper.cpp
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "VibratorManagerHalWrapper"
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorManagerHalWrapper.h>
+
+namespace Aidl = android::hardware::vibrator;
+
+namespace android {
+
+namespace vibrator {
+
+constexpr int32_t SINGLE_VIBRATOR_ID = 0;
+const constexpr char* MISSING_VIBRATOR_MESSAGE_PREFIX = "No vibrator with id=";
+
+HalResult<void> LegacyManagerHalWrapper::ping() {
+ auto pingFn = [](HalWrapper* hal) { return hal->ping(); };
+ return mController->doWithRetry<void>(pingFn, "ping");
+}
+
+void LegacyManagerHalWrapper::tryReconnect() {
+ mController->tryReconnect();
+}
+
+HalResult<ManagerCapabilities> LegacyManagerHalWrapper::getCapabilities() {
+ return HalResult<ManagerCapabilities>::ok(ManagerCapabilities::NONE);
+}
+
+HalResult<std::vector<int32_t>> LegacyManagerHalWrapper::getVibratorIds() {
+ if (mController->init()) {
+ return HalResult<std::vector<int32_t>>::ok(std::vector<int32_t>(1, SINGLE_VIBRATOR_ID));
+ }
+ // Controller.init did not connect to any vibrator HAL service, so the device has no vibrator.
+ return HalResult<std::vector<int32_t>>::ok(std::vector<int32_t>());
+}
+
+HalResult<std::shared_ptr<HalController>> LegacyManagerHalWrapper::getVibrator(int32_t id) {
+ if (id == SINGLE_VIBRATOR_ID && mController->init()) {
+ return HalResult<std::shared_ptr<HalController>>::ok(mController);
+ }
+ // Controller.init did not connect to any vibrator HAL service, so the device has no vibrator.
+ return HalResult<std::shared_ptr<HalController>>::failed(MISSING_VIBRATOR_MESSAGE_PREFIX +
+ std::to_string(id));
+}
+
+HalResult<void> LegacyManagerHalWrapper::prepareSynced(const std::vector<int32_t>&) {
+ return HalResult<void>::unsupported();
+}
+
+HalResult<void> LegacyManagerHalWrapper::triggerSynced(const std::function<void()>&) {
+ return HalResult<void>::unsupported();
+}
+
+HalResult<void> LegacyManagerHalWrapper::cancelSynced() {
+ return HalResult<void>::unsupported();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+std::shared_ptr<HalWrapper> AidlManagerHalWrapper::connectToVibrator(
+ int32_t vibratorId, std::shared_ptr<CallbackScheduler> callbackScheduler) {
+ std::function<HalResult<sp<Aidl::IVibrator>>()> reconnectFn = [=]() {
+ sp<Aidl::IVibrator> vibrator;
+ auto result = this->getHal()->getVibrator(vibratorId, &vibrator);
+ return HalResult<sp<Aidl::IVibrator>>::fromStatus(result, vibrator);
+ };
+ auto result = reconnectFn();
+ if (!result.isOk()) {
+ return nullptr;
+ }
+ auto vibrator = result.value();
+ if (!vibrator) {
+ return nullptr;
+ }
+ return std::move(std::make_unique<AidlHalWrapper>(std::move(callbackScheduler),
+ std::move(vibrator), reconnectFn));
+}
+
+HalResult<void> AidlManagerHalWrapper::ping() {
+ return HalResult<void>::fromStatus(IInterface::asBinder(getHal())->pingBinder());
+}
+
+void AidlManagerHalWrapper::tryReconnect() {
+ sp<Aidl::IVibratorManager> newHandle = checkVintfService<Aidl::IVibratorManager>();
+ if (newHandle) {
+ std::lock_guard<std::mutex> lock(mHandleMutex);
+ mHandle = std::move(newHandle);
+ }
+}
+
+HalResult<ManagerCapabilities> AidlManagerHalWrapper::getCapabilities() {
+ std::lock_guard<std::mutex> lock(mCapabilitiesMutex);
+ if (mCapabilities.has_value()) {
+ // Return copy of cached value.
+ return HalResult<ManagerCapabilities>::ok(*mCapabilities);
+ }
+ int32_t cap = 0;
+ auto result = getHal()->getCapabilities(&cap);
+ auto ret = HalResult<ManagerCapabilities>::fromStatus(result,
+ static_cast<ManagerCapabilities>(cap));
+ if (ret.isOk()) {
+ // Cache copy of returned value.
+ mCapabilities.emplace(ret.value());
+ }
+ return ret;
+}
+
+HalResult<std::vector<int32_t>> AidlManagerHalWrapper::getVibratorIds() {
+ std::lock_guard<std::mutex> lock(mVibratorsMutex);
+ if (mVibratorIds.has_value()) {
+ // Return copy of cached values.
+ return HalResult<std::vector<int32_t>>::ok(*mVibratorIds);
+ }
+ std::vector<int32_t> ids;
+ auto result = getHal()->getVibratorIds(&ids);
+ auto ret = HalResult<std::vector<int32_t>>::fromStatus(result, ids);
+ if (ret.isOk()) {
+ // Cache copy of returned value and the individual controllers.
+ mVibratorIds.emplace(ret.value());
+ for (auto& id : ids) {
+ HalController::Connector connector = [&, id](auto scheduler) {
+ return this->connectToVibrator(id, scheduler);
+ };
+ auto controller = std::make_unique<HalController>(mCallbackScheduler, connector);
+ mVibrators[id] = std::move(controller);
+ }
+ }
+ return ret;
+}
+
+HalResult<std::shared_ptr<HalController>> AidlManagerHalWrapper::getVibrator(int32_t id) {
+ // Make sure we cache vibrator ids and initialize the individual controllers.
+ getVibratorIds();
+ std::lock_guard<std::mutex> lock(mVibratorsMutex);
+ auto it = mVibrators.find(id);
+ if (it != mVibrators.end()) {
+ return HalResult<std::shared_ptr<HalController>>::ok(it->second);
+ }
+ return HalResult<std::shared_ptr<HalController>>::failed(MISSING_VIBRATOR_MESSAGE_PREFIX +
+ std::to_string(id));
+}
+
+HalResult<void> AidlManagerHalWrapper::prepareSynced(const std::vector<int32_t>& ids) {
+ auto ret = HalResult<void>::fromStatus(getHal()->prepareSynced(ids));
+ if (ret.isOk()) {
+ // Force reload of all vibrator controllers that were prepared for a sync operation here.
+ // This will trigger calls to getVibrator(id) on each controller, so they can use the
+ // latest service provided by this manager.
+ std::lock_guard<std::mutex> lock(mVibratorsMutex);
+ for (auto& id : ids) {
+ auto it = mVibrators.find(id);
+ if (it != mVibrators.end()) {
+ it->second->tryReconnect();
+ }
+ }
+ }
+ return ret;
+}
+
+HalResult<void> AidlManagerHalWrapper::triggerSynced(
+ const std::function<void()>& completionCallback) {
+ HalResult<ManagerCapabilities> capabilities = getCapabilities();
+ bool supportsCallback = capabilities.isOk() &&
+ static_cast<int32_t>(capabilities.value() & ManagerCapabilities::TRIGGER_CALLBACK);
+ auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr;
+ return HalResult<void>::fromStatus(getHal()->triggerSynced(cb));
+}
+
+HalResult<void> AidlManagerHalWrapper::cancelSynced() {
+ auto ret = HalResult<void>::fromStatus(getHal()->cancelSynced());
+ if (ret.isOk()) {
+ // Force reload of all vibrator controllers that were prepared for a sync operation before.
+ // This will trigger calls to getVibrator(id) on each controller, so they can use the
+ // latest service provided by this manager.
+ std::lock_guard<std::mutex> lock(mVibratorsMutex);
+ for (auto& entry : mVibrators) {
+ entry.second->tryReconnect();
+ }
+ }
+ return ret;
+}
+
+sp<Aidl::IVibratorManager> AidlManagerHalWrapper::getHal() {
+ std::lock_guard<std::mutex> lock(mHandleMutex);
+ return mHandle;
+}
+
+}; // namespace vibrator
+
+}; // namespace android
diff --git a/services/vibratorservice/benchmarks/Android.bp b/services/vibratorservice/benchmarks/Android.bp
new file mode 100644
index 0000000..a468146
--- /dev/null
+++ b/services/vibratorservice/benchmarks/Android.bp
@@ -0,0 +1,46 @@
+// Copyright (C) 2020 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"],
+}
+
+cc_benchmark {
+ name: "libvibratorservice_benchmarks",
+ srcs: [
+ "VibratorHalControllerBenchmarks.cpp",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ "libvibratorservice",
+ "android.hardware.vibrator-V2-cpp",
+ "android.hardware.vibrator@1.0",
+ "android.hardware.vibrator@1.1",
+ "android.hardware.vibrator@1.2",
+ "android.hardware.vibrator@1.3",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+}
diff --git a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
new file mode 100644
index 0000000..53f3daf
--- /dev/null
+++ b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
@@ -0,0 +1,471 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "PowerHalControllerBenchmarks"
+
+#include <benchmark/benchmark.h>
+#include <vibratorservice/VibratorHalController.h>
+
+using ::android::enum_range;
+using ::android::hardware::vibrator::CompositeEffect;
+using ::android::hardware::vibrator::CompositePrimitive;
+using ::android::hardware::vibrator::Effect;
+using ::android::hardware::vibrator::EffectStrength;
+using ::benchmark::Counter;
+using ::benchmark::Fixture;
+using ::benchmark::kMicrosecond;
+using ::benchmark::State;
+using ::benchmark::internal::Benchmark;
+
+using namespace android;
+using namespace std::chrono_literals;
+
+class VibratorBench : public Fixture {
+public:
+ void SetUp(State& /*state*/) override { mController.init(); }
+
+ void TearDown(State& state) override { turnVibratorOff(state); }
+
+ static void DefaultConfig(Benchmark* b) { b->Unit(kMicrosecond); }
+
+ static void DefaultArgs(Benchmark* /*b*/) {
+ // none
+ }
+
+protected:
+ vibrator::HalController mController;
+
+ auto getOtherArg(const State& state, std::size_t index) const { return state.range(index + 0); }
+
+ bool hasCapabilities(vibrator::Capabilities&& query, State& state) {
+ auto result = mController.getInfo().capabilities;
+ if (result.isFailed()) {
+ state.SkipWithError(result.errorMessage());
+ return false;
+ }
+ if (!result.isOk()) {
+ return false;
+ }
+ return (result.value() & query) == query;
+ }
+
+ void turnVibratorOff(State& state) {
+ checkHalResult(halCall<void>(mController, [](auto hal) { return hal->off(); }), state);
+ }
+
+ template <class R>
+ bool checkHalResult(const vibrator::HalResult<R>& result, State& state) {
+ if (result.isFailed()) {
+ state.SkipWithError(result.errorMessage());
+ return false;
+ }
+ return true;
+ }
+
+ template <class R>
+ vibrator::HalResult<R> halCall(vibrator::HalController& controller,
+ const vibrator::HalFunction<vibrator::HalResult<R>>& halFn) {
+ return controller.doWithRetry<R>(halFn, "benchmark");
+ }
+};
+
+#define BENCHMARK_WRAPPER(fixt, test, code) \
+ BENCHMARK_DEFINE_F(fixt, test) \
+ /* NOLINTNEXTLINE */ \
+ (State& state){code} BENCHMARK_REGISTER_F(fixt, test) \
+ ->Apply(fixt::DefaultConfig) \
+ ->Apply(fixt::DefaultArgs)
+
+BENCHMARK_WRAPPER(VibratorBench, init, {
+ for (auto _ : state) {
+ state.PauseTiming();
+ vibrator::HalController controller;
+ state.ResumeTiming();
+ controller.init();
+ }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, initCached, {
+ for (auto _ : state) {
+ mController.init();
+ }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, ping, {
+ for (auto _ : state) {
+ state.ResumeTiming();
+ auto ret = halCall<void>(mController, [](auto hal) { return hal->ping(); });
+ state.PauseTiming();
+ checkHalResult(ret, state);
+ }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, tryReconnect, {
+ for (auto _ : state) {
+ mController.tryReconnect();
+ }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, on, {
+ auto duration = 60s;
+ auto callback = []() {};
+
+ for (auto _ : state) {
+ state.ResumeTiming();
+ auto ret =
+ halCall<void>(mController, [&](auto hal) { return hal->on(duration, callback); });
+ state.PauseTiming();
+ if (checkHalResult(ret, state)) {
+ turnVibratorOff(state);
+ }
+ }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, off, {
+ auto duration = 60s;
+ auto callback = []() {};
+
+ for (auto _ : state) {
+ state.PauseTiming();
+ auto ret =
+ halCall<void>(mController, [&](auto hal) { return hal->on(duration, callback); });
+ if (!checkHalResult(ret, state)) {
+ continue;
+ }
+ state.ResumeTiming();
+ turnVibratorOff(state);
+ }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, setAmplitude, {
+ if (!hasCapabilities(vibrator::Capabilities::AMPLITUDE_CONTROL, state)) {
+ return;
+ }
+
+ auto duration = 60s;
+ auto callback = []() {};
+ auto amplitude = 1.0f;
+
+ for (auto _ : state) {
+ state.PauseTiming();
+ vibrator::HalController controller;
+ controller.init();
+ auto result =
+ halCall<void>(controller, [&](auto hal) { return hal->on(duration, callback); });
+ if (!checkHalResult(result, state)) {
+ continue;
+ }
+ state.ResumeTiming();
+ auto ret =
+ halCall<void>(controller, [&](auto hal) { return hal->setAmplitude(amplitude); });
+ state.PauseTiming();
+ if (checkHalResult(ret, state)) {
+ turnVibratorOff(state);
+ }
+ }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, setAmplitudeCached, {
+ if (!hasCapabilities(vibrator::Capabilities::AMPLITUDE_CONTROL, state)) {
+ return;
+ }
+
+ auto duration = 6000s;
+ auto callback = []() {};
+ auto amplitude = 1.0f;
+
+ auto onResult =
+ halCall<void>(mController, [&](auto hal) { return hal->on(duration, callback); });
+ checkHalResult(onResult, state);
+
+ for (auto _ : state) {
+ auto ret =
+ halCall<void>(mController, [&](auto hal) { return hal->setAmplitude(amplitude); });
+ checkHalResult(ret, state);
+ }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, setExternalControl, {
+ if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_CONTROL, state)) {
+ return;
+ }
+
+ for (auto _ : state) {
+ state.PauseTiming();
+ vibrator::HalController controller;
+ controller.init();
+ state.ResumeTiming();
+ auto ret =
+ halCall<void>(controller, [](auto hal) { return hal->setExternalControl(true); });
+ state.PauseTiming();
+ if (checkHalResult(ret, state)) {
+ auto result = halCall<void>(controller,
+ [](auto hal) { return hal->setExternalControl(false); });
+ checkHalResult(result, state);
+ }
+ }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, setExternalControlCached, {
+ if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_CONTROL, state)) {
+ return;
+ }
+
+ for (auto _ : state) {
+ state.ResumeTiming();
+ auto result =
+ halCall<void>(mController, [](auto hal) { return hal->setExternalControl(true); });
+ state.PauseTiming();
+ if (checkHalResult(result, state)) {
+ auto ret = halCall<void>(mController,
+ [](auto hal) { return hal->setExternalControl(false); });
+ checkHalResult(ret, state);
+ }
+ }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, setExternalAmplitudeCached, {
+ if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL, state)) {
+ return;
+ }
+
+ auto amplitude = 1.0f;
+
+ auto onResult =
+ halCall<void>(mController, [](auto hal) { return hal->setExternalControl(true); });
+ checkHalResult(onResult, state);
+
+ for (auto _ : state) {
+ auto ret =
+ halCall<void>(mController, [&](auto hal) { return hal->setAmplitude(amplitude); });
+ checkHalResult(ret, state);
+ }
+
+ auto offResult =
+ halCall<void>(mController, [](auto hal) { return hal->setExternalControl(false); });
+ checkHalResult(offResult, state);
+});
+
+BENCHMARK_WRAPPER(VibratorBench, getInfo, {
+ for (auto _ : state) {
+ state.PauseTiming();
+ vibrator::HalController controller;
+ controller.init();
+ state.ResumeTiming();
+ auto result = controller.getInfo();
+ checkHalResult(result.capabilities, state);
+ checkHalResult(result.supportedEffects, state);
+ checkHalResult(result.supportedPrimitives, state);
+ checkHalResult(result.primitiveDurations, state);
+ checkHalResult(result.resonantFrequency, state);
+ checkHalResult(result.qFactor, state);
+ }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, getInfoCached, {
+ // First call to cache values.
+ mController.getInfo();
+
+ for (auto _ : state) {
+ auto result = mController.getInfo();
+ checkHalResult(result.capabilities, state);
+ checkHalResult(result.supportedEffects, state);
+ checkHalResult(result.supportedPrimitives, state);
+ checkHalResult(result.primitiveDurations, state);
+ checkHalResult(result.resonantFrequency, state);
+ checkHalResult(result.qFactor, state);
+ }
+});
+
+class VibratorEffectsBench : public VibratorBench {
+public:
+ static void DefaultArgs(Benchmark* b) {
+ vibrator::HalController controller;
+ auto effectsResult = controller.getInfo().supportedEffects;
+ if (!effectsResult.isOk()) {
+ return;
+ }
+
+ std::vector<Effect> supported = effectsResult.value();
+ b->ArgNames({"Effect", "Strength"});
+
+ if (supported.empty()) {
+ b->Args({static_cast<long>(-1), static_cast<long>(-1)});
+ return;
+ }
+
+ for (const auto& effect : enum_range<Effect>()) {
+ if (std::find(supported.begin(), supported.end(), effect) == supported.end()) {
+ continue;
+ }
+ for (const auto& strength : enum_range<EffectStrength>()) {
+ b->Args({static_cast<long>(effect), static_cast<long>(strength)});
+ }
+ }
+ }
+
+protected:
+ bool hasArgs(const State& state) const { return this->getOtherArg(state, 0) >= 0; }
+
+ auto getEffect(const State& state) const {
+ return static_cast<Effect>(this->getOtherArg(state, 0));
+ }
+
+ auto getStrength(const State& state) const {
+ return static_cast<EffectStrength>(this->getOtherArg(state, 1));
+ }
+};
+
+BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnEnable, {
+ if (!hasCapabilities(vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
+ return;
+ }
+ if (!hasArgs(state)) {
+ return;
+ }
+
+ int32_t id = 1;
+ auto effect = getEffect(state);
+ auto strength = getStrength(state);
+
+ for (auto _ : state) {
+ state.ResumeTiming();
+ auto ret = halCall<void>(mController, [&](auto hal) {
+ return hal->alwaysOnEnable(id, effect, strength);
+ });
+ state.PauseTiming();
+ if (checkHalResult(ret, state)) {
+ auto disableResult =
+ halCall<void>(mController, [&](auto hal) { return hal->alwaysOnDisable(id); });
+ checkHalResult(disableResult, state);
+ }
+ }
+});
+
+BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnDisable, {
+ if (!hasCapabilities(vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
+ return;
+ }
+ if (!hasArgs(state)) {
+ return;
+ }
+
+ int32_t id = 1;
+ auto effect = getEffect(state);
+ auto strength = getStrength(state);
+
+ for (auto _ : state) {
+ state.PauseTiming();
+ auto enableResult = halCall<void>(mController, [&](auto hal) {
+ return hal->alwaysOnEnable(id, effect, strength);
+ });
+ if (!checkHalResult(enableResult, state)) {
+ continue;
+ }
+ state.ResumeTiming();
+ auto disableResult =
+ halCall<void>(mController, [&](auto hal) { return hal->alwaysOnDisable(id); });
+ checkHalResult(disableResult, state);
+ }
+});
+
+BENCHMARK_WRAPPER(VibratorEffectsBench, performEffect, {
+ if (!hasArgs(state)) {
+ return;
+ }
+
+ auto effect = getEffect(state);
+ auto strength = getStrength(state);
+ auto callback = []() {};
+
+ for (auto _ : state) {
+ state.ResumeTiming();
+ auto ret = halCall<std::chrono::milliseconds>(mController, [&](auto hal) {
+ return hal->performEffect(effect, strength, callback);
+ });
+ state.PauseTiming();
+ if (checkHalResult(ret, state)) {
+ turnVibratorOff(state);
+ }
+ }
+});
+
+class VibratorPrimitivesBench : public VibratorBench {
+public:
+ static void DefaultArgs(Benchmark* b) {
+ vibrator::HalController controller;
+ auto primitivesResult = controller.getInfo().supportedPrimitives;
+ if (!primitivesResult.isOk()) {
+ return;
+ }
+
+ std::vector<CompositePrimitive> supported = primitivesResult.value();
+ b->ArgNames({"Primitive"});
+
+ if (supported.empty()) {
+ b->Args({static_cast<long>(-1)});
+ return;
+ }
+
+ for (const auto& primitive : enum_range<CompositePrimitive>()) {
+ if (std::find(supported.begin(), supported.end(), primitive) == supported.end()) {
+ continue;
+ }
+ if (primitive == CompositePrimitive::NOOP) {
+ continue;
+ }
+ b->Args({static_cast<long>(primitive)});
+ }
+ }
+
+protected:
+ bool hasArgs(const State& state) const { return this->getOtherArg(state, 0) >= 0; }
+
+ auto getPrimitive(const State& state) const {
+ return static_cast<CompositePrimitive>(this->getOtherArg(state, 0));
+ }
+};
+
+BENCHMARK_WRAPPER(VibratorPrimitivesBench, performComposedEffect, {
+ if (!hasCapabilities(vibrator::Capabilities::COMPOSE_EFFECTS, state)) {
+ return;
+ }
+ if (!hasArgs(state)) {
+ return;
+ }
+
+ CompositeEffect effect;
+ effect.primitive = getPrimitive(state);
+ effect.scale = 1.0f;
+ effect.delayMs = static_cast<int32_t>(0);
+
+ std::vector<CompositeEffect> effects;
+ effects.push_back(effect);
+ auto callback = []() {};
+
+ for (auto _ : state) {
+ state.ResumeTiming();
+ auto ret = halCall<std::chrono::milliseconds>(mController, [&](auto hal) {
+ return hal->performComposedEffect(effects, callback);
+ });
+ state.PauseTiming();
+ if (checkHalResult(ret, state)) {
+ turnVibratorOff(state);
+ }
+ }
+});
+
+BENCHMARK_MAIN();
\ No newline at end of file
diff --git a/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h b/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h
new file mode 100644
index 0000000..2c194b5
--- /dev/null
+++ b/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef ANDROID_VIBRATOR_CALLBACK_SCHEDULER_H
+#define ANDROID_VIBRATOR_CALLBACK_SCHEDULER_H
+
+#include <android-base/thread_annotations.h>
+#include <chrono>
+#include <condition_variable>
+#include <queue>
+#include <thread>
+
+namespace android {
+
+namespace vibrator {
+
+// Wrapper for a callback to be executed after a delay.
+class DelayedCallback {
+public:
+ using Timestamp = std::chrono::time_point<std::chrono::steady_clock>;
+
+ DelayedCallback(std::function<void()> callback, std::chrono::milliseconds delay)
+ : mCallback(callback), mExpiration(std::chrono::steady_clock::now() + delay) {}
+ ~DelayedCallback() = default;
+
+ void run() const;
+ bool isExpired() const;
+ Timestamp getExpiration() const;
+
+ // Compare by expiration time, where A < B when A expires first.
+ bool operator<(const DelayedCallback& other) const;
+ bool operator>(const DelayedCallback& other) const;
+
+private:
+ std::function<void()> mCallback;
+ Timestamp mExpiration;
+};
+
+// Schedules callbacks to be executed after a delay.
+class CallbackScheduler {
+public:
+ CallbackScheduler() : mCallbackThread(nullptr), mFinished(false) {}
+ virtual ~CallbackScheduler();
+
+ virtual void schedule(std::function<void()> callback, std::chrono::milliseconds delay);
+
+private:
+ std::condition_variable_any mCondition;
+ std::mutex mMutex;
+
+ // Lazily instantiated only at the first time this scheduler is used.
+ std::unique_ptr<std::thread> mCallbackThread;
+
+ // Used to quit the callback thread when this instance is being destroyed.
+ bool mFinished GUARDED_BY(mMutex);
+
+ // Priority queue with reverse comparator, so tasks that expire first will be on top.
+ std::priority_queue<DelayedCallback, std::vector<DelayedCallback>,
+ std::greater<DelayedCallback>>
+ mQueue GUARDED_BY(mMutex);
+
+ void loop();
+};
+
+}; // namespace vibrator
+
+}; // namespace android
+
+#endif // ANDROID_VIBRATOR_CALLBACK_SCHEDULER_H
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
new file mode 100644
index 0000000..6c31e2b
--- /dev/null
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef ANDROID_OS_VIBRATORHALCONTROLLER_H
+#define ANDROID_OS_VIBRATORHALCONTROLLER_H
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+namespace android {
+
+namespace vibrator {
+
+std::shared_ptr<HalWrapper> connectHal(std::shared_ptr<CallbackScheduler> scheduler);
+
+template <typename T>
+using HalFunction = std::function<T(HalWrapper*)>;
+
+// Controller for Vibrator HAL handle.
+// This relies on a given Connector to connect to the underlying Vibrator HAL service and reconnects
+// after each failed api call. This also ensures connecting to the service is thread-safe.
+class HalController {
+public:
+ using Connector =
+ std::function<std::shared_ptr<HalWrapper>(std::shared_ptr<CallbackScheduler>)>;
+
+ HalController() : HalController(std::make_shared<CallbackScheduler>(), &connectHal) {}
+ HalController(std::shared_ptr<CallbackScheduler> callbackScheduler, Connector connector)
+ : mConnector(connector),
+ mConnectedHal(nullptr),
+ mCallbackScheduler(std::move(callbackScheduler)) {}
+ virtual ~HalController() = default;
+
+ /* Connects to the newest HAL version available, possibly waiting for the registered service to
+ * become available. This will automatically be called at the first API usage if it was not
+ * manually called beforehand. Calling this manually during the setup phase can avoid slowing
+ * the first API call later on. Returns true if any HAL version is available, false otherwise.
+ */
+ virtual bool init();
+
+ /* Reloads HAL service instance without waiting. This relies on the HAL version found by init()
+ * to rapidly reconnect to the specific HAL service, or defers to init() if it was never called.
+ */
+ virtual void tryReconnect();
+
+ /* Returns info loaded from the connected HAL. This allows partial results to be returned if any
+ * of the Info fields has failed, but also retried on any failure.
+ */
+ Info getInfo() {
+ static Info sDefaultInfo = InfoCache().get();
+ return apply<Info>([](HalWrapper* hal) { return hal->getInfo(); }, sDefaultInfo, "getInfo");
+ }
+
+ /* Calls given HAL function, applying automatic retries to reconnect with the HAL when the
+ * result has failed. Parameter functionName is for logging purposes.
+ */
+ template <typename T>
+ HalResult<T> doWithRetry(const HalFunction<HalResult<T>>& halFn, const char* functionName) {
+ return apply(halFn, HalResult<T>::unsupported(), functionName);
+ }
+
+private:
+ static constexpr int MAX_RETRIES = 1;
+
+ Connector mConnector;
+ std::mutex mConnectedHalMutex;
+ // Shared pointer to allow local copies to be used by different threads.
+ std::shared_ptr<HalWrapper> mConnectedHal GUARDED_BY(mConnectedHalMutex);
+ // Shared pointer to allow copies to be passed to possible recreated mConnectedHal instances.
+ std::shared_ptr<CallbackScheduler> mCallbackScheduler;
+
+ /* Calls given HAL function, applying automatic retries to reconnect with the HAL when the
+ * result has failed. Given default value is returned when no HAL is available, and given
+ * function name is for logging purposes.
+ */
+ template <typename T>
+ T apply(const HalFunction<T>& halFn, T defaultValue, const char* functionName) {
+ if (!init()) {
+ ALOGV("Skipped %s because Vibrator HAL is not available", functionName);
+ return defaultValue;
+ }
+ std::shared_ptr<HalWrapper> hal;
+ {
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ hal = mConnectedHal;
+ }
+
+ for (int i = 0; i < MAX_RETRIES; i++) {
+ T result = halFn(hal.get());
+ if (result.checkAndLogFailure(functionName)) {
+ tryReconnect();
+ } else {
+ return result;
+ }
+ }
+
+ return halFn(hal.get());
+ }
+};
+
+}; // namespace vibrator
+
+}; // namespace android
+
+#endif // ANDROID_OS_VIBRATORHALCONTROLLER_H
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
new file mode 100644
index 0000000..8720d9d
--- /dev/null
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -0,0 +1,470 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef ANDROID_OS_VIBRATORHALWRAPPER_H
+#define ANDROID_OS_VIBRATORHALWRAPPER_H
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/vibrator/1.3/IVibrator.h>
+#include <android/hardware/vibrator/BnVibratorCallback.h>
+#include <android/hardware/vibrator/IVibrator.h>
+#include <binder/IServiceManager.h>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+
+namespace android {
+
+namespace vibrator {
+
+// -------------------------------------------------------------------------------------------------
+
+// Result of a call to the Vibrator HAL wrapper, holding data if successful.
+template <typename T>
+class HalResult {
+public:
+ static HalResult<T> ok(T value) { return HalResult(value); }
+ static HalResult<T> failed(std::string msg) {
+ return HalResult(std::move(msg), /* unsupported= */ false);
+ }
+ static HalResult<T> unsupported() { return HalResult("", /* unsupported= */ true); }
+
+ static HalResult<T> fromStatus(binder::Status status, T data) {
+ if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+ return HalResult<T>::unsupported();
+ }
+ if (status.isOk()) {
+ return HalResult<T>::ok(data);
+ }
+ return HalResult<T>::failed(status.toString8().c_str());
+ }
+ static HalResult<T> fromStatus(hardware::vibrator::V1_0::Status status, T data);
+
+ template <typename R>
+ static HalResult<T> fromReturn(hardware::Return<R>& ret, T data);
+
+ template <typename R>
+ static HalResult<T> fromReturn(hardware::Return<R>& ret,
+ hardware::vibrator::V1_0::Status status, T data);
+
+ // This will throw std::bad_optional_access if this result is not ok.
+ const T& value() const { return mValue.value(); }
+ const T valueOr(T&& defaultValue) const { return mValue.value_or(defaultValue); }
+ bool isOk() const { return !mUnsupported && mValue.has_value(); }
+ bool isFailed() const { return !mUnsupported && !mValue.has_value(); }
+ bool isUnsupported() const { return mUnsupported; }
+ const char* errorMessage() const { return mErrorMessage.c_str(); }
+ bool checkAndLogFailure(const char* functionName) const {
+ if (isFailed()) {
+ ALOGE("%s failed: %s", functionName, errorMessage());
+ return true;
+ }
+ return false;
+ }
+
+private:
+ std::optional<T> mValue;
+ std::string mErrorMessage;
+ bool mUnsupported;
+
+ explicit HalResult(T value)
+ : mValue(std::make_optional(value)), mErrorMessage(), mUnsupported(false) {}
+ explicit HalResult(std::string errorMessage, bool unsupported)
+ : mValue(), mErrorMessage(std::move(errorMessage)), mUnsupported(unsupported) {}
+};
+
+// Empty result of a call to the Vibrator HAL wrapper.
+template <>
+class HalResult<void> {
+public:
+ static HalResult<void> ok() { return HalResult(); }
+ static HalResult<void> failed(std::string msg) { return HalResult(std::move(msg)); }
+ static HalResult<void> unsupported() { return HalResult(/* unsupported= */ true); }
+
+ static HalResult<void> fromStatus(status_t status);
+ static HalResult<void> fromStatus(binder::Status status);
+ static HalResult<void> fromStatus(hardware::vibrator::V1_0::Status status);
+
+ template <typename R>
+ static HalResult<void> fromReturn(hardware::Return<R>& ret);
+
+ bool isOk() const { return !mUnsupported && !mFailed; }
+ bool isFailed() const { return !mUnsupported && mFailed; }
+ bool isUnsupported() const { return mUnsupported; }
+ const char* errorMessage() const { return mErrorMessage.c_str(); }
+ bool checkAndLogFailure(const char* functionName) const {
+ if (isFailed()) {
+ ALOGE("%s failed: %s", functionName, errorMessage());
+ return true;
+ }
+ return false;
+ }
+
+private:
+ std::string mErrorMessage;
+ bool mFailed;
+ bool mUnsupported;
+
+ explicit HalResult(bool unsupported = false)
+ : mErrorMessage(), mFailed(false), mUnsupported(unsupported) {}
+ explicit HalResult(std::string errorMessage)
+ : mErrorMessage(std::move(errorMessage)), mFailed(true), mUnsupported(false) {}
+};
+
+class HalCallbackWrapper : public hardware::vibrator::BnVibratorCallback {
+public:
+ HalCallbackWrapper(std::function<void()> completionCallback)
+ : mCompletionCallback(completionCallback) {}
+
+ binder::Status onComplete() override {
+ mCompletionCallback();
+ return binder::Status::ok();
+ }
+
+private:
+ const std::function<void()> mCompletionCallback;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+// Vibrator HAL capabilities.
+enum class Capabilities : int32_t {
+ NONE = 0,
+ ON_CALLBACK = hardware::vibrator::IVibrator::CAP_ON_CALLBACK,
+ PERFORM_CALLBACK = hardware::vibrator::IVibrator::CAP_PERFORM_CALLBACK,
+ AMPLITUDE_CONTROL = hardware::vibrator::IVibrator::CAP_AMPLITUDE_CONTROL,
+ EXTERNAL_CONTROL = hardware::vibrator::IVibrator::CAP_EXTERNAL_CONTROL,
+ EXTERNAL_AMPLITUDE_CONTROL = hardware::vibrator::IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL,
+ COMPOSE_EFFECTS = hardware::vibrator::IVibrator::CAP_COMPOSE_EFFECTS,
+ COMPOSE_PWLE_EFFECTS = hardware::vibrator::IVibrator::CAP_COMPOSE_PWLE_EFFECTS,
+ ALWAYS_ON_CONTROL = hardware::vibrator::IVibrator::CAP_ALWAYS_ON_CONTROL,
+};
+
+inline Capabilities operator|(Capabilities lhs, Capabilities rhs) {
+ using underlying = typename std::underlying_type<Capabilities>::type;
+ return static_cast<Capabilities>(static_cast<underlying>(lhs) | static_cast<underlying>(rhs));
+}
+
+inline Capabilities& operator|=(Capabilities& lhs, Capabilities rhs) {
+ return lhs = lhs | rhs;
+}
+
+inline Capabilities operator&(Capabilities lhs, Capabilities rhs) {
+ using underlying = typename std::underlying_type<Capabilities>::type;
+ return static_cast<Capabilities>(static_cast<underlying>(lhs) & static_cast<underlying>(rhs));
+}
+
+inline Capabilities& operator&=(Capabilities& lhs, Capabilities rhs) {
+ return lhs = lhs & rhs;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+class Info {
+public:
+ const HalResult<Capabilities> capabilities;
+ const HalResult<std::vector<hardware::vibrator::Effect>> supportedEffects;
+ const HalResult<std::vector<hardware::vibrator::Braking>> supportedBraking;
+ const HalResult<std::vector<hardware::vibrator::CompositePrimitive>> supportedPrimitives;
+ const HalResult<std::vector<std::chrono::milliseconds>> primitiveDurations;
+ const HalResult<float> minFrequency;
+ const HalResult<float> resonantFrequency;
+ const HalResult<float> frequencyResolution;
+ const HalResult<float> qFactor;
+ const HalResult<std::vector<float>> maxAmplitudes;
+
+ bool checkAndLogFailure(const char*) const {
+ return capabilities.checkAndLogFailure("getCapabilities") ||
+ supportedEffects.checkAndLogFailure("getSupportedEffects") ||
+ supportedBraking.checkAndLogFailure("getSupportedBraking") ||
+ supportedPrimitives.checkAndLogFailure("getSupportedPrimitives") ||
+ primitiveDurations.checkAndLogFailure("getPrimitiveDuration") ||
+ minFrequency.checkAndLogFailure("getMinFrequency") ||
+ resonantFrequency.checkAndLogFailure("getResonantFrequency") ||
+ frequencyResolution.checkAndLogFailure("getFrequencyResolution") ||
+ qFactor.checkAndLogFailure("getQFactor") ||
+ maxAmplitudes.checkAndLogFailure("getMaxAmplitudes");
+ }
+};
+
+class InfoCache {
+public:
+ Info get() {
+ return {mCapabilities, mSupportedEffects, mSupportedBraking,
+ mSupportedPrimitives, mPrimitiveDurations, mMinFrequency,
+ mResonantFrequency, mFrequencyResolution, mQFactor,
+ mMaxAmplitudes};
+ }
+
+private:
+ static const constexpr char* MSG = "never loaded";
+ HalResult<Capabilities> mCapabilities = HalResult<Capabilities>::failed(MSG);
+ HalResult<std::vector<hardware::vibrator::Effect>> mSupportedEffects =
+ HalResult<std::vector<hardware::vibrator::Effect>>::failed(MSG);
+ HalResult<std::vector<hardware::vibrator::Braking>> mSupportedBraking =
+ HalResult<std::vector<hardware::vibrator::Braking>>::failed(MSG);
+ HalResult<std::vector<hardware::vibrator::CompositePrimitive>> mSupportedPrimitives =
+ HalResult<std::vector<hardware::vibrator::CompositePrimitive>>::failed(MSG);
+ HalResult<std::vector<std::chrono::milliseconds>> mPrimitiveDurations =
+ HalResult<std::vector<std::chrono::milliseconds>>::failed(MSG);
+ HalResult<float> mMinFrequency = HalResult<float>::failed(MSG);
+ HalResult<float> mResonantFrequency = HalResult<float>::failed(MSG);
+ HalResult<float> mFrequencyResolution = HalResult<float>::failed(MSG);
+ HalResult<float> mQFactor = HalResult<float>::failed(MSG);
+ HalResult<std::vector<float>> mMaxAmplitudes = HalResult<std::vector<float>>::failed(MSG);
+
+ friend class HalWrapper;
+};
+
+// Wrapper for Vibrator HAL handlers.
+class HalWrapper {
+public:
+ explicit HalWrapper(std::shared_ptr<CallbackScheduler> scheduler)
+ : mCallbackScheduler(std::move(scheduler)) {}
+ virtual ~HalWrapper() = default;
+
+ /* reloads wrapped HAL service instance without waiting. This can be used to reconnect when the
+ * service restarts, to rapidly retry after a failure.
+ */
+ virtual void tryReconnect() = 0;
+
+ Info getInfo();
+
+ virtual HalResult<void> ping() = 0;
+ virtual HalResult<void> on(std::chrono::milliseconds timeout,
+ const std::function<void()>& completionCallback) = 0;
+ virtual HalResult<void> off() = 0;
+
+ virtual HalResult<void> setAmplitude(float amplitude) = 0;
+ virtual HalResult<void> setExternalControl(bool enabled) = 0;
+
+ virtual HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
+ hardware::vibrator::EffectStrength strength) = 0;
+ virtual HalResult<void> alwaysOnDisable(int32_t id) = 0;
+
+ virtual HalResult<std::chrono::milliseconds> performEffect(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback) = 0;
+
+ virtual HalResult<std::chrono::milliseconds> performComposedEffect(
+ const std::vector<hardware::vibrator::CompositeEffect>& primitives,
+ const std::function<void()>& completionCallback);
+
+ virtual HalResult<void> performPwleEffect(
+ const std::vector<hardware::vibrator::PrimitivePwle>& primitives,
+ const std::function<void()>& completionCallback);
+
+protected:
+ // Shared pointer to allow CallbackScheduler to outlive this wrapper.
+ const std::shared_ptr<CallbackScheduler> mCallbackScheduler;
+
+ // Load and cache vibrator info, returning cached result is present.
+ HalResult<Capabilities> getCapabilities();
+ HalResult<std::vector<std::chrono::milliseconds>> getPrimitiveDurations();
+
+ // Request vibrator info to HAL skipping cache.
+ virtual HalResult<Capabilities> getCapabilitiesInternal() = 0;
+ virtual HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffectsInternal();
+ virtual HalResult<std::vector<hardware::vibrator::Braking>> getSupportedBrakingInternal();
+ virtual HalResult<std::vector<hardware::vibrator::CompositePrimitive>>
+ getSupportedPrimitivesInternal();
+ virtual HalResult<std::vector<std::chrono::milliseconds>> getPrimitiveDurationsInternal(
+ const std::vector<hardware::vibrator::CompositePrimitive>& supportedPrimitives);
+ virtual HalResult<float> getMinFrequencyInternal();
+ virtual HalResult<float> getResonantFrequencyInternal();
+ virtual HalResult<float> getFrequencyResolutionInternal();
+ virtual HalResult<float> getQFactorInternal();
+ virtual HalResult<std::vector<float>> getMaxAmplitudesInternal();
+
+private:
+ std::mutex mInfoMutex;
+ InfoCache mInfoCache GUARDED_BY(mInfoMutex);
+};
+
+// Wrapper for the AIDL Vibrator HAL.
+class AidlHalWrapper : public HalWrapper {
+public:
+ AidlHalWrapper(
+ std::shared_ptr<CallbackScheduler> scheduler, sp<hardware::vibrator::IVibrator> handle,
+ std::function<HalResult<sp<hardware::vibrator::IVibrator>>()> reconnectFn =
+ []() {
+ return HalResult<sp<hardware::vibrator::IVibrator>>::ok(
+ checkVintfService<hardware::vibrator::IVibrator>());
+ })
+ : HalWrapper(std::move(scheduler)),
+ mReconnectFn(reconnectFn),
+ mHandle(std::move(handle)) {}
+ virtual ~AidlHalWrapper() = default;
+
+ HalResult<void> ping() override final;
+ void tryReconnect() override final;
+
+ HalResult<void> on(std::chrono::milliseconds timeout,
+ const std::function<void()>& completionCallback) override final;
+ HalResult<void> off() override final;
+
+ HalResult<void> setAmplitude(float amplitude) override final;
+ HalResult<void> setExternalControl(bool enabled) override final;
+
+ HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
+ hardware::vibrator::EffectStrength strength) override final;
+ HalResult<void> alwaysOnDisable(int32_t id) override final;
+
+ HalResult<std::chrono::milliseconds> performEffect(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback) override final;
+
+ HalResult<std::chrono::milliseconds> performComposedEffect(
+ const std::vector<hardware::vibrator::CompositeEffect>& primitives,
+ const std::function<void()>& completionCallback) override final;
+
+ HalResult<void> performPwleEffect(
+ const std::vector<hardware::vibrator::PrimitivePwle>& primitives,
+ const std::function<void()>& completionCallback) override final;
+
+protected:
+ HalResult<Capabilities> getCapabilitiesInternal() override final;
+ HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffectsInternal() override final;
+ HalResult<std::vector<hardware::vibrator::Braking>> getSupportedBrakingInternal()
+ override final;
+ HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitivesInternal()
+ override final;
+ HalResult<std::vector<std::chrono::milliseconds>> getPrimitiveDurationsInternal(
+ const std::vector<hardware::vibrator::CompositePrimitive>& supportedPrimitives)
+ override final;
+ HalResult<float> getMinFrequencyInternal() override final;
+ HalResult<float> getResonantFrequencyInternal() override final;
+ HalResult<float> getFrequencyResolutionInternal() override final;
+ HalResult<float> getQFactorInternal() override final;
+ HalResult<std::vector<float>> getMaxAmplitudesInternal() override final;
+
+private:
+ const std::function<HalResult<sp<hardware::vibrator::IVibrator>>()> mReconnectFn;
+ std::mutex mHandleMutex;
+ sp<hardware::vibrator::IVibrator> mHandle GUARDED_BY(mHandleMutex);
+
+ sp<hardware::vibrator::IVibrator> getHal();
+};
+
+// Wrapper for the HDIL Vibrator HALs.
+template <typename I>
+class HidlHalWrapper : public HalWrapper {
+public:
+ HidlHalWrapper(std::shared_ptr<CallbackScheduler> scheduler, sp<I> handle)
+ : HalWrapper(std::move(scheduler)), mHandle(std::move(handle)) {}
+ virtual ~HidlHalWrapper() = default;
+
+ HalResult<void> ping() override final;
+ void tryReconnect() override final;
+
+ HalResult<void> on(std::chrono::milliseconds timeout,
+ const std::function<void()>& completionCallback) override final;
+ HalResult<void> off() override final;
+
+ HalResult<void> setAmplitude(float amplitude) override final;
+ virtual HalResult<void> setExternalControl(bool enabled) override;
+
+ HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
+ hardware::vibrator::EffectStrength strength) override final;
+ HalResult<void> alwaysOnDisable(int32_t id) override final;
+
+protected:
+ std::mutex mHandleMutex;
+ sp<I> mHandle GUARDED_BY(mHandleMutex);
+
+ virtual HalResult<Capabilities> getCapabilitiesInternal() override;
+
+ template <class T>
+ using perform_fn =
+ hardware::Return<void> (I::*)(T, hardware::vibrator::V1_0::EffectStrength,
+ hardware::vibrator::V1_0::IVibrator::perform_cb);
+
+ template <class T>
+ HalResult<std::chrono::milliseconds> performInternal(
+ perform_fn<T> performFn, sp<I> handle, T effect,
+ hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback);
+
+ sp<I> getHal();
+};
+
+// Wrapper for the HDIL Vibrator HAL v1.0.
+class HidlHalWrapperV1_0 : public HidlHalWrapper<hardware::vibrator::V1_0::IVibrator> {
+public:
+ HidlHalWrapperV1_0(std::shared_ptr<CallbackScheduler> scheduler,
+ sp<hardware::vibrator::V1_0::IVibrator> handle)
+ : HidlHalWrapper<hardware::vibrator::V1_0::IVibrator>(std::move(scheduler),
+ std::move(handle)) {}
+ virtual ~HidlHalWrapperV1_0() = default;
+
+ HalResult<std::chrono::milliseconds> performEffect(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback) override final;
+};
+
+// Wrapper for the HDIL Vibrator HAL v1.1.
+class HidlHalWrapperV1_1 : public HidlHalWrapper<hardware::vibrator::V1_1::IVibrator> {
+public:
+ HidlHalWrapperV1_1(std::shared_ptr<CallbackScheduler> scheduler,
+ sp<hardware::vibrator::V1_1::IVibrator> handle)
+ : HidlHalWrapper<hardware::vibrator::V1_1::IVibrator>(std::move(scheduler),
+ std::move(handle)) {}
+ virtual ~HidlHalWrapperV1_1() = default;
+
+ HalResult<std::chrono::milliseconds> performEffect(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback) override final;
+};
+
+// Wrapper for the HDIL Vibrator HAL v1.2.
+class HidlHalWrapperV1_2 : public HidlHalWrapper<hardware::vibrator::V1_2::IVibrator> {
+public:
+ HidlHalWrapperV1_2(std::shared_ptr<CallbackScheduler> scheduler,
+ sp<hardware::vibrator::V1_2::IVibrator> handle)
+ : HidlHalWrapper<hardware::vibrator::V1_2::IVibrator>(std::move(scheduler),
+ std::move(handle)) {}
+ virtual ~HidlHalWrapperV1_2() = default;
+
+ HalResult<std::chrono::milliseconds> performEffect(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback) override final;
+};
+
+// Wrapper for the HDIL Vibrator HAL v1.3.
+class HidlHalWrapperV1_3 : public HidlHalWrapper<hardware::vibrator::V1_3::IVibrator> {
+public:
+ HidlHalWrapperV1_3(std::shared_ptr<CallbackScheduler> scheduler,
+ sp<hardware::vibrator::V1_3::IVibrator> handle)
+ : HidlHalWrapper<hardware::vibrator::V1_3::IVibrator>(std::move(scheduler),
+ std::move(handle)) {}
+ virtual ~HidlHalWrapperV1_3() = default;
+
+ HalResult<void> setExternalControl(bool enabled) override final;
+
+ HalResult<std::chrono::milliseconds> performEffect(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback) override final;
+
+protected:
+ HalResult<Capabilities> getCapabilitiesInternal() override final;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+}; // namespace vibrator
+
+}; // namespace android
+
+#endif // ANDROID_OS_VIBRATORHALWRAPPER_H
diff --git a/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h b/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h
new file mode 100644
index 0000000..9168565
--- /dev/null
+++ b/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef ANDROID_OS_VIBRATOR_MANAGER_HAL_CONTROLLER_H
+#define ANDROID_OS_VIBRATOR_MANAGER_HAL_CONTROLLER_H
+
+#include <android/hardware/vibrator/IVibratorManager.h>
+#include <vibratorservice/VibratorHalController.h>
+#include <vibratorservice/VibratorManagerHalWrapper.h>
+#include <unordered_map>
+
+namespace android {
+
+namespace vibrator {
+
+std::shared_ptr<ManagerHalWrapper> connectManagerHal(std::shared_ptr<CallbackScheduler> scheduler);
+
+// Controller for VibratorManager HAL handle.
+class ManagerHalController : public ManagerHalWrapper {
+public:
+ using Connector =
+ std::function<std::shared_ptr<ManagerHalWrapper>(std::shared_ptr<CallbackScheduler>)>;
+
+ ManagerHalController()
+ : ManagerHalController(std::make_shared<CallbackScheduler>(), &connectManagerHal) {}
+ ManagerHalController(std::shared_ptr<CallbackScheduler> callbackScheduler, Connector connector)
+ : mConnector(connector), mCallbackScheduler(callbackScheduler), mConnectedHal(nullptr) {}
+ virtual ~ManagerHalController() = default;
+
+ /* Connects to the HAL service, possibly waiting for the registered service to
+ * become available. This will automatically be called at the first API usage if it was not
+ * manually called beforehand. Calling this manually during the setup phase can avoid slowing
+ * the first API call later on. This will fallback to a legacy manager implementation if the
+ * service is not available.
+ */
+ virtual void init();
+
+ /* reloads HAL service instance without waiting. This relies on the HAL found by init()
+ * to rapidly reconnect to the specific HAL service, or defers to init() if it was never called.
+ */
+ void tryReconnect() override final;
+
+ HalResult<void> ping() override final;
+
+ HalResult<ManagerCapabilities> getCapabilities() override final;
+ HalResult<std::vector<int32_t>> getVibratorIds() override final;
+ HalResult<std::shared_ptr<HalController>> getVibrator(int32_t id) override final;
+
+ HalResult<void> prepareSynced(const std::vector<int32_t>& ids) override final;
+ HalResult<void> triggerSynced(const std::function<void()>& completionCallback) override final;
+ HalResult<void> cancelSynced() override final;
+
+private:
+ Connector mConnector;
+ std::shared_ptr<CallbackScheduler> mCallbackScheduler;
+ std::mutex mConnectedHalMutex;
+ // Shared pointer to allow local copies to be used by different threads.
+ std::shared_ptr<ManagerHalWrapper> mConnectedHal GUARDED_BY(mConnectedHalMutex);
+
+ template <typename T>
+ HalResult<T> processHalResult(HalResult<T> result, const char* functionName);
+
+ template <typename T>
+ using hal_fn = std::function<HalResult<T>(std::shared_ptr<ManagerHalWrapper>)>;
+
+ template <typename T>
+ HalResult<T> apply(hal_fn<T>& halFn, const char* functionName);
+};
+
+}; // namespace vibrator
+
+}; // namespace android
+
+#endif // ANDROID_OS_VIBRATOR_MANAGER_HAL_CONTROLLER_H
diff --git a/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h
new file mode 100644
index 0000000..563f55e
--- /dev/null
+++ b/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef ANDROID_OS_VIBRATOR_MANAGER_HAL_WRAPPER_H
+#define ANDROID_OS_VIBRATOR_MANAGER_HAL_WRAPPER_H
+
+#include <android/hardware/vibrator/IVibratorManager.h>
+#include <vibratorservice/VibratorHalController.h>
+#include <unordered_map>
+
+namespace android {
+
+namespace vibrator {
+
+// VibratorManager HAL capabilities.
+enum class ManagerCapabilities : int32_t {
+ NONE = 0,
+ SYNC = hardware::vibrator::IVibratorManager::CAP_SYNC,
+ PREPARE_ON = hardware::vibrator::IVibratorManager::CAP_PREPARE_ON,
+ PREPARE_PERFORM = hardware::vibrator::IVibratorManager::CAP_PREPARE_PERFORM,
+ PREPARE_COMPOSE = hardware::vibrator::IVibratorManager::CAP_PREPARE_COMPOSE,
+ MIXED_TRIGGER_ON = hardware::vibrator::IVibratorManager::IVibratorManager::CAP_MIXED_TRIGGER_ON,
+ MIXED_TRIGGER_PERFORM = hardware::vibrator::IVibratorManager::CAP_MIXED_TRIGGER_PERFORM,
+ MIXED_TRIGGER_COMPOSE = hardware::vibrator::IVibratorManager::CAP_MIXED_TRIGGER_COMPOSE,
+ TRIGGER_CALLBACK = hardware::vibrator::IVibratorManager::CAP_TRIGGER_CALLBACK
+};
+
+inline ManagerCapabilities operator|(ManagerCapabilities lhs, ManagerCapabilities rhs) {
+ using underlying = typename std::underlying_type<ManagerCapabilities>::type;
+ return static_cast<ManagerCapabilities>(static_cast<underlying>(lhs) |
+ static_cast<underlying>(rhs));
+}
+
+inline ManagerCapabilities& operator|=(ManagerCapabilities& lhs, ManagerCapabilities rhs) {
+ return lhs = lhs | rhs;
+}
+
+inline ManagerCapabilities operator&(ManagerCapabilities lhs, ManagerCapabilities rhs) {
+ using underlying = typename std::underlying_type<ManagerCapabilities>::type;
+ return static_cast<ManagerCapabilities>(static_cast<underlying>(lhs) &
+ static_cast<underlying>(rhs));
+}
+
+inline ManagerCapabilities& operator&=(ManagerCapabilities& lhs, ManagerCapabilities rhs) {
+ return lhs = lhs & rhs;
+}
+
+// Wrapper for VibratorManager HAL handlers.
+class ManagerHalWrapper {
+public:
+ ManagerHalWrapper() = default;
+ virtual ~ManagerHalWrapper() = default;
+
+ virtual HalResult<void> ping() = 0;
+
+ /* reloads wrapped HAL service instance without waiting. This can be used to reconnect when the
+ * service restarts, to rapidly retry after a failure.
+ */
+ virtual void tryReconnect() = 0;
+
+ virtual HalResult<ManagerCapabilities> getCapabilities() = 0;
+ virtual HalResult<std::vector<int32_t>> getVibratorIds() = 0;
+ virtual HalResult<std::shared_ptr<HalController>> getVibrator(int32_t id) = 0;
+
+ virtual HalResult<void> prepareSynced(const std::vector<int32_t>& ids) = 0;
+ virtual HalResult<void> triggerSynced(const std::function<void()>& completionCallback) = 0;
+ virtual HalResult<void> cancelSynced() = 0;
+};
+
+// Wrapper for the VibratorManager over single Vibrator HAL.
+class LegacyManagerHalWrapper : public ManagerHalWrapper {
+public:
+ LegacyManagerHalWrapper() : LegacyManagerHalWrapper(std::make_shared<HalController>()) {}
+ explicit LegacyManagerHalWrapper(std::shared_ptr<HalController> controller)
+ : mController(std::move(controller)) {}
+ virtual ~LegacyManagerHalWrapper() = default;
+
+ HalResult<void> ping() override final;
+ void tryReconnect() override final;
+
+ HalResult<ManagerCapabilities> getCapabilities() override final;
+ HalResult<std::vector<int32_t>> getVibratorIds() override final;
+ HalResult<std::shared_ptr<HalController>> getVibrator(int32_t id) override final;
+
+ HalResult<void> prepareSynced(const std::vector<int32_t>& ids) override final;
+ HalResult<void> triggerSynced(const std::function<void()>& completionCallback) override final;
+ HalResult<void> cancelSynced() override final;
+
+private:
+ const std::shared_ptr<HalController> mController;
+};
+
+// Wrapper for the AIDL VibratorManager HAL.
+class AidlManagerHalWrapper : public ManagerHalWrapper {
+public:
+ explicit AidlManagerHalWrapper(std::shared_ptr<CallbackScheduler> callbackScheduler,
+ sp<hardware::vibrator::IVibratorManager> handle)
+ : mHandle(std::move(handle)), mCallbackScheduler(callbackScheduler) {}
+ virtual ~AidlManagerHalWrapper() = default;
+
+ HalResult<void> ping() override final;
+ void tryReconnect() override final;
+
+ HalResult<ManagerCapabilities> getCapabilities() override final;
+ HalResult<std::vector<int32_t>> getVibratorIds() override final;
+ HalResult<std::shared_ptr<HalController>> getVibrator(int32_t id) override final;
+
+ HalResult<void> prepareSynced(const std::vector<int32_t>& ids) override final;
+ HalResult<void> triggerSynced(const std::function<void()>& completionCallback) override final;
+ HalResult<void> cancelSynced() override final;
+
+private:
+ std::mutex mHandleMutex;
+ std::mutex mCapabilitiesMutex;
+ std::mutex mVibratorsMutex;
+ sp<hardware::vibrator::IVibratorManager> mHandle GUARDED_BY(mHandleMutex);
+ std::optional<ManagerCapabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex);
+ std::optional<std::vector<int32_t>> mVibratorIds GUARDED_BY(mVibratorsMutex);
+ std::unordered_map<int32_t, std::shared_ptr<HalController>> mVibrators
+ GUARDED_BY(mVibratorsMutex);
+ std::shared_ptr<CallbackScheduler> mCallbackScheduler;
+
+ sp<hardware::vibrator::IVibratorManager> getHal();
+ std::shared_ptr<HalWrapper> connectToVibrator(int32_t vibratorId,
+ std::shared_ptr<CallbackScheduler> scheduler);
+};
+
+}; // namespace vibrator
+
+}; // namespace android
+
+#endif // ANDROID_OS_VIBRATOR_MANAGER_HAL_WRAPPER_H
diff --git a/services/vibratorservice/test/Android.bp b/services/vibratorservice/test/Android.bp
new file mode 100644
index 0000000..3294724
--- /dev/null
+++ b/services/vibratorservice/test/Android.bp
@@ -0,0 +1,60 @@
+// Copyright (C) 2020 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"],
+}
+
+cc_test {
+ name: "libvibratorservice_test",
+ test_suites: ["device-tests"],
+ srcs: [
+ "VibratorCallbackSchedulerTest.cpp",
+ "VibratorHalControllerTest.cpp",
+ "VibratorHalWrapperAidlTest.cpp",
+ "VibratorHalWrapperHidlV1_0Test.cpp",
+ "VibratorHalWrapperHidlV1_1Test.cpp",
+ "VibratorHalWrapperHidlV1_2Test.cpp",
+ "VibratorHalWrapperHidlV1_3Test.cpp",
+ "VibratorManagerHalControllerTest.cpp",
+ "VibratorManagerHalWrapperAidlTest.cpp",
+ "VibratorManagerHalWrapperLegacyTest.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libhidlbase",
+ "liblog",
+ "libvibratorservice",
+ "libutils",
+ "android.hardware.vibrator-V2-cpp",
+ "android.hardware.vibrator@1.0",
+ "android.hardware.vibrator@1.1",
+ "android.hardware.vibrator@1.2",
+ "android.hardware.vibrator@1.3",
+ ],
+ static_libs: [
+ "libgmock",
+ ],
+}
diff --git a/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
new file mode 100644
index 0000000..aaeb8f9
--- /dev/null
+++ b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "VibratorHalWrapperAidlTest"
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/vibrator/IVibrator.h>
+#include <condition_variable>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+#include <thread>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+
+using std::chrono::milliseconds;
+using std::chrono::steady_clock;
+using std::chrono::time_point;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorCallbackSchedulerTest : public Test {
+public:
+ void SetUp() override {
+ mScheduler = std::make_unique<vibrator::CallbackScheduler>();
+ std::lock_guard<std::mutex> lock(mMutex);
+ mExpiredCallbacks.clear();
+ }
+
+protected:
+ std::mutex mMutex;
+ std::condition_variable_any mCondition;
+ std::unique_ptr<vibrator::CallbackScheduler> mScheduler = nullptr;
+ std::vector<int32_t> mExpiredCallbacks GUARDED_BY(mMutex);
+
+ std::function<void()> createCallback(int32_t id) {
+ return [=]() {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mExpiredCallbacks.push_back(id);
+ }
+ mCondition.notify_all();
+ };
+ }
+
+ std::vector<int32_t> getExpiredCallbacks() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return std::vector<int32_t>(mExpiredCallbacks);
+ }
+
+ bool waitForCallbacks(uint32_t callbackCount, milliseconds timeout) {
+ time_point<steady_clock> expiration = steady_clock::now() + timeout;
+ while (steady_clock::now() < expiration) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (callbackCount <= mExpiredCallbacks.size()) {
+ return true;
+ }
+ mCondition.wait_until(mMutex, expiration);
+ }
+ return false;
+ }
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorCallbackSchedulerTest, TestScheduleRunsOnlyAfterDelay) {
+ mScheduler->schedule(createCallback(1), 15ms);
+
+ // Not triggered before delay.
+ ASSERT_FALSE(waitForCallbacks(1, 10ms));
+ ASSERT_TRUE(getExpiredCallbacks().empty());
+
+ ASSERT_TRUE(waitForCallbacks(1, 10ms));
+ ASSERT_THAT(getExpiredCallbacks(), ElementsAre(1));
+}
+
+TEST_F(VibratorCallbackSchedulerTest, TestScheduleMultipleCallbacksRunsInDelayOrder) {
+ mScheduler->schedule(createCallback(1), 10ms);
+ mScheduler->schedule(createCallback(2), 5ms);
+ mScheduler->schedule(createCallback(3), 1ms);
+
+ ASSERT_TRUE(waitForCallbacks(3, 15ms));
+ ASSERT_THAT(getExpiredCallbacks(), ElementsAre(3, 2, 1));
+}
+
+TEST_F(VibratorCallbackSchedulerTest, TestScheduleInParallelRunsInDelayOrder) {
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 5; i++) {
+ threads.push_back(std::thread(
+ [=]() { mScheduler->schedule(createCallback(i), milliseconds(10 + 2 * i)); }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ ASSERT_TRUE(waitForCallbacks(5, 25ms));
+ ASSERT_THAT(getExpiredCallbacks(), ElementsAre(0, 1, 2, 3, 4));
+}
+
+TEST_F(VibratorCallbackSchedulerTest, TestDestructorDropsPendingCallbacksAndKillsThread) {
+ mScheduler->schedule(createCallback(1), 5ms);
+ mScheduler.reset(nullptr);
+
+ // Should time out waiting for callback to run.
+ ASSERT_FALSE(waitForCallbacks(1, 10ms));
+ ASSERT_TRUE(getExpiredCallbacks().empty());
+}
diff --git a/services/vibratorservice/test/VibratorHalControllerTest.cpp b/services/vibratorservice/test/VibratorHalControllerTest.cpp
new file mode 100644
index 0000000..8e77bc5
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalControllerTest.cpp
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "VibratorHalControllerTest"
+
+#include <android/hardware/vibrator/IVibrator.h>
+#include <cutils/atomic.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+#include <thread>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalController.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+#include "test_utils.h"
+
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+
+using std::chrono::milliseconds;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+static const auto ON_FN = [](vibrator::HalWrapper* hal) { return hal->on(10ms, []() {}); };
+static const auto OFF_FN = [](vibrator::HalWrapper* hal) { return hal->off(); };
+static const auto PING_FN = [](vibrator::HalWrapper* hal) { return hal->ping(); };
+
+// -------------------------------------------------------------------------------------------------
+
+class MockHalWrapper : public vibrator::HalWrapper {
+public:
+ MockHalWrapper(std::shared_ptr<vibrator::CallbackScheduler> scheduler)
+ : HalWrapper(scheduler) {}
+ virtual ~MockHalWrapper() = default;
+
+ MOCK_METHOD(vibrator::HalResult<void>, ping, (), (override));
+ MOCK_METHOD(void, tryReconnect, (), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, on,
+ (milliseconds timeout, const std::function<void()>& completionCallback),
+ (override));
+ MOCK_METHOD(vibrator::HalResult<void>, off, (), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, setAmplitude, (float amplitude), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, setExternalControl, (bool enabled), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, alwaysOnEnable,
+ (int32_t id, Effect effect, EffectStrength strength), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, alwaysOnDisable, (int32_t id), (override));
+ MOCK_METHOD(vibrator::HalResult<milliseconds>, performEffect,
+ (Effect effect, EffectStrength strength,
+ const std::function<void()>& completionCallback),
+ (override));
+ MOCK_METHOD(vibrator::HalResult<vibrator::Capabilities>, getCapabilitiesInternal, (),
+ (override));
+
+ vibrator::CallbackScheduler* getCallbackScheduler() { return mCallbackScheduler.get(); }
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorHalControllerTest : public Test {
+public:
+ void SetUp() override {
+ mConnectCounter = 0;
+ auto callbackScheduler = std::make_shared<vibrator::CallbackScheduler>();
+ mMockHal = std::make_shared<StrictMock<MockHalWrapper>>(callbackScheduler);
+ mController = std::make_unique<
+ vibrator::HalController>(std::move(callbackScheduler),
+ [&](std::shared_ptr<vibrator::CallbackScheduler>) {
+ android_atomic_inc(&(this->mConnectCounter));
+ return this->mMockHal;
+ });
+ ASSERT_NE(mController, nullptr);
+ }
+
+protected:
+ int32_t mConnectCounter;
+ std::shared_ptr<MockHalWrapper> mMockHal;
+ std::unique_ptr<vibrator::HalController> mController;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorHalControllerTest, TestInit) {
+ ASSERT_TRUE(mController->init());
+ ASSERT_EQ(1, mConnectCounter);
+
+ // Noop when wrapper was already initialized.
+ ASSERT_TRUE(mController->init());
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestGetInfoRetriesOnAnyFailure) {
+ EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), getCapabilitiesInternal())
+ .Times(Exactly(2))
+ .WillOnce(Return(vibrator::HalResult<vibrator::Capabilities>::failed("message")))
+ .WillRepeatedly(Return(vibrator::HalResult<vibrator::Capabilities>::ok(
+ vibrator::Capabilities::ON_CALLBACK)));
+
+ auto result = mController->getInfo();
+ ASSERT_FALSE(result.capabilities.isFailed());
+
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestApiCallsAreForwardedToHal) {
+ EXPECT_CALL(*mMockHal.get(), on(_, _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::ok()));
+
+ auto result = mController->doWithRetry<void>(ON_FN, "on");
+ ASSERT_TRUE(result.isOk());
+
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestUnsupportedApiResultDoNotResetHalConnection) {
+ EXPECT_CALL(*mMockHal.get(), off())
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::unsupported()));
+
+ ASSERT_EQ(0, mConnectCounter);
+ auto result = mController->doWithRetry<void>(OFF_FN, "off");
+ ASSERT_TRUE(result.isUnsupported());
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestFailedApiResultResetsHalConnection) {
+ EXPECT_CALL(*mMockHal.get(), on(_, _))
+ .Times(Exactly(2))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message")));
+ EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
+
+ ASSERT_EQ(0, mConnectCounter);
+
+ auto result = mController->doWithRetry<void>(ON_FN, "on");
+ ASSERT_TRUE(result.isFailed());
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestFailedApiResultReturnsSuccessAfterRetries) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message")));
+ EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::ok()));
+ }
+
+ ASSERT_EQ(0, mConnectCounter);
+
+ auto result = mController->doWithRetry<void>(PING_FN, "ping");
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestMultiThreadConnectsOnlyOnce) {
+ ASSERT_EQ(0, mConnectCounter);
+
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(10))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::ok()));
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mController->doWithRetry<void>(PING_FN, "ping");
+ ASSERT_TRUE(result.isOk());
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ // Connector was called only by the first thread to use the api.
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestNoVibratorReturnsUnsupportedAndAttemptsToReconnect) {
+ mController = std::make_unique<
+ vibrator::HalController>(nullptr, [&](std::shared_ptr<vibrator::CallbackScheduler>) {
+ android_atomic_inc(&(this->mConnectCounter));
+ return nullptr;
+ });
+ ASSERT_EQ(0, mConnectCounter);
+
+ ASSERT_TRUE(mController->doWithRetry<void>(OFF_FN, "off").isUnsupported());
+ ASSERT_TRUE(mController->doWithRetry<void>(PING_FN, "ping").isUnsupported());
+
+ // One connection attempt per api call.
+ ASSERT_EQ(2, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestScheduledCallbackSurvivesReconnection) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), on(_, _))
+ .Times(Exactly(1))
+ .WillRepeatedly([&](milliseconds timeout, std::function<void()> callback) {
+ mMockHal.get()->getCallbackScheduler()->schedule(callback, timeout);
+ return vibrator::HalResult<void>::ok();
+ });
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message")));
+ EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message")));
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto onFn = [&](vibrator::HalWrapper* hal) { return hal->on(10ms, callback); };
+ ASSERT_TRUE(mController->doWithRetry<void>(onFn, "on").isOk());
+ ASSERT_TRUE(mController->doWithRetry<void>(PING_FN, "ping").isFailed());
+ mMockHal.reset();
+ ASSERT_EQ(0, *callbackCounter.get());
+
+ // Callback triggered even after HalWrapper was reconnected.
+ std::this_thread::sleep_for(15ms);
+ ASSERT_EQ(1, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
new file mode 100644
index 0000000..af0cdb8
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -0,0 +1,665 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "VibratorHalWrapperAidlTest"
+
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+#include <thread>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+#include "test_utils.h"
+
+using android::binder::Status;
+
+using android::hardware::vibrator::Braking;
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::CompositePrimitive;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+using android::hardware::vibrator::IVibrator;
+using android::hardware::vibrator::IVibratorCallback;
+using android::hardware::vibrator::PrimitivePwle;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockBinder : public BBinder {
+public:
+ MOCK_METHOD(status_t, linkToDeath,
+ (const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags), (override));
+ MOCK_METHOD(status_t, unlinkToDeath,
+ (const wp<DeathRecipient>& recipient, void* cookie, uint32_t flags,
+ wp<DeathRecipient>* outRecipient),
+ (override));
+ MOCK_METHOD(status_t, pingBinder, (), (override));
+};
+
+class MockIVibrator : public IVibrator {
+public:
+ MOCK_METHOD(Status, getCapabilities, (int32_t * ret), (override));
+ MOCK_METHOD(Status, off, (), (override));
+ MOCK_METHOD(Status, on, (int32_t timeout, const sp<IVibratorCallback>& cb), (override));
+ MOCK_METHOD(Status, perform,
+ (Effect e, EffectStrength s, const sp<IVibratorCallback>& cb, int32_t* ret),
+ (override));
+ MOCK_METHOD(Status, getSupportedEffects, (std::vector<Effect> * ret), (override));
+ MOCK_METHOD(Status, setAmplitude, (float amplitude), (override));
+ MOCK_METHOD(Status, setExternalControl, (bool enabled), (override));
+ MOCK_METHOD(Status, getCompositionDelayMax, (int32_t * ret), (override));
+ MOCK_METHOD(Status, getCompositionSizeMax, (int32_t * ret), (override));
+ MOCK_METHOD(Status, getSupportedPrimitives, (std::vector<CompositePrimitive> * ret),
+ (override));
+ MOCK_METHOD(Status, getPrimitiveDuration, (CompositePrimitive p, int32_t* ret), (override));
+ MOCK_METHOD(Status, compose,
+ (const std::vector<CompositeEffect>& e, const sp<IVibratorCallback>& cb),
+ (override));
+ MOCK_METHOD(Status, composePwle,
+ (const std::vector<PrimitivePwle>& e, const sp<IVibratorCallback>& cb), (override));
+ MOCK_METHOD(Status, getSupportedAlwaysOnEffects, (std::vector<Effect> * ret), (override));
+ MOCK_METHOD(Status, alwaysOnEnable, (int32_t id, Effect e, EffectStrength s), (override));
+ MOCK_METHOD(Status, alwaysOnDisable, (int32_t id), (override));
+ MOCK_METHOD(Status, getQFactor, (float * ret), (override));
+ MOCK_METHOD(Status, getResonantFrequency, (float * ret), (override));
+ MOCK_METHOD(Status, getFrequencyResolution, (float* ret), (override));
+ MOCK_METHOD(Status, getFrequencyMinimum, (float* ret), (override));
+ MOCK_METHOD(Status, getBandwidthAmplitudeMap, (std::vector<float> * ret), (override));
+ MOCK_METHOD(Status, getPwlePrimitiveDurationMax, (int32_t * ret), (override));
+ MOCK_METHOD(Status, getPwleCompositionSizeMax, (int32_t * ret), (override));
+ MOCK_METHOD(Status, getSupportedBraking, (std::vector<Braking> * ret), (override));
+ MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
+ MOCK_METHOD(std::string, getInterfaceHash, (), (override));
+ MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorHalWrapperAidlTest : public Test {
+public:
+ void SetUp() override {
+ mMockBinder = new StrictMock<MockBinder>();
+ mMockHal = new StrictMock<MockIVibrator>();
+ mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
+ mWrapper = std::make_unique<vibrator::AidlHalWrapper>(mMockScheduler, mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+ }
+
+protected:
+ std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
+ std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIVibrator>> mMockHal = nullptr;
+ sp<StrictMock<MockBinder>> mMockBinder = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+ACTION(TriggerCallbackInArg1) {
+ if (arg1 != nullptr) {
+ arg1->onComplete();
+ }
+}
+
+ACTION(TriggerCallbackInArg2) {
+ if (arg2 != nullptr) {
+ arg2->onComplete();
+ }
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestPing) {
+ EXPECT_CALL(*mMockHal.get(), onAsBinder())
+ .Times(Exactly(2))
+ .WillRepeatedly(Return(mMockBinder.get()));
+ EXPECT_CALL(*mMockBinder.get(), pingBinder())
+ .Times(Exactly(2))
+ .WillOnce(Return(android::OK))
+ .WillRepeatedly(Return(android::DEAD_OBJECT));
+
+ ASSERT_TRUE(mWrapper->ping().isOk());
+ ASSERT_TRUE(mWrapper->ping().isFailed());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestOnWithCallbackSupport) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), on(Eq(10), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), on(Eq(100), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), on(Eq(1000), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->on(10ms, callback).isOk());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->on(100ms, callback).isUnsupported());
+ // Callback not triggered for unsupported
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->on(1000ms, callback).isFailed());
+ // Callback not triggered on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestOnWithoutCallbackSupport) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<0>(IVibrator::CAP_COMPOSE_EFFECTS), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), on(Eq(10), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status()));
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ EXPECT_CALL(*mMockHal.get(), on(Eq(11), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), on(Eq(12), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->on(10ms, callback).isOk());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->on(11ms, callback).isUnsupported());
+ ASSERT_TRUE(mWrapper->on(12ms, callback).isFailed());
+
+ // Callback not triggered for unsupported and on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestOff) {
+ EXPECT_CALL(*mMockHal.get(), off())
+ .Times(Exactly(3))
+ .WillOnce(Return(Status()))
+ .WillOnce(
+ Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+
+ ASSERT_TRUE(mWrapper->off().isOk());
+ ASSERT_TRUE(mWrapper->off().isUnsupported());
+ ASSERT_TRUE(mWrapper->off().isFailed());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestSetAmplitude) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(0.1f))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(0.2f)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(0.5f)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ ASSERT_TRUE(mWrapper->setAmplitude(0.1f).isOk());
+ ASSERT_TRUE(mWrapper->setAmplitude(0.2f).isUnsupported());
+ ASSERT_TRUE(mWrapper->setAmplitude(0.5f).isFailed());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestSetExternalControl) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(false)))
+ .Times(Exactly(2))
+ .WillOnce(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ ASSERT_TRUE(mWrapper->setExternalControl(true).isOk());
+ ASSERT_TRUE(mWrapper->setExternalControl(false).isUnsupported());
+ ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestAlwaysOnEnable) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ alwaysOnEnable(Eq(1), Eq(Effect::CLICK), Eq(EffectStrength::LIGHT)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(),
+ alwaysOnEnable(Eq(2), Eq(Effect::TICK), Eq(EffectStrength::MEDIUM)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(),
+ alwaysOnEnable(Eq(3), Eq(Effect::POP), Eq(EffectStrength::STRONG)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ auto result = mWrapper->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->alwaysOnEnable(2, Effect::TICK, EffectStrength::MEDIUM);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->alwaysOnEnable(3, Effect::POP, EffectStrength::STRONG);
+ ASSERT_TRUE(result.isFailed());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestAlwaysOnDisable) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(1))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(2)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(3)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ ASSERT_TRUE(mWrapper->alwaysOnDisable(1).isOk());
+ ASSERT_TRUE(mWrapper->alwaysOnDisable(2).isUnsupported());
+ ASSERT_TRUE(mWrapper->alwaysOnDisable(3).isFailed());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetInfoDoesNotCacheFailedResult) {
+ constexpr float F_MIN = 100.f;
+ constexpr float F0 = 123.f;
+ constexpr float F_RESOLUTION = 0.5f;
+ constexpr float Q_FACTOR = 123.f;
+ std::vector<Effect> supportedEffects = {Effect::CLICK, Effect::TICK};
+ std::vector<CompositePrimitive> supportedPrimitives = {CompositePrimitive::CLICK};
+ std::vector<Braking> supportedBraking = {Braking::CLAB};
+ std::vector<float> amplitudes = {0.f, 1.f, 0.f};
+
+ std::vector<std::chrono::milliseconds> primitiveDurations;
+ constexpr auto primitiveRange = enum_range<CompositePrimitive>();
+ constexpr auto primitiveCount = std::distance(primitiveRange.begin(), primitiveRange.end());
+ primitiveDurations.resize(primitiveCount);
+ primitiveDurations[static_cast<size_t>(CompositePrimitive::CLICK)] = 10ms;
+
+ EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+ .Times(Exactly(2))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), getSupportedEffects(_))
+ .Times(Exactly(2))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(supportedEffects), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), getSupportedBraking(_))
+ .Times(Exactly(2))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(supportedBraking), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
+ .Times(Exactly(2))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::CLICK), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(10), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), getFrequencyMinimum(_))
+ .Times(Exactly(2))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(F_MIN), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), getResonantFrequency(_))
+ .Times(Exactly(2))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(F0), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), getFrequencyResolution(_))
+ .Times(Exactly(2))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(F_RESOLUTION), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), getQFactor(_))
+ .Times(Exactly(2))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(Q_FACTOR), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), getBandwidthAmplitudeMap(_))
+ .Times(Exactly(2))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(amplitudes), Return(Status())));
+
+ vibrator::Info failed = mWrapper->getInfo();
+ ASSERT_TRUE(failed.capabilities.isFailed());
+ ASSERT_TRUE(failed.supportedEffects.isFailed());
+ ASSERT_TRUE(failed.supportedBraking.isFailed());
+ ASSERT_TRUE(failed.supportedPrimitives.isFailed());
+ ASSERT_TRUE(failed.primitiveDurations.isFailed());
+ ASSERT_TRUE(failed.minFrequency.isFailed());
+ ASSERT_TRUE(failed.resonantFrequency.isFailed());
+ ASSERT_TRUE(failed.frequencyResolution.isFailed());
+ ASSERT_TRUE(failed.qFactor.isFailed());
+ ASSERT_TRUE(failed.maxAmplitudes.isFailed());
+
+ vibrator::Info successful = mWrapper->getInfo();
+ ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, successful.capabilities.value());
+ ASSERT_EQ(supportedEffects, successful.supportedEffects.value());
+ ASSERT_EQ(supportedBraking, successful.supportedBraking.value());
+ ASSERT_EQ(supportedPrimitives, successful.supportedPrimitives.value());
+ ASSERT_EQ(primitiveDurations, successful.primitiveDurations.value());
+ ASSERT_EQ(F_MIN, successful.minFrequency.value());
+ ASSERT_EQ(F0, successful.resonantFrequency.value());
+ ASSERT_EQ(F_RESOLUTION, successful.frequencyResolution.value());
+ ASSERT_EQ(Q_FACTOR, successful.qFactor.value());
+ ASSERT_EQ(amplitudes, successful.maxAmplitudes.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetInfoCachesResult) {
+ constexpr float F_MIN = 100.f;
+ constexpr float F0 = 123.f;
+ std::vector<Effect> supportedEffects = {Effect::CLICK, Effect::TICK};
+
+ EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), getSupportedEffects(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(supportedEffects), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), getQFactor(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), getFrequencyMinimum(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(F_MIN), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), getResonantFrequency(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(F0), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), getFrequencyResolution(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), getBandwidthAmplitudeMap(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), getSupportedBraking(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(
+ std::thread([&]() { ASSERT_TRUE(mWrapper->getInfo().capabilities.isOk()); }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ vibrator::Info info = mWrapper->getInfo();
+ ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, info.capabilities.value());
+ ASSERT_EQ(supportedEffects, info.supportedEffects.value());
+ ASSERT_TRUE(info.supportedBraking.isUnsupported());
+ ASSERT_TRUE(info.supportedPrimitives.isUnsupported());
+ ASSERT_TRUE(info.primitiveDurations.isUnsupported());
+ ASSERT_EQ(F_MIN, info.minFrequency.value());
+ ASSERT_EQ(F0, info.resonantFrequency.value());
+ ASSERT_TRUE(info.frequencyResolution.isUnsupported());
+ ASSERT_TRUE(info.qFactor.isUnsupported());
+ ASSERT_TRUE(info.maxAmplitudes.isUnsupported());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithCallbackSupport) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<0>(IVibrator::CAP_PERFORM_CALLBACK), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _, _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<3>(1000), TriggerCallbackInArg2(), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::POP), Eq(EffectStrength::MEDIUM), _, _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::THUD), Eq(EffectStrength::STRONG), _, _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(1000ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performEffect(Effect::POP, EffectStrength::MEDIUM, callback);
+ ASSERT_TRUE(result.isUnsupported());
+ // Callback not triggered for unsupported
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+ // Callback not triggered on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithoutCallbackSupport) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _, _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(10), Return(Status())));
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::POP), Eq(EffectStrength::MEDIUM), _, _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::THUD), Eq(EffectStrength::STRONG), _, _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performEffect(Effect::POP, EffectStrength::MEDIUM, callback);
+ ASSERT_TRUE(result.isUnsupported());
+
+ result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ // Callback not triggered for unsupported and on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestPerformComposedEffect) {
+ std::vector<CompositePrimitive> supportedPrimitives = {CompositePrimitive::CLICK,
+ CompositePrimitive::SPIN,
+ CompositePrimitive::THUD};
+ std::vector<CompositeEffect> emptyEffects, singleEffect, multipleEffects;
+ singleEffect.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::CLICK, 10ms, 0.0f));
+ multipleEffects.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f));
+ multipleEffects.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f));
+
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::CLICK), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(1), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::SPIN), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::THUD), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(3), Return(Status())));
+
+ EXPECT_CALL(*mMockHal.get(), compose(Eq(emptyEffects), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), compose(Eq(singleEffect), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performComposedEffect(emptyEffects, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(0ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performComposedEffect(singleEffect, callback);
+ ASSERT_TRUE(result.isUnsupported());
+ // Callback not triggered for unsupported
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performComposedEffect(multipleEffects, callback);
+ ASSERT_TRUE(result.isFailed());
+ // Callback not triggered on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestPerformComposedCachesPrimitiveDurationsAndIgnoresFailures) {
+ std::vector<CompositePrimitive> supportedPrimitives = {CompositePrimitive::SPIN,
+ CompositePrimitive::THUD};
+ std::vector<CompositeEffect> multipleEffects;
+ multipleEffects.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 10ms, 0.5f));
+ multipleEffects.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 100ms, 1.0f));
+
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::SPIN), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::THUD), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+
+ EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::SPIN), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::THUD), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _))
+ .Times(Exactly(2))
+ .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performComposedEffect(multipleEffects, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(112ms, result.value()); // Failed primitive durations counted as 1.
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performComposedEffect(multipleEffects, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(114ms, result.value()); // Second fetch succeeds and returns primitive duration.
+ ASSERT_EQ(2, *callbackCounter.get());
+
+ result = mWrapper->performComposedEffect(multipleEffects, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(114ms, result.value()); // Cached durations not fetched again, same duration returned.
+ ASSERT_EQ(3, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestPerformPwleEffect) {
+ std::vector<PrimitivePwle> emptyPrimitives, multiplePrimitives;
+ multiplePrimitives.push_back(vibrator::TestFactory::createActivePwle(0, 1, 0, 1, 10ms));
+ multiplePrimitives.push_back(vibrator::TestFactory::createBrakingPwle(Braking::NONE, 100ms));
+
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), composePwle(Eq(emptyPrimitives), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), composePwle(Eq(multiplePrimitives), _))
+ .Times(Exactly(2))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performPwleEffect(emptyPrimitives, callback);
+ ASSERT_TRUE(result.isUnsupported());
+ // Callback not triggered on failure
+ ASSERT_EQ(0, *callbackCounter.get());
+
+ result = mWrapper->performPwleEffect(multiplePrimitives, callback);
+ ASSERT_TRUE(result.isFailed());
+ // Callback not triggered for unsupported
+ ASSERT_EQ(0, *callbackCounter.get());
+
+ result = mWrapper->performPwleEffect(multiplePrimitives, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(1, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
new file mode 100644
index 0000000..96b2582
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "VibratorHalWrapperHidlV1_0Test"
+
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+#include <thread>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+#include "test_utils.h"
+
+namespace V1_0 = android::hardware::vibrator::V1_0;
+
+using android::hardware::vibrator::Braking;
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::CompositePrimitive;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+using android::hardware::vibrator::IVibrator;
+using android::hardware::vibrator::PrimitivePwle;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIVibratorV1_0 : public V1_0::IVibrator {
+public:
+ MOCK_METHOD(hardware::Return<void>, ping, (), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
+ MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
+ MOCK_METHOD(hardware::Return<void>, perform,
+ (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorHalWrapperHidlV1_0Test : public Test {
+public:
+ void SetUp() override {
+ mMockHal = new StrictMock<MockIVibratorV1_0>();
+ mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
+ mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_0>(mMockScheduler, mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+ }
+
+protected:
+ std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
+ std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIVibratorV1_0>> mMockHal = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestPing) {
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(2))
+ .WillOnce([]() { return hardware::Return<void>(); })
+ .WillRepeatedly([]() {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ ASSERT_TRUE(mWrapper->ping().isOk());
+ ASSERT_TRUE(mWrapper->ping().isFailed());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestOn) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(1))))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](uint32_t) { return hardware::Return<V1_0::Status>(V1_0::Status::OK); });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(1ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(10))))
+ .Times(Exactly(1))
+ .WillRepeatedly([](uint32_t) {
+ return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
+ });
+ EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(11))))
+ .Times(Exactly(1))
+ .WillRepeatedly([](uint32_t) {
+ return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE);
+ });
+ EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(12))))
+ .Times(Exactly(1))
+ .WillRepeatedly([](uint32_t) {
+ return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->on(1ms, callback).isOk());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->on(10ms, callback).isUnsupported());
+ ASSERT_TRUE(mWrapper->on(11ms, callback).isFailed());
+ ASSERT_TRUE(mWrapper->on(12ms, callback).isFailed());
+
+ // Callback not triggered for unsupported and on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestOff) {
+ EXPECT_CALL(*mMockHal.get(), off())
+ .Times(Exactly(4))
+ .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::OK); })
+ .WillOnce([]() {
+ return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
+ })
+ .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE); })
+ .WillRepeatedly([]() {
+ return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ ASSERT_TRUE(mWrapper->off().isOk());
+ ASSERT_TRUE(mWrapper->off().isUnsupported());
+ ASSERT_TRUE(mWrapper->off().isFailed());
+ ASSERT_TRUE(mWrapper->off().isFailed());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestSetAmplitude) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(1))))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](uint8_t) { return hardware::Return<V1_0::Status>(V1_0::Status::OK); });
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(2))))
+ .Times(Exactly(1))
+ .WillRepeatedly([](uint8_t) {
+ return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
+ });
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(3))))
+ .Times(Exactly(1))
+ .WillRepeatedly([](uint8_t) {
+ return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE);
+ });
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(4))))
+ .Times(Exactly(1))
+ .WillRepeatedly([](uint8_t) {
+ return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ auto maxAmplitude = std::numeric_limits<uint8_t>::max();
+ ASSERT_TRUE(mWrapper->setAmplitude(1.0f / maxAmplitude).isOk());
+ ASSERT_TRUE(mWrapper->setAmplitude(2.0f / maxAmplitude).isUnsupported());
+ ASSERT_TRUE(mWrapper->setAmplitude(3.0f / maxAmplitude).isFailed());
+ ASSERT_TRUE(mWrapper->setAmplitude(4.0f / maxAmplitude).isFailed());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestSetExternalControlUnsupported) {
+ ASSERT_TRUE(mWrapper->setExternalControl(true).isUnsupported());
+ ASSERT_TRUE(mWrapper->setExternalControl(false).isUnsupported());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestAlwaysOnEnableUnsupported) {
+ ASSERT_TRUE(mWrapper->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isUnsupported());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestAlwaysOnDisableUnsupported) {
+ ASSERT_TRUE(mWrapper->alwaysOnDisable(1).isUnsupported());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoDoesNotCacheFailedResult) {
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(2))
+ .WillOnce([]() {
+ return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
+ })
+ .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+
+ ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
+
+ vibrator::Info info = mWrapper->getInfo();
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, info.capabilities.value());
+ ASSERT_TRUE(info.supportedEffects.isUnsupported());
+ ASSERT_TRUE(info.supportedBraking.isUnsupported());
+ ASSERT_TRUE(info.supportedPrimitives.isUnsupported());
+ ASSERT_TRUE(info.primitiveDurations.isUnsupported());
+ ASSERT_TRUE(info.minFrequency.isUnsupported());
+ ASSERT_TRUE(info.resonantFrequency.isUnsupported());
+ ASSERT_TRUE(info.frequencyResolution.isUnsupported());
+ ASSERT_TRUE(info.qFactor.isUnsupported());
+ ASSERT_TRUE(info.maxAmplitudes.isUnsupported());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoWithoutAmplitudeControl) {
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillRepeatedly([]() {
+ return hardware::Return<bool>(false);
+ });
+
+ ASSERT_EQ(vibrator::Capabilities::NONE, mWrapper->getInfo().capabilities.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoCachesResult) {
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillRepeatedly([]() {
+ return hardware::Return<bool>(true);
+ });
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(
+ std::thread([&]() { ASSERT_TRUE(mWrapper->getInfo().capabilities.isOk()); }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ vibrator::Info info = mWrapper->getInfo();
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, info.capabilities.value());
+ ASSERT_TRUE(info.supportedEffects.isUnsupported());
+ ASSERT_TRUE(info.supportedBraking.isUnsupported());
+ ASSERT_TRUE(info.supportedPrimitives.isUnsupported());
+ ASSERT_TRUE(info.primitiveDurations.isUnsupported());
+ ASSERT_TRUE(info.minFrequency.isUnsupported());
+ ASSERT_TRUE(info.resonantFrequency.isUnsupported());
+ ASSERT_TRUE(info.frequencyResolution.isUnsupported());
+ ASSERT_TRUE(info.qFactor.isUnsupported());
+ ASSERT_TRUE(info.maxAmplitudes.isUnsupported());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffect) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ EXPECT_CALL(*mMockHal.get(),
+ perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::MEDIUM), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) {
+ cb(V1_0::Status::UNSUPPORTED_OPERATION, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockHal.get(),
+ perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::STRONG), _))
+ .Times(Exactly(2))
+ .WillOnce([](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) {
+ cb(V1_0::Status::BAD_VALUE, 10);
+ return hardware::Return<void>();
+ })
+ .WillRepeatedly(
+ [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performEffect(Effect::CLICK, EffectStrength::MEDIUM, callback);
+ ASSERT_TRUE(result.isUnsupported());
+
+ result = mWrapper->performEffect(Effect::CLICK, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ result = mWrapper->performEffect(Effect::CLICK, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ // Callback not triggered for unsupported and on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffectUnsupported) {
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ // Using TICK that is only available in v1.1
+ auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isUnsupported());
+ // No callback is triggered.
+ ASSERT_EQ(0, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformComposedEffectUnsupported) {
+ std::vector<CompositeEffect> emptyEffects, singleEffect, multipleEffects;
+ singleEffect.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::CLICK, 10ms, 0.0f));
+ multipleEffects.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f));
+ multipleEffects.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f));
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->performComposedEffect(singleEffect, callback).isUnsupported());
+ ASSERT_TRUE(mWrapper->performComposedEffect(multipleEffects, callback).isUnsupported());
+
+ // No callback is triggered.
+ ASSERT_EQ(0, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformPwleEffectUnsupported) {
+ std::vector<PrimitivePwle> emptyPrimitives, multiplePrimitives;
+ multiplePrimitives.push_back(vibrator::TestFactory::createActivePwle(0, 1, 0, 1, 10ms));
+ multiplePrimitives.push_back(vibrator::TestFactory::createBrakingPwle(Braking::NONE, 100ms));
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->performPwleEffect(emptyPrimitives, callback).isUnsupported());
+ ASSERT_TRUE(mWrapper->performPwleEffect(multiplePrimitives, callback).isUnsupported());
+
+ // No callback is triggered.
+ ASSERT_EQ(0, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp
new file mode 100644
index 0000000..d887efc
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "VibratorHalWrapperHidlV1_1Test"
+
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+#include "test_utils.h"
+
+namespace V1_0 = android::hardware::vibrator::V1_0;
+namespace V1_1 = android::hardware::vibrator::V1_1;
+
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIVibratorV1_1 : public V1_1::IVibrator {
+public:
+ MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
+ MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
+ MOCK_METHOD(hardware::Return<void>, perform,
+ (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+ MOCK_METHOD(hardware::Return<void>, perform_1_1,
+ (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb),
+ (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorHalWrapperHidlV1_1Test : public Test {
+public:
+ void SetUp() override {
+ mMockHal = new StrictMock<MockIVibratorV1_1>();
+ mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
+ mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_1>(mMockScheduler, mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+ }
+
+protected:
+ std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
+ std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIVibratorV1_1>> mMockHal = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectV1_0) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_1::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
+
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectV1_1) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
+ MockIVibratorV1_1::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::MEDIUM), _))
+ .Times(Exactly(1))
+ .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
+ MockIVibratorV1_1::perform_cb cb) {
+ cb(V1_0::Status::UNSUPPORTED_OPERATION, 0);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::STRONG), _))
+ .Times(Exactly(2))
+ .WillOnce([](V1_1::Effect_1_1, V1_0::EffectStrength,
+ MockIVibratorV1_1::perform_cb cb) {
+ cb(V1_0::Status::BAD_VALUE, 0);
+ return hardware::Return<void>();
+ })
+ .WillRepeatedly(
+ [](V1_1::Effect_1_1, V1_0::EffectStrength, MockIVibratorV1_1::perform_cb) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performEffect(Effect::TICK, EffectStrength::MEDIUM, callback);
+ ASSERT_TRUE(result.isUnsupported());
+
+ result = mWrapper->performEffect(Effect::TICK, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ result = mWrapper->performEffect(Effect::TICK, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ // Callback not triggered for unsupported and on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectUnsupported) {
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ // Using THUD that is only available in v1.2
+ auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isUnsupported());
+ // No callback is triggered.
+ ASSERT_EQ(0, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp
new file mode 100644
index 0000000..26d9350
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "VibratorHalWrapperHidlV1_2Test"
+
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+#include "test_utils.h"
+
+namespace V1_0 = android::hardware::vibrator::V1_0;
+namespace V1_1 = android::hardware::vibrator::V1_1;
+namespace V1_2 = android::hardware::vibrator::V1_2;
+
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIVibratorV1_2 : public V1_2::IVibrator {
+public:
+ MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
+ MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
+ MOCK_METHOD(hardware::Return<void>, perform,
+ (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+ MOCK_METHOD(hardware::Return<void>, perform_1_1,
+ (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb),
+ (override));
+ MOCK_METHOD(hardware::Return<void>, perform_1_2,
+ (V1_2::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorHalWrapperHidlV1_2Test : public Test {
+public:
+ void SetUp() override {
+ mMockHal = new StrictMock<MockIVibratorV1_2>();
+ mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
+ mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_2>(mMockScheduler, mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+ }
+
+protected:
+ std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
+ std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIVibratorV1_2>> mMockHal = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_0) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
+
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_1) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
+ MockIVibratorV1_2::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
+
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_2) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::MEDIUM), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
+ cb(V1_0::Status::UNSUPPORTED_OPERATION, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::STRONG), _))
+ .Times(Exactly(2))
+ .WillOnce([](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
+ cb(V1_0::Status::BAD_VALUE, 10);
+ return hardware::Return<void>();
+ })
+ .WillRepeatedly(
+ [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performEffect(Effect::THUD, EffectStrength::MEDIUM, callback);
+ ASSERT_TRUE(result.isUnsupported());
+
+ result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ // Callback not triggered for unsupported and on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectUnsupported) {
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ // Using TEXTURE_TICK that is only available in v1.3
+ auto result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isUnsupported());
+ // No callback is triggered.
+ ASSERT_EQ(0, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
new file mode 100644
index 0000000..a6f1a74
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "VibratorHalWrapperHidlV1_3Test"
+
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+#include <thread>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+#include "test_utils.h"
+
+namespace V1_0 = android::hardware::vibrator::V1_0;
+namespace V1_1 = android::hardware::vibrator::V1_1;
+namespace V1_2 = android::hardware::vibrator::V1_2;
+namespace V1_3 = android::hardware::vibrator::V1_3;
+
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+using android::hardware::vibrator::IVibrator;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIVibratorV1_3 : public V1_3::IVibrator {
+public:
+ MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
+ MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
+ MOCK_METHOD(hardware::Return<bool>, supportsExternalControl, (), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, setExternalControl, (bool enabled), (override));
+ MOCK_METHOD(hardware::Return<void>, perform,
+ (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+ MOCK_METHOD(hardware::Return<void>, perform_1_1,
+ (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb),
+ (override));
+ MOCK_METHOD(hardware::Return<void>, perform_1_2,
+ (V1_2::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+ MOCK_METHOD(hardware::Return<void>, perform_1_3,
+ (V1_3::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorHalWrapperHidlV1_3Test : public Test {
+public:
+ void SetUp() override {
+ mMockHal = new StrictMock<MockIVibratorV1_3>();
+ mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
+ mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_3>(mMockScheduler, mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+ }
+
+protected:
+ std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
+ std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIVibratorV1_3>> mMockHal = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestSetExternalControl) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true)))
+ .Times(Exactly(2))
+ .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::OK); })
+ .WillRepeatedly([]() {
+ return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
+ });
+ EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(false)))
+ .Times(Exactly(2))
+ .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE); })
+ .WillRepeatedly([]() {
+ return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ ASSERT_TRUE(mWrapper->setExternalControl(true).isOk());
+ ASSERT_TRUE(mWrapper->setExternalControl(true).isUnsupported());
+ ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed());
+ ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoSuccessful) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
+ return hardware::Return<bool>(true);
+ });
+ }
+
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL | vibrator::Capabilities::EXTERNAL_CONTROL |
+ vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL,
+ mWrapper->getInfo().capabilities.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoOnlyAmplitudeControl) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() {
+ return hardware::Return<bool>(true);
+ });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
+ return hardware::Return<bool>(false);
+ });
+ }
+
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoOnlyExternalControl) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() {
+ return hardware::Return<bool>(false);
+ });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
+ return hardware::Return<bool>(true);
+ });
+ }
+
+ ASSERT_EQ(vibrator::Capabilities::EXTERNAL_CONTROL, mWrapper->getInfo().capabilities.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoNoCapabilities) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() { return hardware::Return<bool>(false); });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
+ return hardware::Return<bool>(false);
+ });
+ }
+
+ ASSERT_EQ(vibrator::Capabilities::NONE, mWrapper->getInfo().capabilities.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoFailed) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() {
+ return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() {
+ return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
+ ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoCachesResult) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
+ return hardware::Return<bool>(false);
+ });
+ }
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(
+ std::thread([&]() { ASSERT_TRUE(mWrapper->getInfo().capabilities.isOk()); }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoDoesNotCacheFailedResult) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() {
+ return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() {
+ return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() { return hardware::Return<bool>(false); });
+ }
+
+ // Call to supportsAmplitudeControl failed.
+ ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
+
+ // Call to supportsExternalControl failed.
+ ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
+
+ // Returns successful result from third call.
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value());
+
+ // Returns cached successful result.
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_0) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
+
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_1) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
+ MockIVibratorV1_3::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
+
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_2) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback);
+
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_3) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::MEDIUM),
+ _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
+ cb(V1_0::Status::UNSUPPORTED_OPERATION, 0);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::STRONG),
+ _))
+ .Times(Exactly(2))
+ .WillOnce([](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
+ cb(V1_0::Status::BAD_VALUE, 0);
+ return hardware::Return<void>();
+ })
+ .WillRepeatedly(
+ [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::MEDIUM, callback);
+ ASSERT_TRUE(result.isUnsupported());
+
+ result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ // Callback not triggered for unsupported and on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp b/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp
new file mode 100644
index 0000000..e5fbbae
--- /dev/null
+++ b/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "VibratorManagerHalControllerTest"
+
+#include <cutils/atomic.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorManagerHalController.h>
+
+#include "test_utils.h"
+
+using android::vibrator::HalController;
+
+using namespace android;
+using namespace testing;
+
+static constexpr int MAX_ATTEMPTS = 2;
+static const std::vector<int32_t> VIBRATOR_IDS = {1, 2};
+static constexpr int VIBRATOR_ID = 1;
+
+class MockManagerHalWrapper : public vibrator::ManagerHalWrapper {
+public:
+ MOCK_METHOD(void, tryReconnect, (), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, ping, (), (override));
+ MOCK_METHOD(vibrator::HalResult<vibrator::ManagerCapabilities>, getCapabilities, (),
+ (override));
+ MOCK_METHOD(vibrator::HalResult<std::vector<int32_t>>, getVibratorIds, (), (override));
+ MOCK_METHOD(vibrator::HalResult<std::shared_ptr<HalController>>, getVibrator, (int32_t id),
+ (override));
+ MOCK_METHOD(vibrator::HalResult<void>, prepareSynced, (const std::vector<int32_t>& ids),
+ (override));
+ MOCK_METHOD(vibrator::HalResult<void>, triggerSynced,
+ (const std::function<void()>& completionCallback), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, cancelSynced, (), (override));
+};
+
+class VibratorManagerHalControllerTest : public Test {
+public:
+ void SetUp() override {
+ mConnectCounter = 0;
+ auto callbackScheduler = std::make_shared<vibrator::CallbackScheduler>();
+ mMockHal = std::make_shared<StrictMock<MockManagerHalWrapper>>();
+ auto connector = [this](std::shared_ptr<vibrator::CallbackScheduler>) {
+ android_atomic_inc(&mConnectCounter);
+ return mMockHal;
+ };
+ mController = std::make_unique<vibrator::ManagerHalController>(std::move(callbackScheduler),
+ connector);
+ ASSERT_NE(mController, nullptr);
+ }
+
+protected:
+ int32_t mConnectCounter;
+ std::shared_ptr<MockManagerHalWrapper> mMockHal;
+ std::unique_ptr<vibrator::ManagerHalController> mController;
+
+ void setHalExpectations(int32_t cardinality, vibrator::HalResult<void> voidResult,
+ vibrator::HalResult<vibrator::ManagerCapabilities> capabilitiesResult,
+ vibrator::HalResult<std::vector<int32_t>> idsResult,
+ vibrator::HalResult<std::shared_ptr<HalController>> vibratorResult) {
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(voidResult));
+ EXPECT_CALL(*mMockHal.get(), getCapabilities())
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(capabilitiesResult));
+ EXPECT_CALL(*mMockHal.get(), getVibratorIds())
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(idsResult));
+ EXPECT_CALL(*mMockHal.get(), getVibrator(_))
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(vibratorResult));
+ EXPECT_CALL(*mMockHal.get(), prepareSynced(_))
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(voidResult));
+ EXPECT_CALL(*mMockHal.get(), triggerSynced(_))
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(voidResult));
+ EXPECT_CALL(*mMockHal.get(), cancelSynced())
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(voidResult));
+
+ if (cardinality > 1) {
+ // One reconnection call after each failure.
+ EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(7 * cardinality));
+ }
+ }
+};
+
+TEST_F(VibratorManagerHalControllerTest, TestInit) {
+ mController->init();
+ ASSERT_EQ(1, mConnectCounter);
+
+ // Noop when wrapper was already initialized.
+ mController->init();
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorManagerHalControllerTest, TestApiCallsAreForwardedToHal) {
+ setHalExpectations(/* cardinality= */ 1, vibrator::HalResult<void>::ok(),
+ vibrator::HalResult<vibrator::ManagerCapabilities>::ok(
+ vibrator::ManagerCapabilities::SYNC),
+ vibrator::HalResult<std::vector<int32_t>>::ok(VIBRATOR_IDS),
+ vibrator::HalResult<std::shared_ptr<HalController>>::ok(nullptr));
+
+ ASSERT_TRUE(mController->ping().isOk());
+
+ auto getCapabilitiesResult = mController->getCapabilities();
+ ASSERT_TRUE(getCapabilitiesResult.isOk());
+ ASSERT_EQ(vibrator::ManagerCapabilities::SYNC, getCapabilitiesResult.value());
+
+ auto getVibratorIdsResult = mController->getVibratorIds();
+ ASSERT_TRUE(getVibratorIdsResult.isOk());
+ ASSERT_EQ(VIBRATOR_IDS, getVibratorIdsResult.value());
+
+ auto getVibratorResult = mController->getVibrator(VIBRATOR_ID);
+ ASSERT_TRUE(getVibratorResult.isOk());
+ ASSERT_EQ(nullptr, getVibratorResult.value());
+
+ ASSERT_TRUE(mController->prepareSynced(VIBRATOR_IDS).isOk());
+ ASSERT_TRUE(mController->triggerSynced([]() {}).isOk());
+ ASSERT_TRUE(mController->cancelSynced().isOk());
+
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorManagerHalControllerTest, TestUnsupportedApiResultDoNotResetHalConnection) {
+ setHalExpectations(/* cardinality= */ 1, vibrator::HalResult<void>::unsupported(),
+ vibrator::HalResult<vibrator::ManagerCapabilities>::unsupported(),
+ vibrator::HalResult<std::vector<int32_t>>::unsupported(),
+ vibrator::HalResult<std::shared_ptr<HalController>>::unsupported());
+
+ ASSERT_EQ(0, mConnectCounter);
+
+ ASSERT_TRUE(mController->ping().isUnsupported());
+ ASSERT_TRUE(mController->getCapabilities().isUnsupported());
+ ASSERT_TRUE(mController->getVibratorIds().isUnsupported());
+ ASSERT_TRUE(mController->getVibrator(VIBRATOR_ID).isUnsupported());
+ ASSERT_TRUE(mController->prepareSynced(VIBRATOR_IDS).isUnsupported());
+ ASSERT_TRUE(mController->triggerSynced([]() {}).isUnsupported());
+ ASSERT_TRUE(mController->cancelSynced().isUnsupported());
+
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorManagerHalControllerTest, TestFailedApiResultResetsHalConnection) {
+ setHalExpectations(MAX_ATTEMPTS, vibrator::HalResult<void>::failed("message"),
+ vibrator::HalResult<vibrator::ManagerCapabilities>::failed("message"),
+ vibrator::HalResult<std::vector<int32_t>>::failed("message"),
+ vibrator::HalResult<std::shared_ptr<HalController>>::failed("message"));
+
+ ASSERT_EQ(0, mConnectCounter);
+
+ ASSERT_TRUE(mController->ping().isFailed());
+ ASSERT_TRUE(mController->getCapabilities().isFailed());
+ ASSERT_TRUE(mController->getVibratorIds().isFailed());
+ ASSERT_TRUE(mController->getVibrator(VIBRATOR_ID).isFailed());
+ ASSERT_TRUE(mController->prepareSynced(VIBRATOR_IDS).isFailed());
+ ASSERT_TRUE(mController->triggerSynced([]() {}).isFailed());
+ ASSERT_TRUE(mController->cancelSynced().isFailed());
+
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorManagerHalControllerTest, TestFailedApiResultReturnsSuccessAfterRetries) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message")));
+ EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::ok()));
+ }
+
+ ASSERT_EQ(0, mConnectCounter);
+ ASSERT_TRUE(mController->ping().isOk());
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorManagerHalControllerTest, TestMultiThreadConnectsOnlyOnce) {
+ ASSERT_EQ(0, mConnectCounter);
+
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(10))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::ok()));
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() { ASSERT_TRUE(mController->ping().isOk()); }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ // Connector was called only by the first thread to use the api.
+ ASSERT_EQ(1, mConnectCounter);
+}
diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
new file mode 100644
index 0000000..548d028
--- /dev/null
+++ b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "VibratorManagerHalWrapperAidlTest"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorManagerHalWrapper.h>
+
+#include "test_utils.h"
+
+using android::binder::Status;
+
+using android::hardware::vibrator::Braking;
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::CompositePrimitive;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+using android::hardware::vibrator::IVibrator;
+using android::hardware::vibrator::IVibratorCallback;
+using android::hardware::vibrator::IVibratorManager;
+using android::hardware::vibrator::PrimitivePwle;
+
+using namespace android;
+using namespace testing;
+
+static const auto OFF_FN = [](vibrator::HalWrapper* hal) { return hal->off(); };
+
+class MockBinder : public BBinder {
+public:
+ MOCK_METHOD(status_t, linkToDeath,
+ (const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags), (override));
+ MOCK_METHOD(status_t, unlinkToDeath,
+ (const wp<DeathRecipient>& recipient, void* cookie, uint32_t flags,
+ wp<DeathRecipient>* outRecipient),
+ (override));
+ MOCK_METHOD(status_t, pingBinder, (), (override));
+};
+
+class MockIVibrator : public IVibrator {
+public:
+ MOCK_METHOD(Status, getCapabilities, (int32_t * ret), (override));
+ MOCK_METHOD(Status, off, (), (override));
+ MOCK_METHOD(Status, on, (int32_t timeout, const sp<IVibratorCallback>& cb), (override));
+ MOCK_METHOD(Status, perform,
+ (Effect e, EffectStrength s, const sp<IVibratorCallback>& cb, int32_t* ret),
+ (override));
+ MOCK_METHOD(Status, getSupportedEffects, (std::vector<Effect> * ret), (override));
+ MOCK_METHOD(Status, setAmplitude, (float amplitude), (override));
+ MOCK_METHOD(Status, setExternalControl, (bool enabled), (override));
+ MOCK_METHOD(Status, getCompositionDelayMax, (int32_t * ret), (override));
+ MOCK_METHOD(Status, getCompositionSizeMax, (int32_t * ret), (override));
+ MOCK_METHOD(Status, getSupportedPrimitives, (std::vector<CompositePrimitive> * ret),
+ (override));
+ MOCK_METHOD(Status, getPrimitiveDuration, (CompositePrimitive p, int32_t* ret), (override));
+ MOCK_METHOD(Status, compose,
+ (const std::vector<CompositeEffect>& e, const sp<IVibratorCallback>& cb),
+ (override));
+ MOCK_METHOD(Status, composePwle,
+ (const std::vector<PrimitivePwle>& e, const sp<IVibratorCallback>& cb), (override));
+ MOCK_METHOD(Status, getSupportedAlwaysOnEffects, (std::vector<Effect> * ret), (override));
+ MOCK_METHOD(Status, alwaysOnEnable, (int32_t id, Effect e, EffectStrength s), (override));
+ MOCK_METHOD(Status, alwaysOnDisable, (int32_t id), (override));
+ MOCK_METHOD(Status, getQFactor, (float * ret), (override));
+ MOCK_METHOD(Status, getResonantFrequency, (float * ret), (override));
+ MOCK_METHOD(Status, getFrequencyResolution, (float* ret), (override));
+ MOCK_METHOD(Status, getFrequencyMinimum, (float* ret), (override));
+ MOCK_METHOD(Status, getBandwidthAmplitudeMap, (std::vector<float> * ret), (override));
+ MOCK_METHOD(Status, getPwlePrimitiveDurationMax, (int32_t * ret), (override));
+ MOCK_METHOD(Status, getPwleCompositionSizeMax, (int32_t * ret), (override));
+ MOCK_METHOD(Status, getSupportedBraking, (std::vector<Braking> * ret), (override));
+ MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
+ MOCK_METHOD(std::string, getInterfaceHash, (), (override));
+ MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+};
+
+class MockIVibratorManager : public IVibratorManager {
+public:
+ MOCK_METHOD(Status, getCapabilities, (int32_t * ret), (override));
+ MOCK_METHOD(Status, getVibratorIds, (std::vector<int32_t> * ret), (override));
+ MOCK_METHOD(Status, getVibrator, (int32_t id, sp<IVibrator>* ret), (override));
+ MOCK_METHOD(Status, prepareSynced, (const std::vector<int32_t>& ids), (override));
+ MOCK_METHOD(Status, triggerSynced, (const sp<IVibratorCallback>& cb), (override));
+ MOCK_METHOD(Status, cancelSynced, (), (override));
+ MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
+ MOCK_METHOD(std::string, getInterfaceHash, (), (override));
+ MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorManagerHalWrapperAidlTest : public Test {
+public:
+ void SetUp() override {
+ mMockBinder = new StrictMock<MockBinder>();
+ mMockVibrator = new StrictMock<MockIVibrator>();
+ mMockHal = new StrictMock<MockIVibratorManager>();
+ mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
+ mWrapper = std::make_unique<vibrator::AidlManagerHalWrapper>(mMockScheduler, mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+ }
+
+protected:
+ std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
+ std::unique_ptr<vibrator::ManagerHalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIVibratorManager>> mMockHal = nullptr;
+ sp<StrictMock<MockIVibrator>> mMockVibrator = nullptr;
+ sp<StrictMock<MockBinder>> mMockBinder = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+static const std::vector<int32_t> kVibratorIds = {1, 2};
+static constexpr int kVibratorId = 1;
+
+ACTION(TriggerCallback) {
+ if (arg0 != nullptr) {
+ arg0->onComplete();
+ }
+}
+
+TEST_F(VibratorManagerHalWrapperAidlTest, TestPing) {
+ EXPECT_CALL(*mMockHal.get(), onAsBinder())
+ .Times(Exactly(2))
+ .WillRepeatedly(Return(mMockBinder.get()));
+ EXPECT_CALL(*mMockBinder.get(), pingBinder())
+ .Times(Exactly(2))
+ .WillOnce(Return(android::OK))
+ .WillRepeatedly(Return(android::DEAD_OBJECT));
+
+ ASSERT_TRUE(mWrapper->ping().isOk());
+ ASSERT_TRUE(mWrapper->ping().isFailed());
+}
+
+TEST_F(VibratorManagerHalWrapperAidlTest, TestGetCapabilitiesDoesNotCacheFailedResult) {
+ EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+ .Times(Exactly(3))
+ .WillOnce(
+ Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(IVibratorManager::CAP_SYNC), Return(Status())));
+
+ ASSERT_TRUE(mWrapper->getCapabilities().isUnsupported());
+ ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::ManagerCapabilities::SYNC, result.value());
+}
+
+TEST_F(VibratorManagerHalWrapperAidlTest, TestGetCapabilitiesCachesResult) {
+ EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(IVibratorManager::CAP_SYNC), Return(Status())));
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::ManagerCapabilities::SYNC, result.value());
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::ManagerCapabilities::SYNC, result.value());
+}
+
+TEST_F(VibratorManagerHalWrapperAidlTest, TestGetVibratorIdsDoesNotCacheFailedResult) {
+ EXPECT_CALL(*mMockHal.get(), getVibratorIds(_))
+ .Times(Exactly(3))
+ .WillOnce(
+ Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(kVibratorIds), Return(Status())));
+
+ ASSERT_TRUE(mWrapper->getVibratorIds().isUnsupported());
+ ASSERT_TRUE(mWrapper->getVibratorIds().isFailed());
+
+ auto result = mWrapper->getVibratorIds();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(kVibratorIds, result.value());
+}
+
+TEST_F(VibratorManagerHalWrapperAidlTest, TestGetVibratorIdsCachesResult) {
+ EXPECT_CALL(*mMockHal.get(), getVibratorIds(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(kVibratorIds), Return(Status())));
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mWrapper->getVibratorIds();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(kVibratorIds, result.value());
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ auto result = mWrapper->getVibratorIds();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(kVibratorIds, result.value());
+}
+
+TEST_F(VibratorManagerHalWrapperAidlTest, TestGetVibratorWithValidIdReturnsController) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), getVibratorIds(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(kVibratorIds), Return(Status())));
+
+ EXPECT_CALL(*mMockHal.get(), getVibrator(Eq(kVibratorId), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(mMockVibrator), Return(Status())));
+ }
+
+ auto result = mWrapper->getVibrator(kVibratorId);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_NE(nullptr, result.value().get());
+ ASSERT_TRUE(result.value().get()->init());
+}
+
+TEST_F(VibratorManagerHalWrapperAidlTest, TestGetVibratorWithInvalidIdFails) {
+ EXPECT_CALL(*mMockHal.get(), getVibratorIds(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(kVibratorIds), Return(Status())));
+
+ ASSERT_TRUE(mWrapper->getVibrator(0).isFailed());
+}
+
+TEST_F(VibratorManagerHalWrapperAidlTest, TestGetVibratorRecoversVibratorPointer) {
+ EXPECT_CALL(*mMockHal.get(), getVibratorIds(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(kVibratorIds), Return(Status())));
+
+ EXPECT_CALL(*mMockHal.get(), getVibrator(Eq(kVibratorId), _))
+ .Times(Exactly(3))
+ .WillOnce(DoAll(SetArgPointee<1>(nullptr),
+ Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(mMockVibrator), Return(Status())));
+
+ EXPECT_CALL(*mMockVibrator.get(), off())
+ .Times(Exactly(3))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(Return(Status()));
+
+ // Get vibrator controller is successful even if first getVibrator.
+ auto result = mWrapper->getVibrator(kVibratorId);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_NE(nullptr, result.value().get());
+
+ auto vibrator = result.value();
+ // First getVibrator call fails.
+ ASSERT_FALSE(vibrator->init());
+ // First and second off() calls fail, reload IVibrator with getVibrator.
+ ASSERT_TRUE(vibrator->doWithRetry<void>(OFF_FN, "off").isFailed());
+ // Third call to off() worked after IVibrator reloaded.
+ ASSERT_TRUE(vibrator->doWithRetry<void>(OFF_FN, "off").isOk());
+}
+
+TEST_F(VibratorManagerHalWrapperAidlTest, TestPrepareSynced) {
+ EXPECT_CALL(*mMockHal.get(), getVibratorIds(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(kVibratorIds), Return(Status())));
+
+ EXPECT_CALL(*mMockHal.get(), getVibrator(_, _))
+ .Times(Exactly(2))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(mMockVibrator), Return(Status())));
+
+ EXPECT_CALL(*mMockHal.get(), prepareSynced(Eq(kVibratorIds)))
+ .Times(Exactly(3))
+ .WillOnce(
+ Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(Return(Status()));
+
+ ASSERT_TRUE(mWrapper->getVibratorIds().isOk());
+ ASSERT_TRUE(mWrapper->prepareSynced(kVibratorIds).isUnsupported());
+ ASSERT_TRUE(mWrapper->prepareSynced(kVibratorIds).isFailed());
+ ASSERT_TRUE(mWrapper->prepareSynced(kVibratorIds).isOk());
+}
+
+TEST_F(VibratorManagerHalWrapperAidlTest, TestTriggerSyncedWithCallbackSupport) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(IVibratorManager::CAP_TRIGGER_CALLBACK),
+ Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), triggerSynced(_))
+ .Times(Exactly(3))
+ .WillOnce(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(DoAll(TriggerCallback(), Return(Status())));
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->triggerSynced(callback).isUnsupported());
+ ASSERT_TRUE(mWrapper->triggerSynced(callback).isFailed());
+ ASSERT_TRUE(mWrapper->triggerSynced(callback).isOk());
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorManagerHalWrapperAidlTest, TestTriggerSyncedWithoutCallbackSupport) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<0>(IVibratorManager::CAP_SYNC), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), triggerSynced(Eq(nullptr)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status()));
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->triggerSynced(callback).isOk());
+ ASSERT_EQ(0, *callbackCounter.get());
+}
+
+TEST_F(VibratorManagerHalWrapperAidlTest, TestCancelSynced) {
+ EXPECT_CALL(*mMockHal.get(), cancelSynced())
+ .Times(Exactly(3))
+ .WillOnce(
+ Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(Return(Status()));
+
+ ASSERT_TRUE(mWrapper->cancelSynced().isUnsupported());
+ ASSERT_TRUE(mWrapper->cancelSynced().isFailed());
+ ASSERT_TRUE(mWrapper->cancelSynced().isOk());
+}
+
+TEST_F(VibratorManagerHalWrapperAidlTest, TestCancelSyncedReloadsAllControllers) {
+ EXPECT_CALL(*mMockHal.get(), getVibratorIds(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(kVibratorIds), Return(Status())));
+
+ EXPECT_CALL(*mMockHal.get(), getVibrator(_, _))
+ .Times(Exactly(2))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(mMockVibrator), Return(Status())));
+
+ EXPECT_CALL(*mMockHal.get(), cancelSynced()).Times(Exactly(1)).WillRepeatedly(Return(Status()));
+
+ ASSERT_TRUE(mWrapper->getVibratorIds().isOk());
+ ASSERT_TRUE(mWrapper->cancelSynced().isOk());
+}
diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp
new file mode 100644
index 0000000..0850ef3
--- /dev/null
+++ b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "VibratorManagerHalWrapperLegacyTest"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorManagerHalWrapper.h>
+
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::CompositePrimitive;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+
+using std::chrono::milliseconds;
+
+using namespace android;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockHalController : public vibrator::HalController {
+public:
+ MockHalController() = default;
+ virtual ~MockHalController() = default;
+
+ MOCK_METHOD(bool, init, (), (override));
+ MOCK_METHOD(void, tryReconnect, (), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorManagerHalWrapperLegacyTest : public Test {
+public:
+ void SetUp() override {
+ mMockController = std::make_shared<StrictMock<MockHalController>>();
+ mWrapper = std::make_unique<vibrator::LegacyManagerHalWrapper>(mMockController);
+ ASSERT_NE(mWrapper, nullptr);
+ }
+
+protected:
+ std::shared_ptr<StrictMock<MockHalController>> mMockController = nullptr;
+ std::unique_ptr<vibrator::ManagerHalWrapper> mWrapper = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorManagerHalWrapperLegacyTest, TestPing) {
+ EXPECT_CALL(*mMockController.get(), init()).Times(Exactly(1)).WillOnce(Return(false));
+
+ ASSERT_TRUE(mWrapper->ping().isUnsupported());
+}
+
+TEST_F(VibratorManagerHalWrapperLegacyTest, TestTryReconnect) {
+ EXPECT_CALL(*mMockController.get(), tryReconnect()).Times(Exactly(1));
+
+ mWrapper->tryReconnect();
+}
+
+TEST_F(VibratorManagerHalWrapperLegacyTest, TestGetCapabilities) {
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::ManagerCapabilities::NONE, result.value());
+}
+
+TEST_F(VibratorManagerHalWrapperLegacyTest, TestGetVibratorIds) {
+ std::vector<int> expectedIds = {0};
+
+ EXPECT_CALL(*mMockController.get(), init())
+ .Times(Exactly(2))
+ .WillOnce(Return(false))
+ .WillRepeatedly(Return(true));
+
+ auto result = mWrapper->getVibratorIds();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(std::vector<int32_t>(), result.value());
+
+ result = mWrapper->getVibratorIds();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(expectedIds, result.value());
+}
+
+TEST_F(VibratorManagerHalWrapperLegacyTest, TestGetVibratorWithValidIdReturnsController) {
+ EXPECT_CALL(*mMockController.get(), init())
+ .Times(Exactly(2))
+ .WillOnce(Return(false))
+ .WillRepeatedly(Return(true));
+
+ ASSERT_TRUE(mWrapper->getVibrator(0).isFailed());
+
+ auto result = mWrapper->getVibrator(0);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(mMockController.get(), result.value().get());
+}
+
+TEST_F(VibratorManagerHalWrapperLegacyTest, TestGetVibratorWithInvalidIdFails) {
+ ASSERT_TRUE(mWrapper->getVibrator(-1).isFailed());
+}
+
+TEST_F(VibratorManagerHalWrapperLegacyTest, TestSyncedOperationsUnsupported) {
+ std::vector<int32_t> vibratorIds;
+ vibratorIds.push_back(0);
+
+ ASSERT_TRUE(mWrapper->prepareSynced(vibratorIds).isUnsupported());
+ ASSERT_TRUE(mWrapper->triggerSynced([]() {}).isUnsupported());
+ ASSERT_TRUE(mWrapper->cancelSynced().isUnsupported());
+}
diff --git a/services/vibratorservice/test/test_utils.h b/services/vibratorservice/test/test_utils.h
new file mode 100644
index 0000000..1933a11
--- /dev/null
+++ b/services/vibratorservice/test/test_utils.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef VIBRATORSERVICE_UNITTEST_UTIL_H_
+#define VIBRATORSERVICE_UNITTEST_UTIL_H_
+
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <vibratorservice/VibratorHalWrapper.h>
+
+namespace android {
+
+namespace vibrator {
+
+using ::android::hardware::vibrator::ActivePwle;
+using ::android::hardware::vibrator::Braking;
+using ::android::hardware::vibrator::BrakingPwle;
+using ::android::hardware::vibrator::CompositeEffect;
+using ::android::hardware::vibrator::CompositePrimitive;
+using ::android::hardware::vibrator::PrimitivePwle;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockCallbackScheduler : public vibrator::CallbackScheduler {
+public:
+ MOCK_METHOD(void, schedule, (std::function<void()> callback, std::chrono::milliseconds delay),
+ (override));
+};
+
+ACTION(TriggerSchedulerCallback) {
+ arg0();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+class TestFactory {
+public:
+ static CompositeEffect createCompositeEffect(CompositePrimitive primitive,
+ std::chrono::milliseconds delay, float scale) {
+ CompositeEffect effect;
+ effect.primitive = primitive;
+ effect.delayMs = delay.count();
+ effect.scale = scale;
+ return effect;
+ }
+
+ static PrimitivePwle createActivePwle(float startAmplitude, float startFrequency,
+ float endAmplitude, float endFrequency,
+ std::chrono::milliseconds duration) {
+ ActivePwle pwle;
+ pwle.startAmplitude = startAmplitude;
+ pwle.endAmplitude = endAmplitude;
+ pwle.startFrequency = startFrequency;
+ pwle.endFrequency = endFrequency;
+ pwle.duration = duration.count();
+ return pwle;
+ }
+
+ static PrimitivePwle createBrakingPwle(Braking braking, std::chrono::milliseconds duration) {
+ BrakingPwle pwle;
+ pwle.braking = braking;
+ pwle.duration = duration.count();
+ return pwle;
+ }
+
+ static std::function<void()> createCountingCallback(int32_t* counter) {
+ return [counter]() { *counter += 1; };
+ }
+
+private:
+ TestFactory() = delete;
+ ~TestFactory() = delete;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+} // namespace vibrator
+
+} // namespace android
+
+#endif // VIBRATORSERVICE_UNITTEST_UTIL_H_
\ No newline at end of file
diff --git a/services/vr/bufferhubd/Android.bp b/services/vr/bufferhubd/Android.bp
index 8523bb2..f5491cf 100644
--- a/services/vr/bufferhubd/Android.bp
+++ b/services/vr/bufferhubd/Android.bp
@@ -18,8 +18,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/services/vr/hardware_composer/aidl/Android.bp b/services/vr/hardware_composer/aidl/Android.bp
index 98afdec..fa71ed7 100644
--- a/services/vr/hardware_composer/aidl/Android.bp
+++ b/services/vr/hardware_composer/aidl/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/vulkan/include/hardware/hwvulkan.h b/vulkan/include/hardware/hwvulkan.h
index 9e9a14d..98bc8e3 100644
--- a/vulkan/include/hardware/hwvulkan.h
+++ b/vulkan/include/hardware/hwvulkan.h
@@ -54,8 +54,9 @@
/* A hwvulkan_device_t corresponds to an ICD on other systems. Currently there
* can only be one on a system (HWVULKAN_DEVICE_0). It is opened once per
* process when the Vulkan API is first used; the hw_device_t::close() function
- * is never called. Any non-trivial resource allocation should be done when
- * the VkInstance is created rather than when the hwvulkan_device_t is opened.
+ * is called upon driver unloading. Any non-trivial resource allocation should
+ * be done when the VkInstance is created rather than when the hwvulkan_device_t
+ * is opened.
*/
typedef struct hwvulkan_device_t {
struct hw_device_t common;
diff --git a/vulkan/include/vulkan/vk_android_native_buffer.h b/vulkan/include/vulkan/vk_android_native_buffer.h
index 9ffe83b..ba98696 100644
--- a/vulkan/include/vulkan/vk_android_native_buffer.h
+++ b/vulkan/include/vulkan/vk_android_native_buffer.h
@@ -27,17 +27,19 @@
#define VK_ANDROID_native_buffer 1
#define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NUMBER 11
-/* NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 6
+/*
+ * NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 6
*
* This version of the extension transitions from gralloc0 to gralloc1 usage
* flags (int -> 2x uint64_t). The WSI implementation will temporarily continue
* to fill out deprecated fields in VkNativeBufferANDROID, and will call the
* deprecated vkGetSwapchainGrallocUsageANDROID if the new
* vkGetSwapchainGrallocUsage2ANDROID is not supported. This transitionary
- * backwards-compatibility support is temporary, and will likely be removed in
+ * backwards-compatibility support is temporary, and will likely be removed
* (along with all gralloc0 support) in a future release.
*/
-/* NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 8
+/*
+ * NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 8
*
* This version of the extension doesn't introduce new types or structs, but is
* to accommodate the new struct VkBindImageMemorySwapchainInfoKHR added in
@@ -47,97 +49,155 @@
* in VkBindImageMemorySwapchainInfoKHR will be additionally chained to the
* pNext chain of VkBindImageMemoryInfo and passed down to the driver.
*/
-#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 8
-#define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME "VK_ANDROID_native_buffer"
+#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 8
+#define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME "VK_ANDROID_native_buffer"
-#define VK_ANDROID_NATIVE_BUFFER_ENUM(type,id) ((type)(1000000000 + (1000 * (VK_ANDROID_NATIVE_BUFFER_EXTENSION_NUMBER - 1)) + (id)))
-#define VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 0)
-#define VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 1)
-#define VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 2)
+#define VK_ANDROID_NATIVE_BUFFER_ENUM(type, id) \
+ ((type)(1000000000 + \
+ (1000 * (VK_ANDROID_NATIVE_BUFFER_EXTENSION_NUMBER - 1)) + (id)))
+#define VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID \
+ VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 0)
+#define VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID \
+ VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 1)
+#define VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID \
+ VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 2)
+/* clang-format off */
typedef enum VkSwapchainImageUsageFlagBitsANDROID {
VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID = 0x00000001,
VK_SWAPCHAIN_IMAGE_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
} VkSwapchainImageUsageFlagBitsANDROID;
typedef VkFlags VkSwapchainImageUsageFlagsANDROID;
+/*
+ * struct VkNativeBufferUsage2ANDROID
+ *
+ * consumer: gralloc1 consumer usage flag
+ * producer: gralloc1 producer usage flag
+ */
typedef struct {
- uint64_t consumer;
- uint64_t producer;
+ uint64_t consumer;
+ uint64_t producer;
} VkNativeBufferUsage2ANDROID;
+/*
+ * struct VkNativeBufferANDROID
+ *
+ * sType: VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID
+ * pNext: NULL or a pointer to a structure extending this structure
+ * handle: buffer handle returned from gralloc alloc()
+ * stride: stride returned from gralloc alloc()
+ * format: gralloc format requested when the buffer was allocated
+ * usage: gralloc usage requested when the buffer was allocated
+ * usage2: gralloc usage requested when the buffer was allocated
+ */
typedef struct {
- VkStructureType sType; // must be VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID
- const void* pNext;
-
- // Buffer handle and stride returned from gralloc alloc()
- buffer_handle_t handle;
- int stride;
-
- // Gralloc format and usage requested when the buffer was allocated.
- int format;
- int usage; // DEPRECATED in SPEC_VERSION 6
- // -- Added in SPEC_VERSION 6 --
- VkNativeBufferUsage2ANDROID usage2;
+ VkStructureType sType;
+ const void* pNext;
+ buffer_handle_t handle;
+ int stride;
+ int format;
+ int usage; /* DEPRECATED in SPEC_VERSION 6 */
+ VkNativeBufferUsage2ANDROID usage2; /* ADDED in SPEC_VERSION 6 */
} VkNativeBufferANDROID;
+/*
+ * struct VkSwapchainImageCreateInfoANDROID
+ *
+ * sType: VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID
+ * pNext: NULL or a pointer to a structure extending this structure
+ * usage: is a bitmask of VkSwapchainImageUsageFlagsANDROID
+ */
typedef struct {
- VkStructureType sType; // must be VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID
- const void* pNext;
-
- VkSwapchainImageUsageFlagsANDROID usage;
+ VkStructureType sType;
+ const void* pNext;
+ VkSwapchainImageUsageFlagsANDROID usage;
} VkSwapchainImageCreateInfoANDROID;
+/*
+ * struct VkPhysicalDevicePresentationPropertiesANDROID
+ *
+ * sType: VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID
+ * pNext: NULL or a pointer to a structure extending this structure
+ * sharedImage: specifies if the image can be shared with the display system
+ */
typedef struct {
- VkStructureType sType; // must be VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID
- const void* pNext;
-
- VkBool32 sharedImage;
+ VkStructureType sType;
+ const void* pNext;
+ VkBool32 sharedImage;
} VkPhysicalDevicePresentationPropertiesANDROID;
-// -- DEPRECATED in SPEC_VERSION 6 --
-typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsageANDROID)(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage);
-// -- ADDED in SPEC_VERSION 6 --
-typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage2ANDROID)(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage);
-typedef VkResult (VKAPI_PTR *PFN_vkAcquireImageANDROID)(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence);
-typedef VkResult (VKAPI_PTR *PFN_vkQueueSignalReleaseImageANDROID)(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd);
+/* DEPRECATED in SPEC_VERSION 6 */
+typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsageANDROID)(
+ VkDevice device,
+ VkFormat format,
+ VkImageUsageFlags imageUsage,
+ int* grallocUsage);
+
+/* ADDED in SPEC_VERSION 6 */
+typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage2ANDROID)(
+ VkDevice device,
+ VkFormat format,
+ VkImageUsageFlags imageUsage,
+ VkSwapchainImageUsageFlagsANDROID swapchainImageUsage,
+ uint64_t* grallocConsumerUsage,
+ uint64_t* grallocProducerUsage);
+
+typedef VkResult (VKAPI_PTR *PFN_vkAcquireImageANDROID)(
+ VkDevice device,
+ VkImage image,
+ int nativeFenceFd,
+ VkSemaphore semaphore,
+ VkFence fence);
+
+typedef VkResult (VKAPI_PTR *PFN_vkQueueSignalReleaseImageANDROID)(
+ VkQueue queue,
+ uint32_t waitSemaphoreCount,
+ const VkSemaphore* pWaitSemaphores,
+ VkImage image,
+ int* pNativeFenceFd);
#ifndef VK_NO_PROTOTYPES
-// -- DEPRECATED in SPEC_VERSION 6 --
+/* DEPRECATED in SPEC_VERSION 6 */
VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsageANDROID(
- VkDevice device,
- VkFormat format,
- VkImageUsageFlags imageUsage,
- int* grallocUsage
+ VkDevice device,
+ VkFormat format,
+ VkImageUsageFlags imageUsage,
+ int* grallocUsage
);
-// -- ADDED in SPEC_VERSION 6 --
+
+/* ADDED in SPEC_VERSION 6 */
VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage2ANDROID(
- VkDevice device,
- VkFormat format,
- VkImageUsageFlags imageUsage,
+ VkDevice device,
+ VkFormat format,
+ VkImageUsageFlags imageUsage,
VkSwapchainImageUsageFlagsANDROID swapchainImageUsage,
- uint64_t* grallocConsumerUsage,
- uint64_t* grallocProducerUsage
+ uint64_t* grallocConsumerUsage,
+ uint64_t* grallocProducerUsage
);
+
VKAPI_ATTR VkResult VKAPI_CALL vkAcquireImageANDROID(
- VkDevice device,
- VkImage image,
- int nativeFenceFd,
- VkSemaphore semaphore,
- VkFence fence
+ VkDevice device,
+ VkImage image,
+ int nativeFenceFd,
+ VkSemaphore semaphore,
+ VkFence fence
);
+
VKAPI_ATTR VkResult VKAPI_CALL vkQueueSignalReleaseImageANDROID(
- VkQueue queue,
- uint32_t waitSemaphoreCount,
- const VkSemaphore* pWaitSemaphores,
- VkImage image,
- int* pNativeFenceFd
+ VkQueue queue,
+ uint32_t waitSemaphoreCount,
+ const VkSemaphore* pWaitSemaphores,
+ VkImage image,
+ int* pNativeFenceFd
);
+
#endif
+/* clang-format on */
#ifdef __cplusplus
}
#endif
-#endif // __VK_ANDROID_NATIVE_BUFFER_H__
+#endif /* __VK_ANDROID_NATIVE_BUFFER_H__ */
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index 9aaac5f..d1cd397 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -1184,23 +1184,18 @@
// ----------------------------------------------------------------------------
bool EnsureInitialized() {
- static std::once_flag once_flag;
- static bool initialized;
+ static bool initialized = false;
+ static pid_t init_attempted_for_pid = 0;
+ static std::mutex init_lock;
- std::call_once(once_flag, []() {
- if (driver::OpenHAL()) {
- initialized = true;
- }
- });
+ std::lock_guard<std::mutex> lock(init_lock);
+ if (init_attempted_for_pid == getpid())
+ return initialized;
- {
- static pid_t pid = getpid() + 1;
- static std::mutex layer_lock;
- std::lock_guard<std::mutex> lock(layer_lock);
- if (pid != getpid()) {
- pid = getpid();
- DiscoverLayers();
- }
+ init_attempted_for_pid = getpid();
+ if (driver::OpenHAL()) {
+ DiscoverLayers();
+ initialized = true;
}
return initialized;
@@ -1266,7 +1261,7 @@
ATRACE_CALL();
if (!EnsureInitialized())
- return VK_ERROR_INITIALIZATION_FAILED;
+ return VK_ERROR_OUT_OF_HOST_MEMORY;
uint32_t count = GetLayerCount();
@@ -1290,7 +1285,7 @@
ATRACE_CALL();
if (!EnsureInitialized())
- return VK_ERROR_INITIALIZATION_FAILED;
+ return VK_ERROR_OUT_OF_HOST_MEMORY;
if (pLayerName) {
const Layer* layer = FindLayer(pLayerName);
@@ -1466,6 +1461,11 @@
VkResult EnumerateInstanceVersion(uint32_t* pApiVersion) {
ATRACE_CALL();
+ // Load the driver here if not done yet. This api will be used in Zygote
+ // for Vulkan driver pre-loading because of the minimum overhead.
+ if (!EnsureInitialized())
+ return VK_ERROR_OUT_OF_HOST_MEMORY;
+
*pApiVersion = VK_API_VERSION_1_1;
return VK_SUCCESS;
}
diff --git a/vulkan/libvulkan/api.h b/vulkan/libvulkan/api.h
index 416cba0..2a215d7 100644
--- a/vulkan/libvulkan/api.h
+++ b/vulkan/libvulkan/api.h
@@ -24,17 +24,34 @@
namespace vulkan {
namespace api {
-// clang-format off
-VKAPI_ATTR VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance);
-VKAPI_ATTR void DestroyInstance(VkInstance instance, const VkAllocationCallbacks* pAllocator);
-VKAPI_ATTR VkResult CreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice);
-VKAPI_ATTR void DestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator);
-VKAPI_ATTR VkResult EnumerateInstanceLayerProperties(uint32_t* pPropertyCount, VkLayerProperties* pProperties);
-VKAPI_ATTR VkResult EnumerateInstanceExtensionProperties(const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties);
-VKAPI_ATTR VkResult EnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkLayerProperties* pProperties);
-VKAPI_ATTR VkResult EnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties);
+VKAPI_ATTR VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo,
+ const VkAllocationCallbacks* pAllocator,
+ VkInstance* pInstance);
+VKAPI_ATTR void DestroyInstance(VkInstance instance,
+ const VkAllocationCallbacks* pAllocator);
+VKAPI_ATTR VkResult CreateDevice(VkPhysicalDevice physicalDevice,
+ const VkDeviceCreateInfo* pCreateInfo,
+ const VkAllocationCallbacks* pAllocator,
+ VkDevice* pDevice);
+VKAPI_ATTR void DestroyDevice(VkDevice device,
+ const VkAllocationCallbacks* pAllocator);
+VKAPI_ATTR VkResult
+EnumerateInstanceLayerProperties(uint32_t* pPropertyCount,
+ VkLayerProperties* pProperties);
+VKAPI_ATTR VkResult
+EnumerateInstanceExtensionProperties(const char* pLayerName,
+ uint32_t* pPropertyCount,
+ VkExtensionProperties* pProperties);
+VKAPI_ATTR VkResult
+EnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice,
+ uint32_t* pPropertyCount,
+ VkLayerProperties* pProperties);
+VKAPI_ATTR VkResult
+EnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice,
+ const char* pLayerName,
+ uint32_t* pPropertyCount,
+ VkExtensionProperties* pProperties);
VKAPI_ATTR VkResult EnumerateInstanceVersion(uint32_t* pApiVersion);
-// clang-format on
inline InstanceData& GetData(VkInstance instance) {
return driver::GetData(instance).opaque_api_data;
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 5d47172..d7fdab5 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -81,6 +81,8 @@
Hal(const Hal&) = delete;
Hal& operator=(const Hal&) = delete;
+ bool ShouldUnloadBuiltinDriver();
+ void UnloadBuiltinDriver();
bool InitDebugReportIndex();
static Hal hal_;
@@ -92,15 +94,15 @@
class CreateInfoWrapper {
public:
CreateInfoWrapper(const VkInstanceCreateInfo& create_info,
+ uint32_t icd_api_version,
const VkAllocationCallbacks& allocator);
CreateInfoWrapper(VkPhysicalDevice physical_dev,
const VkDeviceCreateInfo& create_info,
+ uint32_t icd_api_version,
const VkAllocationCallbacks& allocator);
~CreateInfoWrapper();
VkResult Validate();
- void DowngradeApiVersion();
- void UpgradeDeviceCoreApiVersion(uint32_t api_version);
const std::bitset<ProcHook::EXTENSION_COUNT>& GetHookExtensions() const;
const std::bitset<ProcHook::EXTENSION_COUNT>& GetHalExtensions() const;
@@ -115,10 +117,12 @@
const char** names;
uint32_t name_count;
+ ExtensionFilter()
+ : exts(nullptr), ext_count(0), names(nullptr), name_count(0) {}
};
+ VkResult SanitizeApiVersion();
VkResult SanitizePNext();
-
VkResult SanitizeLayers();
VkResult SanitizeExtensions();
@@ -130,6 +134,8 @@
const bool is_instance_;
const VkAllocationCallbacks& allocator_;
+ const uint32_t loader_api_version_;
+ const uint32_t icd_api_version_;
VkPhysicalDevice physical_dev_;
@@ -149,8 +155,8 @@
Hal Hal::hal_;
const std::array<const char*, 2> HAL_SUBNAME_KEY_PROPERTIES = {{
- "ro.hardware." HWVULKAN_HARDWARE_MODULE_ID,
- "ro.board.platform"
+ "ro.hardware.vulkan",
+ "ro.board.platform",
}};
constexpr int LIB_DL_FLAGS = RTLD_LOCAL | RTLD_NOW;
@@ -177,6 +183,8 @@
.library_namespace = library_namespace,
};
so = android_dlopen_ext(lib_name.c_str(), LIB_DL_FLAGS, &dlextinfo);
+ ALOGE("Could not load %s from updatable gfx driver namespace: %s.",
+ lib_name.c_str(), dlerror());
} else {
// load built-in driver
so = android_load_sphal_library(lib_name.c_str(), LIB_DL_FLAGS);
@@ -184,9 +192,8 @@
if (so)
break;
}
- if (!so) {
+ if (!so)
return -ENOENT;
- }
auto hmi = static_cast<hw_module_t*>(dlsym(so, HAL_MODULE_INFO_SYM_AS_STR));
if (!hmi) {
@@ -231,9 +238,15 @@
bool Hal::Open() {
ATRACE_CALL();
+
const nsecs_t openTime = systemTime();
- ALOG_ASSERT(!hal_.dev_, "OpenHAL called more than once");
+ if (hal_.ShouldUnloadBuiltinDriver()) {
+ hal_.UnloadBuiltinDriver();
+ }
+
+ if (hal_.dev_)
+ return true;
// Use a stub device unless we successfully open a real HAL device.
hal_.dev_ = &stubhal::kDevice;
@@ -248,16 +261,16 @@
if (result != 0) {
android::GraphicsEnv::getInstance().setDriverLoaded(
android::GpuStatsInfo::Api::API_VK, false, systemTime() - openTime);
+ ALOGV("unable to load Vulkan HAL, using stub HAL (result=%d)", result);
return true;
}
+
hwvulkan_device_t* device;
ATRACE_BEGIN("hwvulkan module open");
result =
module->common.methods->open(&module->common, HWVULKAN_DEVICE_0,
reinterpret_cast<hw_device_t**>(&device));
-
-
ATRACE_END();
if (result != 0) {
android::GraphicsEnv::getInstance().setDriverLoaded(
@@ -278,6 +291,38 @@
return true;
}
+bool Hal::ShouldUnloadBuiltinDriver() {
+ // Should not unload since the driver was not loaded
+ if (!hal_.dev_)
+ return false;
+
+ // Should not unload if stubhal is used on the device
+ if (hal_.dev_ == &stubhal::kDevice)
+ return false;
+
+ // Unload the driver if updated driver is chosen
+ if (android::GraphicsEnv::getInstance().getDriverNamespace())
+ return true;
+
+ return false;
+}
+
+void Hal::UnloadBuiltinDriver() {
+ ATRACE_CALL();
+
+ ALOGD("Unload builtin Vulkan driver.");
+
+ // Close the opened device
+ ALOG_ASSERT(!hal_.dev_->common.close(hal_.dev_->common),
+ "hw_device_t::close() failed.");
+
+ // Close the opened shared library in the hw_module_t
+ android_unload_sphal_library(hal_.dev_->common.module->dso);
+
+ hal_.dev_ = nullptr;
+ hal_.debug_report_index_ = -1;
+}
+
bool Hal::InitDebugReportIndex() {
ATRACE_CALL();
@@ -316,32 +361,27 @@
}
CreateInfoWrapper::CreateInfoWrapper(const VkInstanceCreateInfo& create_info,
+ uint32_t icd_api_version,
const VkAllocationCallbacks& allocator)
: is_instance_(true),
allocator_(allocator),
+ loader_api_version_(VK_API_VERSION_1_1),
+ icd_api_version_(icd_api_version),
physical_dev_(VK_NULL_HANDLE),
instance_info_(create_info),
- extension_filter_() {
- // instance core versions need to match the loader api version
- for (uint32_t i = ProcHook::EXTENSION_CORE_1_0;
- i != ProcHook::EXTENSION_COUNT; ++i) {
- hook_extensions_.set(i);
- hal_extensions_.set(i);
- }
-}
+ extension_filter_() {}
CreateInfoWrapper::CreateInfoWrapper(VkPhysicalDevice physical_dev,
const VkDeviceCreateInfo& create_info,
+ uint32_t icd_api_version,
const VkAllocationCallbacks& allocator)
: is_instance_(false),
allocator_(allocator),
+ loader_api_version_(VK_API_VERSION_1_1),
+ icd_api_version_(icd_api_version),
physical_dev_(physical_dev),
dev_info_(create_info),
- extension_filter_() {
- // initialize with baseline core API version
- hook_extensions_.set(ProcHook::EXTENSION_CORE_1_0);
- hal_extensions_.set(ProcHook::EXTENSION_CORE_1_0);
-}
+ extension_filter_() {}
CreateInfoWrapper::~CreateInfoWrapper() {
allocator_.pfnFree(allocator_.pUserData, extension_filter_.exts);
@@ -349,7 +389,9 @@
}
VkResult CreateInfoWrapper::Validate() {
- VkResult result = SanitizePNext();
+ VkResult result = SanitizeApiVersion();
+ if (result == VK_SUCCESS)
+ result = SanitizePNext();
if (result == VK_SUCCESS)
result = SanitizeLayers();
if (result == VK_SUCCESS)
@@ -376,6 +418,22 @@
return &dev_info_;
}
+VkResult CreateInfoWrapper::SanitizeApiVersion() {
+ if (!is_instance_ || !instance_info_.pApplicationInfo)
+ return VK_SUCCESS;
+
+ if (icd_api_version_ > VK_API_VERSION_1_0 ||
+ instance_info_.pApplicationInfo->apiVersion < VK_API_VERSION_1_1)
+ return VK_SUCCESS;
+
+ // override apiVersion to avoid error return from 1.0 icd
+ application_info_ = *instance_info_.pApplicationInfo;
+ application_info_.apiVersion = VK_API_VERSION_1_0;
+ instance_info_.pApplicationInfo = &application_info_;
+
+ return VK_SUCCESS;
+}
+
VkResult CreateInfoWrapper::SanitizePNext() {
const struct StructHeader {
VkStructureType type;
@@ -423,15 +481,33 @@
: dev_info_.ppEnabledExtensionNames;
auto& ext_count = (is_instance_) ? instance_info_.enabledExtensionCount
: dev_info_.enabledExtensionCount;
- if (!ext_count)
- return VK_SUCCESS;
VkResult result = InitExtensionFilter();
if (result != VK_SUCCESS)
return result;
- for (uint32_t i = 0; i < ext_count; i++)
- FilterExtension(ext_names[i]);
+ if (is_instance_ && icd_api_version_ < loader_api_version_) {
+ for (uint32_t i = 0; i < ext_count; i++) {
+ // Upon api downgrade, skip the promoted instance extensions in the
+ // first pass to avoid duplicate extensions.
+ const std::optional<uint32_t> version =
+ GetInstanceExtensionPromotedVersion(ext_names[i]);
+ if (version && *version > icd_api_version_ &&
+ *version <= loader_api_version_)
+ continue;
+
+ FilterExtension(ext_names[i]);
+ }
+
+ // Enable the required extensions to support core functionalities.
+ const auto promoted_extensions = GetPromotedInstanceExtensions(
+ icd_api_version_, loader_api_version_);
+ for (const auto& promoted_extension : promoted_extensions)
+ FilterExtension(promoted_extension);
+ } else {
+ for (uint32_t i = 0; i < ext_count; i++)
+ FilterExtension(ext_names[i]);
+ }
// Enable device extensions that contain physical-device commands, so that
// vkGetInstanceProcAddr will return those physical-device commands.
@@ -439,6 +515,23 @@
hook_extensions_.set(ProcHook::KHR_swapchain);
}
+ const uint32_t api_version =
+ is_instance_ ? loader_api_version_
+ : std::min(icd_api_version_, loader_api_version_);
+ switch (api_version) {
+ case VK_API_VERSION_1_1:
+ hook_extensions_.set(ProcHook::EXTENSION_CORE_1_1);
+ hal_extensions_.set(ProcHook::EXTENSION_CORE_1_1);
+ [[clang::fallthrough]];
+ case VK_API_VERSION_1_0:
+ hook_extensions_.set(ProcHook::EXTENSION_CORE_1_0);
+ hal_extensions_.set(ProcHook::EXTENSION_CORE_1_0);
+ break;
+ default:
+ ALOGE("Unknown API version[%u]", api_version);
+ break;
+ }
+
ext_names = extension_filter_.names;
ext_count = extension_filter_.name_count;
@@ -496,10 +589,24 @@
filter.ext_count = count;
// allocate name array
- uint32_t enabled_ext_count = (is_instance_)
- ? instance_info_.enabledExtensionCount
- : dev_info_.enabledExtensionCount;
- count = std::min(filter.ext_count, enabled_ext_count);
+ if (is_instance_) {
+ uint32_t enabled_ext_count = instance_info_.enabledExtensionCount;
+
+ // It requires enabling additional promoted extensions to downgrade api,
+ // so we reserve enough space here.
+ if (icd_api_version_ < loader_api_version_) {
+ enabled_ext_count += CountPromotedInstanceExtensions(
+ icd_api_version_, loader_api_version_);
+ }
+
+ count = std::min(filter.ext_count, enabled_ext_count);
+ } else {
+ count = std::min(filter.ext_count, dev_info_.enabledExtensionCount);
+ }
+
+ if (!count)
+ return VK_SUCCESS;
+
filter.names = reinterpret_cast<const char**>(allocator_.pfnAllocation(
allocator_.pUserData, sizeof(const char*) * count, alignof(const char*),
VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
@@ -527,6 +634,10 @@
hook_extensions_.set(ext_bit);
break;
case ProcHook::KHR_get_physical_device_properties2:
+ case ProcHook::KHR_device_group_creation:
+ case ProcHook::KHR_external_memory_capabilities:
+ case ProcHook::KHR_external_semaphore_capabilities:
+ case ProcHook::KHR_external_fence_capabilities:
case ProcHook::EXTENSION_UNKNOWN:
// Extensions we don't need to do anything about at this level
break;
@@ -584,6 +695,10 @@
case ProcHook::KHR_android_surface:
case ProcHook::KHR_get_physical_device_properties2:
+ case ProcHook::KHR_device_group_creation:
+ case ProcHook::KHR_external_memory_capabilities:
+ case ProcHook::KHR_external_semaphore_capabilities:
+ case ProcHook::KHR_external_fence_capabilities:
case ProcHook::KHR_get_surface_capabilities2:
case ProcHook::KHR_surface:
case ProcHook::EXT_debug_report:
@@ -630,40 +745,6 @@
}
}
-void CreateInfoWrapper::DowngradeApiVersion() {
- // If pApplicationInfo is NULL, apiVersion is assumed to be 1.0:
- if (instance_info_.pApplicationInfo) {
- application_info_ = *instance_info_.pApplicationInfo;
- instance_info_.pApplicationInfo = &application_info_;
- application_info_.apiVersion = VK_API_VERSION_1_0;
- }
-}
-
-void CreateInfoWrapper::UpgradeDeviceCoreApiVersion(uint32_t api_version) {
- ALOG_ASSERT(!is_instance_, "Device only API called by instance wrapper.");
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wold-style-cast"
- api_version ^= VK_VERSION_PATCH(api_version);
-#pragma clang diagnostic pop
-
- // cap the API version to the loader supported highest version
- if (api_version > VK_API_VERSION_1_1)
- api_version = VK_API_VERSION_1_1;
-
- switch (api_version) {
- case VK_API_VERSION_1_1:
- hook_extensions_.set(ProcHook::EXTENSION_CORE_1_1);
- hal_extensions_.set(ProcHook::EXTENSION_CORE_1_1);
- [[clang::fallthrough]];
- case VK_API_VERSION_1_0:
- break;
- default:
- ALOGD("Unknown upgrade API version[%u]", api_version);
- break;
- }
-}
-
VKAPI_ATTR void* DefaultAllocate(void*,
size_t size,
size_t alignment,
@@ -895,21 +976,14 @@
return result;
}
-bool QueryPresentationProperties(
+void QueryPresentationProperties(
VkPhysicalDevice physicalDevice,
- VkPhysicalDevicePresentationPropertiesANDROID *presentation_properties) {
- const InstanceData& data = GetData(physicalDevice);
-
- // GPDP2 must be present and enabled on the instance.
- if (!data.driver.GetPhysicalDeviceProperties2KHR &&
- !data.driver.GetPhysicalDeviceProperties2)
- return false;
-
+ VkPhysicalDevicePresentationPropertiesANDROID* presentation_properties) {
// Request the android-specific presentation properties via GPDP2
- VkPhysicalDeviceProperties2KHR properties = {
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
+ VkPhysicalDeviceProperties2 properties = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
presentation_properties,
- {}
+ {},
};
#pragma clang diagnostic push
@@ -920,14 +994,7 @@
presentation_properties->pNext = nullptr;
presentation_properties->sharedImage = VK_FALSE;
- if (data.driver.GetPhysicalDeviceProperties2KHR) {
- data.driver.GetPhysicalDeviceProperties2KHR(physicalDevice,
- &properties);
- } else {
- data.driver.GetPhysicalDeviceProperties2(physicalDevice, &properties);
- }
-
- return true;
+ GetPhysicalDeviceProperties2(physicalDevice, &properties);
}
VkResult EnumerateDeviceExtensionProperties(
@@ -949,8 +1016,8 @@
}
VkPhysicalDevicePresentationPropertiesANDROID presentation_properties;
- if (QueryPresentationProperties(physicalDevice, &presentation_properties) &&
- presentation_properties.sharedImage) {
+ QueryPresentationProperties(physicalDevice, &presentation_properties);
+ if (presentation_properties.sharedImage) {
loader_extensions.push_back({
VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME,
VK_KHR_SHARED_PRESENTABLE_IMAGE_SPEC_VERSION});
@@ -1019,49 +1086,32 @@
const VkAllocationCallbacks& data_allocator =
(pAllocator) ? *pAllocator : GetDefaultAllocator();
- CreateInfoWrapper wrapper(*pCreateInfo, data_allocator);
- VkResult result = wrapper.Validate();
- if (result != VK_SUCCESS)
- return result;
-
- ATRACE_BEGIN("AllocateInstanceData");
- InstanceData* data = AllocateInstanceData(data_allocator);
- ATRACE_END();
- if (!data)
- return VK_ERROR_OUT_OF_HOST_MEMORY;
-
- data->hook_extensions |= wrapper.GetHookExtensions();
-
- ATRACE_BEGIN("autoDowngradeApiVersion");
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wold-style-cast"
- uint32_t api_version = ((pCreateInfo->pApplicationInfo)
- ? pCreateInfo->pApplicationInfo->apiVersion
- : VK_API_VERSION_1_0);
- uint32_t api_major_version = VK_VERSION_MAJOR(api_version);
- uint32_t api_minor_version = VK_VERSION_MINOR(api_version);
- uint32_t icd_api_version;
+ VkResult result = VK_SUCCESS;
+ uint32_t icd_api_version = VK_API_VERSION_1_0;
PFN_vkEnumerateInstanceVersion pfn_enumerate_instance_version =
reinterpret_cast<PFN_vkEnumerateInstanceVersion>(
Hal::Device().GetInstanceProcAddr(nullptr,
"vkEnumerateInstanceVersion"));
- if (!pfn_enumerate_instance_version) {
- icd_api_version = VK_API_VERSION_1_0;
- } else {
+ if (pfn_enumerate_instance_version) {
ATRACE_BEGIN("pfn_enumerate_instance_version");
result = (*pfn_enumerate_instance_version)(&icd_api_version);
ATRACE_END();
- }
- uint32_t icd_api_major_version = VK_VERSION_MAJOR(icd_api_version);
- uint32_t icd_api_minor_version = VK_VERSION_MINOR(icd_api_version);
+ if (result != VK_SUCCESS)
+ return result;
- if ((icd_api_major_version == 1) && (icd_api_minor_version == 0) &&
- ((api_major_version > 1) || (api_minor_version > 0))) {
- api_version = VK_API_VERSION_1_0;
- wrapper.DowngradeApiVersion();
+ icd_api_version ^= VK_VERSION_PATCH(icd_api_version);
}
-#pragma clang diagnostic pop
- ATRACE_END();
+
+ CreateInfoWrapper wrapper(*pCreateInfo, icd_api_version, data_allocator);
+ result = wrapper.Validate();
+ if (result != VK_SUCCESS)
+ return result;
+
+ InstanceData* data = AllocateInstanceData(data_allocator);
+ if (!data)
+ return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+ data->hook_extensions |= wrapper.GetHookExtensions();
// call into the driver
VkInstance instance;
@@ -1125,7 +1175,16 @@
const VkAllocationCallbacks& data_allocator =
(pAllocator) ? *pAllocator : instance_data.allocator;
- CreateInfoWrapper wrapper(physicalDevice, *pCreateInfo, data_allocator);
+ VkPhysicalDeviceProperties properties;
+ ATRACE_BEGIN("driver.GetPhysicalDeviceProperties");
+ instance_data.driver.GetPhysicalDeviceProperties(physicalDevice,
+ &properties);
+ ATRACE_END();
+
+ CreateInfoWrapper wrapper(
+ physicalDevice, *pCreateInfo,
+ properties.apiVersion ^ VK_VERSION_PATCH(properties.apiVersion),
+ data_allocator);
VkResult result = wrapper.Validate();
if (result != VK_SUCCESS)
return result;
@@ -1137,13 +1196,6 @@
if (!data)
return VK_ERROR_OUT_OF_HOST_MEMORY;
- VkPhysicalDeviceProperties properties;
- ATRACE_BEGIN("driver.GetPhysicalDeviceProperties");
- instance_data.driver.GetPhysicalDeviceProperties(physicalDevice,
- &properties);
- ATRACE_END();
-
- wrapper.UpgradeDeviceCoreApiVersion(properties.apiVersion);
data->hook_extensions |= wrapper.GetHookExtensions();
// call into the driver
@@ -1240,7 +1292,8 @@
VkResult result = VK_SUCCESS;
const auto& data = GetData(instance);
- if (!data.driver.EnumeratePhysicalDeviceGroups) {
+ if (!data.driver.EnumeratePhysicalDeviceGroups &&
+ !data.driver.EnumeratePhysicalDeviceGroupsKHR) {
uint32_t device_count = 0;
result = EnumeratePhysicalDevices(instance, &device_count, nullptr);
if (result < 0)
@@ -1272,9 +1325,15 @@
pPhysicalDeviceGroupProperties[i].subsetAllocation = 0;
}
} else {
- result = data.driver.EnumeratePhysicalDeviceGroups(
- instance, pPhysicalDeviceGroupCount,
- pPhysicalDeviceGroupProperties);
+ if (data.driver.EnumeratePhysicalDeviceGroups) {
+ result = data.driver.EnumeratePhysicalDeviceGroups(
+ instance, pPhysicalDeviceGroupCount,
+ pPhysicalDeviceGroupProperties);
+ } else {
+ result = data.driver.EnumeratePhysicalDeviceGroupsKHR(
+ instance, pPhysicalDeviceGroupCount,
+ pPhysicalDeviceGroupProperties);
+ }
if ((result == VK_SUCCESS || result == VK_INCOMPLETE) &&
*pPhysicalDeviceGroupCount && pPhysicalDeviceGroupProperties) {
for (uint32_t i = 0; i < *pPhysicalDeviceGroupCount; i++) {
@@ -1315,10 +1374,10 @@
if (*pQueue != VK_NULL_HANDLE) SetData(*pQueue, data);
}
-VKAPI_ATTR VkResult
-AllocateCommandBuffers(VkDevice device,
- const VkCommandBufferAllocateInfo* pAllocateInfo,
- VkCommandBuffer* pCommandBuffers) {
+VkResult AllocateCommandBuffers(
+ VkDevice device,
+ const VkCommandBufferAllocateInfo* pAllocateInfo,
+ VkCommandBuffer* pCommandBuffers) {
ATRACE_CALL();
const auto& data = GetData(device);
@@ -1333,10 +1392,10 @@
return result;
}
-VKAPI_ATTR VkResult QueueSubmit(VkQueue queue,
- uint32_t submitCount,
- const VkSubmitInfo* pSubmits,
- VkFence fence) {
+VkResult QueueSubmit(VkQueue queue,
+ uint32_t submitCount,
+ const VkSubmitInfo* pSubmits,
+ VkFence fence) {
ATRACE_CALL();
const auto& data = GetData(queue);
@@ -1344,5 +1403,198 @@
return data.driver.QueueSubmit(queue, submitCount, pSubmits, fence);
}
+void GetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice,
+ VkPhysicalDeviceFeatures2* pFeatures) {
+ ATRACE_CALL();
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceFeatures2) {
+ driver.GetPhysicalDeviceFeatures2(physicalDevice, pFeatures);
+ return;
+ }
+
+ driver.GetPhysicalDeviceFeatures2KHR(physicalDevice, pFeatures);
+}
+
+void GetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice,
+ VkPhysicalDeviceProperties2* pProperties) {
+ ATRACE_CALL();
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceProperties2) {
+ driver.GetPhysicalDeviceProperties2(physicalDevice, pProperties);
+ return;
+ }
+
+ driver.GetPhysicalDeviceProperties2KHR(physicalDevice, pProperties);
+}
+
+void GetPhysicalDeviceFormatProperties2(
+ VkPhysicalDevice physicalDevice,
+ VkFormat format,
+ VkFormatProperties2* pFormatProperties) {
+ ATRACE_CALL();
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceFormatProperties2) {
+ driver.GetPhysicalDeviceFormatProperties2(physicalDevice, format,
+ pFormatProperties);
+ return;
+ }
+
+ driver.GetPhysicalDeviceFormatProperties2KHR(physicalDevice, format,
+ pFormatProperties);
+}
+
+VkResult GetPhysicalDeviceImageFormatProperties2(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo,
+ VkImageFormatProperties2* pImageFormatProperties) {
+ ATRACE_CALL();
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceImageFormatProperties2) {
+ return driver.GetPhysicalDeviceImageFormatProperties2(
+ physicalDevice, pImageFormatInfo, pImageFormatProperties);
+ }
+
+ return driver.GetPhysicalDeviceImageFormatProperties2KHR(
+ physicalDevice, pImageFormatInfo, pImageFormatProperties);
+}
+
+void GetPhysicalDeviceQueueFamilyProperties2(
+ VkPhysicalDevice physicalDevice,
+ uint32_t* pQueueFamilyPropertyCount,
+ VkQueueFamilyProperties2* pQueueFamilyProperties) {
+ ATRACE_CALL();
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceQueueFamilyProperties2) {
+ driver.GetPhysicalDeviceQueueFamilyProperties2(
+ physicalDevice, pQueueFamilyPropertyCount, pQueueFamilyProperties);
+ return;
+ }
+
+ driver.GetPhysicalDeviceQueueFamilyProperties2KHR(
+ physicalDevice, pQueueFamilyPropertyCount, pQueueFamilyProperties);
+}
+
+void GetPhysicalDeviceMemoryProperties2(
+ VkPhysicalDevice physicalDevice,
+ VkPhysicalDeviceMemoryProperties2* pMemoryProperties) {
+ ATRACE_CALL();
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceMemoryProperties2) {
+ driver.GetPhysicalDeviceMemoryProperties2(physicalDevice,
+ pMemoryProperties);
+ return;
+ }
+
+ driver.GetPhysicalDeviceMemoryProperties2KHR(physicalDevice,
+ pMemoryProperties);
+}
+
+void GetPhysicalDeviceSparseImageFormatProperties2(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo,
+ uint32_t* pPropertyCount,
+ VkSparseImageFormatProperties2* pProperties) {
+ ATRACE_CALL();
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceSparseImageFormatProperties2) {
+ driver.GetPhysicalDeviceSparseImageFormatProperties2(
+ physicalDevice, pFormatInfo, pPropertyCount, pProperties);
+ return;
+ }
+
+ driver.GetPhysicalDeviceSparseImageFormatProperties2KHR(
+ physicalDevice, pFormatInfo, pPropertyCount, pProperties);
+}
+
+void GetPhysicalDeviceExternalBufferProperties(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo,
+ VkExternalBufferProperties* pExternalBufferProperties) {
+ ATRACE_CALL();
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceExternalBufferProperties) {
+ driver.GetPhysicalDeviceExternalBufferProperties(
+ physicalDevice, pExternalBufferInfo, pExternalBufferProperties);
+ return;
+ }
+
+ if (driver.GetPhysicalDeviceExternalBufferPropertiesKHR) {
+ driver.GetPhysicalDeviceExternalBufferPropertiesKHR(
+ physicalDevice, pExternalBufferInfo, pExternalBufferProperties);
+ return;
+ }
+
+ memset(&pExternalBufferProperties->externalMemoryProperties, 0,
+ sizeof(VkExternalMemoryProperties));
+}
+
+void GetPhysicalDeviceExternalSemaphoreProperties(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo,
+ VkExternalSemaphoreProperties* pExternalSemaphoreProperties) {
+ ATRACE_CALL();
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceExternalSemaphoreProperties) {
+ driver.GetPhysicalDeviceExternalSemaphoreProperties(
+ physicalDevice, pExternalSemaphoreInfo,
+ pExternalSemaphoreProperties);
+ return;
+ }
+
+ if (driver.GetPhysicalDeviceExternalSemaphorePropertiesKHR) {
+ driver.GetPhysicalDeviceExternalSemaphorePropertiesKHR(
+ physicalDevice, pExternalSemaphoreInfo,
+ pExternalSemaphoreProperties);
+ return;
+ }
+
+ pExternalSemaphoreProperties->exportFromImportedHandleTypes = 0;
+ pExternalSemaphoreProperties->compatibleHandleTypes = 0;
+ pExternalSemaphoreProperties->externalSemaphoreFeatures = 0;
+}
+
+void GetPhysicalDeviceExternalFenceProperties(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo,
+ VkExternalFenceProperties* pExternalFenceProperties) {
+ ATRACE_CALL();
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceExternalFenceProperties) {
+ driver.GetPhysicalDeviceExternalFenceProperties(
+ physicalDevice, pExternalFenceInfo, pExternalFenceProperties);
+ return;
+ }
+
+ if (driver.GetPhysicalDeviceExternalFencePropertiesKHR) {
+ driver.GetPhysicalDeviceExternalFencePropertiesKHR(
+ physicalDevice, pExternalFenceInfo, pExternalFenceProperties);
+ return;
+ }
+
+ pExternalFenceProperties->exportFromImportedHandleTypes = 0;
+ pExternalFenceProperties->compatibleHandleTypes = 0;
+ pExternalFenceProperties->externalFenceFeatures = 0;
+}
+
} // namespace driver
} // namespace vulkan
diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h
index 23c717c..14c516b 100644
--- a/vulkan/libvulkan/driver.h
+++ b/vulkan/libvulkan/driver.h
@@ -103,30 +103,95 @@
bool OpenHAL();
const VkAllocationCallbacks& GetDefaultAllocator();
-bool QueryPresentationProperties(
+void QueryPresentationProperties(
VkPhysicalDevice physicalDevice,
- VkPhysicalDevicePresentationPropertiesANDROID *presentation_properties);
+ VkPhysicalDevicePresentationPropertiesANDROID* presentation_properties);
-// clang-format off
-VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const char* pName);
-VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device, const char* pName);
-VKAPI_ATTR VkResult EnumerateInstanceExtensionProperties(const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties);
-
-VKAPI_ATTR VkResult EnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties);
-
-VKAPI_ATTR VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance);
-VKAPI_ATTR void DestroyInstance(VkInstance instance, const VkAllocationCallbacks* pAllocator);
-VKAPI_ATTR VkResult CreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice);
-VKAPI_ATTR void DestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator);
-
-VKAPI_ATTR VkResult EnumeratePhysicalDevices(VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices);
-VKAPI_ATTR VkResult EnumeratePhysicalDeviceGroups(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties);
-
-VKAPI_ATTR void GetDeviceQueue(VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue* pQueue);
-VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue);
-VKAPI_ATTR VkResult AllocateCommandBuffers(VkDevice device, const VkCommandBufferAllocateInfo* pAllocateInfo, VkCommandBuffer* pCommandBuffers);
-VKAPI_ATTR VkResult QueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence);
-// clang-format on
+VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance,
+ const char* pName);
+VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device,
+ const char* pName);
+VKAPI_ATTR VkResult
+EnumerateInstanceExtensionProperties(const char* pLayerName,
+ uint32_t* pPropertyCount,
+ VkExtensionProperties* pProperties);
+VKAPI_ATTR VkResult
+EnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice,
+ const char* pLayerName,
+ uint32_t* pPropertyCount,
+ VkExtensionProperties* pProperties);
+VKAPI_ATTR VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo,
+ const VkAllocationCallbacks* pAllocator,
+ VkInstance* pInstance);
+VKAPI_ATTR void DestroyInstance(VkInstance instance,
+ const VkAllocationCallbacks* pAllocator);
+VKAPI_ATTR VkResult CreateDevice(VkPhysicalDevice physicalDevice,
+ const VkDeviceCreateInfo* pCreateInfo,
+ const VkAllocationCallbacks* pAllocator,
+ VkDevice* pDevice);
+VKAPI_ATTR void DestroyDevice(VkDevice device,
+ const VkAllocationCallbacks* pAllocator);
+VKAPI_ATTR VkResult
+EnumeratePhysicalDevices(VkInstance instance,
+ uint32_t* pPhysicalDeviceCount,
+ VkPhysicalDevice* pPhysicalDevices);
+VKAPI_ATTR VkResult EnumeratePhysicalDeviceGroups(
+ VkInstance instance,
+ uint32_t* pPhysicalDeviceGroupCount,
+ VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties);
+VKAPI_ATTR void GetDeviceQueue(VkDevice device,
+ uint32_t queueFamilyIndex,
+ uint32_t queueIndex,
+ VkQueue* pQueue);
+VKAPI_ATTR void GetDeviceQueue2(VkDevice device,
+ const VkDeviceQueueInfo2* pQueueInfo,
+ VkQueue* pQueue);
+VKAPI_ATTR VkResult
+AllocateCommandBuffers(VkDevice device,
+ const VkCommandBufferAllocateInfo* pAllocateInfo,
+ VkCommandBuffer* pCommandBuffers);
+VKAPI_ATTR VkResult QueueSubmit(VkQueue queue,
+ uint32_t submitCount,
+ const VkSubmitInfo* pSubmits,
+ VkFence fence);
+VKAPI_ATTR void GetPhysicalDeviceFeatures2(
+ VkPhysicalDevice physicalDevice,
+ VkPhysicalDeviceFeatures2* pFeatures);
+VKAPI_ATTR void GetPhysicalDeviceProperties2(
+ VkPhysicalDevice physicalDevice,
+ VkPhysicalDeviceProperties2* pProperties);
+VKAPI_ATTR void GetPhysicalDeviceFormatProperties2(
+ VkPhysicalDevice physicalDevice,
+ VkFormat format,
+ VkFormatProperties2* pFormatProperties);
+VKAPI_ATTR VkResult GetPhysicalDeviceImageFormatProperties2(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo,
+ VkImageFormatProperties2* pImageFormatProperties);
+VKAPI_ATTR void GetPhysicalDeviceQueueFamilyProperties2(
+ VkPhysicalDevice physicalDevice,
+ uint32_t* pQueueFamilyPropertyCount,
+ VkQueueFamilyProperties2* pQueueFamilyProperties);
+VKAPI_ATTR void GetPhysicalDeviceMemoryProperties2(
+ VkPhysicalDevice physicalDevice,
+ VkPhysicalDeviceMemoryProperties2* pMemoryProperties);
+VKAPI_ATTR void GetPhysicalDeviceSparseImageFormatProperties2(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo,
+ uint32_t* pPropertyCount,
+ VkSparseImageFormatProperties2* pProperties);
+VKAPI_ATTR void GetPhysicalDeviceExternalBufferProperties(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo,
+ VkExternalBufferProperties* pExternalBufferProperties);
+VKAPI_ATTR void GetPhysicalDeviceExternalSemaphoreProperties(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo,
+ VkExternalSemaphoreProperties* pExternalSemaphoreProperties);
+VKAPI_ATTR void GetPhysicalDeviceExternalFenceProperties(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo,
+ VkExternalFenceProperties* pExternalFenceProperties);
template <typename DispatchableType>
void StaticAssertDispatchable(DispatchableType) {
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index 52205e9..5f37a50 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -363,6 +363,55 @@
reinterpret_cast<PFN_vkVoidFunction>(checkedGetPastPresentationTimingGOOGLE),
},
{
+ "vkGetPhysicalDeviceExternalBufferProperties",
+ ProcHook::INSTANCE,
+ ProcHook::EXTENSION_CORE_1_1,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceExternalBufferProperties),
+ nullptr,
+ },
+ {
+ "vkGetPhysicalDeviceExternalFenceProperties",
+ ProcHook::INSTANCE,
+ ProcHook::EXTENSION_CORE_1_1,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceExternalFenceProperties),
+ nullptr,
+ },
+ {
+ "vkGetPhysicalDeviceExternalSemaphoreProperties",
+ ProcHook::INSTANCE,
+ ProcHook::EXTENSION_CORE_1_1,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceExternalSemaphoreProperties),
+ nullptr,
+ },
+ {
+ "vkGetPhysicalDeviceFeatures2",
+ ProcHook::INSTANCE,
+ ProcHook::EXTENSION_CORE_1_1,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceFeatures2),
+ nullptr,
+ },
+ {
+ "vkGetPhysicalDeviceFormatProperties2",
+ ProcHook::INSTANCE,
+ ProcHook::EXTENSION_CORE_1_1,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceFormatProperties2),
+ nullptr,
+ },
+ {
+ "vkGetPhysicalDeviceImageFormatProperties2",
+ ProcHook::INSTANCE,
+ ProcHook::EXTENSION_CORE_1_1,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceImageFormatProperties2),
+ nullptr,
+ },
+ {
+ "vkGetPhysicalDeviceMemoryProperties2",
+ ProcHook::INSTANCE,
+ ProcHook::EXTENSION_CORE_1_1,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceMemoryProperties2),
+ nullptr,
+ },
+ {
"vkGetPhysicalDevicePresentRectanglesKHR",
ProcHook::INSTANCE,
ProcHook::KHR_swapchain,
@@ -370,6 +419,27 @@
nullptr,
},
{
+ "vkGetPhysicalDeviceProperties2",
+ ProcHook::INSTANCE,
+ ProcHook::EXTENSION_CORE_1_1,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceProperties2),
+ nullptr,
+ },
+ {
+ "vkGetPhysicalDeviceQueueFamilyProperties2",
+ ProcHook::INSTANCE,
+ ProcHook::EXTENSION_CORE_1_1,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceQueueFamilyProperties2),
+ nullptr,
+ },
+ {
+ "vkGetPhysicalDeviceSparseImageFormatProperties2",
+ ProcHook::INSTANCE,
+ ProcHook::EXTENSION_CORE_1_1,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceSparseImageFormatProperties2),
+ nullptr,
+ },
+ {
"vkGetPhysicalDeviceSurfaceCapabilities2KHR",
ProcHook::INSTANCE,
ProcHook::KHR_get_surface_capabilities2,
@@ -480,10 +550,9 @@
} // namespace
const ProcHook* GetProcHook(const char* name) {
- const auto& begin = g_proc_hooks;
- const auto& end =
- g_proc_hooks + sizeof(g_proc_hooks) / sizeof(g_proc_hooks[0]);
- const auto hook = std::lower_bound(
+ auto begin = std::cbegin(g_proc_hooks);
+ auto end = std::cend(g_proc_hooks);
+ auto hook = std::lower_bound(
begin, end, name,
[](const ProcHook& e, const char* n) { return strcmp(e.name, n) < 0; });
return (hook < end && strcmp(hook->name, name) == 0) ? hook : nullptr;
@@ -505,6 +574,10 @@
if (strcmp(name, "VK_ANDROID_external_memory_android_hardware_buffer") == 0) return ProcHook::ANDROID_external_memory_android_hardware_buffer;
if (strcmp(name, "VK_KHR_bind_memory2") == 0) return ProcHook::KHR_bind_memory2;
if (strcmp(name, "VK_KHR_get_physical_device_properties2") == 0) return ProcHook::KHR_get_physical_device_properties2;
+ if (strcmp(name, "VK_KHR_device_group_creation") == 0) return ProcHook::KHR_device_group_creation;
+ if (strcmp(name, "VK_KHR_external_memory_capabilities") == 0) return ProcHook::KHR_external_memory_capabilities;
+ if (strcmp(name, "VK_KHR_external_semaphore_capabilities") == 0) return ProcHook::KHR_external_semaphore_capabilities;
+ if (strcmp(name, "VK_KHR_external_fence_capabilities") == 0) return ProcHook::KHR_external_fence_capabilities;
// clang-format on
return ProcHook::EXTENSION_UNKNOWN;
}
@@ -543,9 +616,28 @@
INIT_PROC_EXT(EXT_debug_report, true, instance, CreateDebugReportCallbackEXT);
INIT_PROC_EXT(EXT_debug_report, true, instance, DestroyDebugReportCallbackEXT);
INIT_PROC_EXT(EXT_debug_report, true, instance, DebugReportMessageEXT);
+ INIT_PROC(false, instance, GetPhysicalDeviceFeatures2);
+ INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceFeatures2KHR);
INIT_PROC(false, instance, GetPhysicalDeviceProperties2);
INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceProperties2KHR);
+ INIT_PROC(false, instance, GetPhysicalDeviceFormatProperties2);
+ INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceFormatProperties2KHR);
+ INIT_PROC(false, instance, GetPhysicalDeviceImageFormatProperties2);
+ INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceImageFormatProperties2KHR);
+ INIT_PROC(false, instance, GetPhysicalDeviceQueueFamilyProperties2);
+ INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceQueueFamilyProperties2KHR);
+ INIT_PROC(false, instance, GetPhysicalDeviceMemoryProperties2);
+ INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceMemoryProperties2KHR);
+ INIT_PROC(false, instance, GetPhysicalDeviceSparseImageFormatProperties2);
+ INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceSparseImageFormatProperties2KHR);
+ INIT_PROC(false, instance, GetPhysicalDeviceExternalBufferProperties);
+ INIT_PROC_EXT(KHR_external_memory_capabilities, true, instance, GetPhysicalDeviceExternalBufferPropertiesKHR);
+ INIT_PROC(false, instance, GetPhysicalDeviceExternalSemaphoreProperties);
+ INIT_PROC_EXT(KHR_external_semaphore_capabilities, true, instance, GetPhysicalDeviceExternalSemaphorePropertiesKHR);
+ INIT_PROC(false, instance, GetPhysicalDeviceExternalFenceProperties);
+ INIT_PROC_EXT(KHR_external_fence_capabilities, true, instance, GetPhysicalDeviceExternalFencePropertiesKHR);
INIT_PROC(false, instance, EnumeratePhysicalDeviceGroups);
+ INIT_PROC_EXT(KHR_device_group_creation, true, instance, EnumeratePhysicalDeviceGroupsKHR);
// clang-format on
return success;
@@ -577,5 +669,53 @@
return success;
}
+const std::pair<const char*, uint32_t> g_promoted_instance_extensions[] = {
+ // clang-format off
+ std::make_pair("VK_KHR_device_group_creation", VK_API_VERSION_1_1),
+ std::make_pair("VK_KHR_external_fence_capabilities", VK_API_VERSION_1_1),
+ std::make_pair("VK_KHR_external_memory_capabilities", VK_API_VERSION_1_1),
+ std::make_pair("VK_KHR_external_semaphore_capabilities", VK_API_VERSION_1_1),
+ std::make_pair("VK_KHR_get_physical_device_properties2", VK_API_VERSION_1_1),
+ // clang-format on
+};
+
+std::optional<uint32_t> GetInstanceExtensionPromotedVersion(const char* name) {
+ auto begin = std::cbegin(g_promoted_instance_extensions);
+ auto end = std::cend(g_promoted_instance_extensions);
+ auto iter =
+ std::lower_bound(begin, end, name,
+ [](const std::pair<const char*, uint32_t>& e,
+ const char* n) { return strcmp(e.first, n) < 0; });
+ return (iter < end && strcmp(iter->first, name) == 0)
+ ? std::optional<uint32_t>(iter->second)
+ : std::nullopt;
+}
+
+uint32_t CountPromotedInstanceExtensions(uint32_t begin_version,
+ uint32_t end_version) {
+ auto begin = std::cbegin(g_promoted_instance_extensions);
+ auto end = std::cend(g_promoted_instance_extensions);
+ uint32_t count = 0;
+
+ for (auto iter = begin; iter != end; iter++)
+ if (iter->second > begin_version && iter->second <= end_version)
+ count++;
+
+ return count;
+}
+
+std::vector<const char*> GetPromotedInstanceExtensions(uint32_t begin_version,
+ uint32_t end_version) {
+ auto begin = std::cbegin(g_promoted_instance_extensions);
+ auto end = std::cend(g_promoted_instance_extensions);
+ std::vector<const char*> extensions;
+
+ for (auto iter = begin; iter != end; iter++)
+ if (iter->second > begin_version && iter->second <= end_version)
+ extensions.emplace_back(iter->first);
+
+ return extensions;
+}
+
} // namespace driver
} // namespace vulkan
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index 62538c1..047e774 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -23,6 +23,8 @@
#include <vulkan/vulkan.h>
#include <bitset>
+#include <optional>
+#include <vector>
namespace vulkan {
namespace driver {
@@ -48,6 +50,10 @@
ANDROID_external_memory_android_hardware_buffer,
KHR_bind_memory2,
KHR_get_physical_device_properties2,
+ KHR_device_group_creation,
+ KHR_external_memory_capabilities,
+ KHR_external_semaphore_capabilities,
+ KHR_external_fence_capabilities,
EXTENSION_CORE_1_0,
EXTENSION_CORE_1_1,
@@ -75,9 +81,28 @@
PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT;
PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT;
PFN_vkDebugReportMessageEXT DebugReportMessageEXT;
+ PFN_vkGetPhysicalDeviceFeatures2 GetPhysicalDeviceFeatures2;
+ PFN_vkGetPhysicalDeviceFeatures2KHR GetPhysicalDeviceFeatures2KHR;
PFN_vkGetPhysicalDeviceProperties2 GetPhysicalDeviceProperties2;
PFN_vkGetPhysicalDeviceProperties2KHR GetPhysicalDeviceProperties2KHR;
+ PFN_vkGetPhysicalDeviceFormatProperties2 GetPhysicalDeviceFormatProperties2;
+ PFN_vkGetPhysicalDeviceFormatProperties2KHR GetPhysicalDeviceFormatProperties2KHR;
+ PFN_vkGetPhysicalDeviceImageFormatProperties2 GetPhysicalDeviceImageFormatProperties2;
+ PFN_vkGetPhysicalDeviceImageFormatProperties2KHR GetPhysicalDeviceImageFormatProperties2KHR;
+ PFN_vkGetPhysicalDeviceQueueFamilyProperties2 GetPhysicalDeviceQueueFamilyProperties2;
+ PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR GetPhysicalDeviceQueueFamilyProperties2KHR;
+ PFN_vkGetPhysicalDeviceMemoryProperties2 GetPhysicalDeviceMemoryProperties2;
+ PFN_vkGetPhysicalDeviceMemoryProperties2KHR GetPhysicalDeviceMemoryProperties2KHR;
+ PFN_vkGetPhysicalDeviceSparseImageFormatProperties2 GetPhysicalDeviceSparseImageFormatProperties2;
+ PFN_vkGetPhysicalDeviceSparseImageFormatProperties2KHR GetPhysicalDeviceSparseImageFormatProperties2KHR;
+ PFN_vkGetPhysicalDeviceExternalBufferProperties GetPhysicalDeviceExternalBufferProperties;
+ PFN_vkGetPhysicalDeviceExternalBufferPropertiesKHR GetPhysicalDeviceExternalBufferPropertiesKHR;
+ PFN_vkGetPhysicalDeviceExternalSemaphoreProperties GetPhysicalDeviceExternalSemaphoreProperties;
+ PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR GetPhysicalDeviceExternalSemaphorePropertiesKHR;
+ PFN_vkGetPhysicalDeviceExternalFenceProperties GetPhysicalDeviceExternalFenceProperties;
+ PFN_vkGetPhysicalDeviceExternalFencePropertiesKHR GetPhysicalDeviceExternalFencePropertiesKHR;
PFN_vkEnumeratePhysicalDeviceGroups EnumeratePhysicalDeviceGroups;
+ PFN_vkEnumeratePhysicalDeviceGroupsKHR EnumeratePhysicalDeviceGroupsKHR;
// clang-format on
};
@@ -110,6 +135,12 @@
PFN_vkGetDeviceProcAddr get_proc,
const std::bitset<ProcHook::EXTENSION_COUNT>& extensions);
+std::optional<uint32_t> GetInstanceExtensionPromotedVersion(const char* name);
+uint32_t CountPromotedInstanceExtensions(uint32_t begin_version,
+ uint32_t end_version);
+std::vector<const char*> GetPromotedInstanceExtensions(uint32_t begin_version,
+ uint32_t end_version);
+
} // namespace driver
} // namespace vulkan
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 2913850..8d6681c 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -872,11 +872,10 @@
present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR);
VkPhysicalDevicePresentationPropertiesANDROID present_properties;
- if (QueryPresentationProperties(pdev, &present_properties)) {
- if (present_properties.sharedImage) {
- present_modes.push_back(VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR);
- present_modes.push_back(VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR);
- }
+ QueryPresentationProperties(pdev, &present_properties);
+ if (present_properties.sharedImage) {
+ present_modes.push_back(VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR);
+ present_modes.push_back(VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR);
}
uint32_t num_modes = uint32_t(present_modes.size());
@@ -961,7 +960,6 @@
strerror(-err), err);
}
- // TODO(b/143294545): Return something better than "whole window"
pRects[0].offset.x = 0;
pRects[0].offset.y = 0;
pRects[0].extent = VkExtent2D{static_cast<uint32_t>(width),
diff --git a/vulkan/scripts/driver_generator.py b/vulkan/scripts/driver_generator.py
index a64a702..6a73023 100644
--- a/vulkan/scripts/driver_generator.py
+++ b/vulkan/scripts/driver_generator.py
@@ -40,6 +40,10 @@
'VK_ANDROID_external_memory_android_hardware_buffer',
'VK_KHR_bind_memory2',
'VK_KHR_get_physical_device_properties2',
+ 'VK_KHR_device_group_creation',
+ 'VK_KHR_external_memory_capabilities',
+ 'VK_KHR_external_semaphore_capabilities',
+ 'VK_KHR_external_fence_capabilities',
]
# Functions needed at vulkan::driver level.
@@ -71,12 +75,41 @@
'vkDestroyImage',
'vkGetPhysicalDeviceProperties',
- 'vkGetPhysicalDeviceProperties2',
- 'vkGetPhysicalDeviceProperties2KHR',
# VK_KHR_swapchain v69 requirement
'vkBindImageMemory2',
'vkBindImageMemory2KHR',
+
+ # For promoted VK_KHR_device_group_creation
+ 'vkEnumeratePhysicalDeviceGroupsKHR',
+
+ # For promoted VK_KHR_get_physical_device_properties2
+ 'vkGetPhysicalDeviceFeatures2',
+ 'vkGetPhysicalDeviceFeatures2KHR',
+ 'vkGetPhysicalDeviceProperties2',
+ 'vkGetPhysicalDeviceProperties2KHR',
+ 'vkGetPhysicalDeviceFormatProperties2',
+ 'vkGetPhysicalDeviceFormatProperties2KHR',
+ 'vkGetPhysicalDeviceImageFormatProperties2',
+ 'vkGetPhysicalDeviceImageFormatProperties2KHR',
+ 'vkGetPhysicalDeviceQueueFamilyProperties2',
+ 'vkGetPhysicalDeviceQueueFamilyProperties2KHR',
+ 'vkGetPhysicalDeviceMemoryProperties2',
+ 'vkGetPhysicalDeviceMemoryProperties2KHR',
+ 'vkGetPhysicalDeviceSparseImageFormatProperties2',
+ 'vkGetPhysicalDeviceSparseImageFormatProperties2KHR',
+
+ # For promoted VK_KHR_external_memory_capabilities
+ 'vkGetPhysicalDeviceExternalBufferProperties',
+ 'vkGetPhysicalDeviceExternalBufferPropertiesKHR',
+
+ # For promoted VK_KHR_external_semaphore_capabilities
+ 'vkGetPhysicalDeviceExternalSemaphoreProperties',
+ 'vkGetPhysicalDeviceExternalSemaphorePropertiesKHR',
+
+ # For promoted VK_KHR_external_fence_capabilities
+ 'vkGetPhysicalDeviceExternalFenceProperties',
+ 'vkGetPhysicalDeviceExternalFencePropertiesKHR',
]
# Functions intercepted at vulkan::driver level.
@@ -106,6 +139,24 @@
# VK_KHR_swapchain v69 requirement
'vkBindImageMemory2',
'vkBindImageMemory2KHR',
+
+ # For promoted VK_KHR_get_physical_device_properties2
+ 'vkGetPhysicalDeviceFeatures2',
+ 'vkGetPhysicalDeviceProperties2',
+ 'vkGetPhysicalDeviceFormatProperties2',
+ 'vkGetPhysicalDeviceImageFormatProperties2',
+ 'vkGetPhysicalDeviceQueueFamilyProperties2',
+ 'vkGetPhysicalDeviceMemoryProperties2',
+ 'vkGetPhysicalDeviceSparseImageFormatProperties2',
+
+ # For promoted VK_KHR_external_memory_capabilities
+ 'vkGetPhysicalDeviceExternalBufferProperties',
+
+ # For promoted VK_KHR_external_semaphore_capabilities
+ 'vkGetPhysicalDeviceExternalSemaphoreProperties',
+
+ # For promoted VK_KHR_external_fence_capabilities
+ 'vkGetPhysicalDeviceExternalFenceProperties',
]
@@ -162,6 +213,8 @@
#include <vulkan/vulkan.h>
#include <bitset>
+#include <optional>
+#include <vector>
namespace vulkan {
namespace driver {
@@ -229,6 +282,12 @@
PFN_vkGetDeviceProcAddr get_proc,
const std::bitset<ProcHook::EXTENSION_COUNT>& extensions);
+std::optional<uint32_t> GetInstanceExtensionPromotedVersion(const char* name);
+uint32_t CountPromotedInstanceExtensions(uint32_t begin_version,
+ uint32_t end_version);
+std::vector<const char*> GetPromotedInstanceExtensions(uint32_t begin_version,
+ uint32_t end_version);
+
} // namespace driver
} // namespace vulkan
@@ -464,10 +523,9 @@
} // namespace
const ProcHook* GetProcHook(const char* name) {
- const auto& begin = g_proc_hooks;
- const auto& end =
- g_proc_hooks + sizeof(g_proc_hooks) / sizeof(g_proc_hooks[0]);
- const auto hook = std::lower_bound(
+ auto begin = std::cbegin(g_proc_hooks);
+ auto end = std::cend(g_proc_hooks);
+ auto hook = std::lower_bound(
begin, end, name,
[](const ProcHook& e, const char* n) { return strcmp(e.name, n) < 0; });
return (hook < end && strcmp(hook->name, name) == 0) ? hook : nullptr;
@@ -539,6 +597,54 @@
return success;
}
+const std::pair<const char*, uint32_t> g_promoted_instance_extensions[] = {
+ // clang-format off\n""")
+
+ for key, value in sorted(gencom.promoted_inst_ext_dict.items()):
+ f.write(gencom.indent(1) + 'std::make_pair("' + key + '", ' + value + '),\n')
+
+ f.write("""\
+ // clang-format on
+};
+
+std::optional<uint32_t> GetInstanceExtensionPromotedVersion(const char* name) {
+ auto begin = std::cbegin(g_promoted_instance_extensions);
+ auto end = std::cend(g_promoted_instance_extensions);
+ auto iter =
+ std::lower_bound(begin, end, name,
+ [](const std::pair<const char*, uint32_t>& e,
+ const char* n) { return strcmp(e.first, n) < 0; });
+ return (iter < end && strcmp(iter->first, name) == 0)
+ ? std::optional<uint32_t>(iter->second)
+ : std::nullopt;
+}
+
+uint32_t CountPromotedInstanceExtensions(uint32_t begin_version,
+ uint32_t end_version) {
+ auto begin = std::cbegin(g_promoted_instance_extensions);
+ auto end = std::cend(g_promoted_instance_extensions);
+ uint32_t count = 0;
+
+ for (auto iter = begin; iter != end; iter++)
+ if (iter->second > begin_version && iter->second <= end_version)
+ count++;
+
+ return count;
+}
+
+std::vector<const char*> GetPromotedInstanceExtensions(uint32_t begin_version,
+ uint32_t end_version) {
+ auto begin = std::cbegin(g_promoted_instance_extensions);
+ auto end = std::cend(g_promoted_instance_extensions);
+ std::vector<const char*> extensions;
+
+ for (auto iter = begin; iter != end; iter++)
+ if (iter->second > begin_version && iter->second <= end_version)
+ extensions.emplace_back(iter->first);
+
+ return extensions;
+}
+
} // namespace driver
} // namespace vulkan\n""")
diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py
index e9a96f3..72fd4fb 100644
--- a/vulkan/scripts/generator_common.py
+++ b/vulkan/scripts/generator_common.py
@@ -98,6 +98,9 @@
# Dict for mapping a function to the core Vulkan API version.
version_dict = {}
+# Dict for mapping a promoted instance extension to the core Vulkan API version.
+promoted_inst_ext_dict = {}
+
def indent(num):
"""Returns the requested indents.
@@ -184,6 +187,15 @@
return version[11:]
+def version_2_api_version(version):
+ """Returns the api version from a version string.
+
+ Args:
+ version: Vulkan version string.
+ """
+ return 'VK_API' + version[2:]
+
+
def is_function_supported(cmd):
"""Returns true if a function is core or from a supportable extension.
@@ -328,6 +340,7 @@
return_type_dict
version_code_list
version_dict
+ promoted_inst_ext_dict
"""
registry = os.path.join(os.path.dirname(__file__), '..', '..', '..', '..',
'external', 'vulkan-headers', 'registry', 'vk.xml')
@@ -380,6 +393,10 @@
apiversion = 'VK_VERSION_1_0'
if extension.tag == 'extension':
extname = extension.get('name')
+ if (extension.get('type') == 'instance' and
+ extension.get('promotedto') is not None):
+ promoted_inst_ext_dict[extname] = \
+ version_2_api_version(extension.get('promotedto'))
for req in extension:
if req.get('feature') is not None:
apiversion = req.get('feature')
diff --git a/vulkan/vkjson/vkjson.h b/vulkan/vkjson/vkjson.h
index a283b83..52e7bee 100644
--- a/vulkan/vkjson/vkjson.h
+++ b/vulkan/vkjson/vkjson.h
@@ -158,10 +158,7 @@
VkJsonInstance* instance,
std::string* errors);
-VkJsonDevice VkJsonGetDevice(VkInstance instance,
- VkPhysicalDevice device,
- uint32_t instanceExtensionCount,
- const char* const* instanceExtensions);
+VkJsonDevice VkJsonGetDevice(VkPhysicalDevice device);
std::string VkJsonDeviceToJson(const VkJsonDevice& device);
bool VkJsonDeviceFromJson(const std::string& json,
VkJsonDevice* device,
@@ -177,7 +174,7 @@
typedef VkJsonDevice VkJsonAllProperties;
inline VkJsonAllProperties VkJsonGetAllProperties(
VkPhysicalDevice physicalDevice) {
- return VkJsonGetDevice(VK_NULL_HANDLE, physicalDevice, 0, nullptr);
+ return VkJsonGetDevice(physicalDevice);
}
inline std::string VkJsonAllPropertiesToJson(
const VkJsonAllProperties& properties) {
diff --git a/vulkan/vkjson/vkjson_instance.cc b/vulkan/vkjson/vkjson_instance.cc
index c3f4963..5872495 100644
--- a/vulkan/vkjson/vkjson_instance.cc
+++ b/vulkan/vkjson/vkjson_instance.cc
@@ -28,8 +28,6 @@
#include <utility>
namespace {
-const char* kSupportedInstanceExtensions[] = {
- "VK_KHR_get_physical_device_properties2"};
bool EnumerateExtensions(const char* layer_name,
std::vector<VkExtensionProperties>* extensions) {
@@ -47,15 +45,6 @@
}
bool HasExtension(const char* extension_name,
- uint32_t count,
- const char* const* extensions) {
- return std::find_if(extensions, extensions + count,
- [extension_name](const char* extension) {
- return strcmp(extension, extension_name) == 0;
- }) != extensions + count;
-}
-
-bool HasExtension(const char* extension_name,
const std::vector<VkExtensionProperties>& extensions) {
return std::find_if(extensions.cbegin(), extensions.cend(),
[extension_name](const VkExtensionProperties& extension) {
@@ -65,27 +54,9 @@
}
} // anonymous namespace
-VkJsonDevice VkJsonGetDevice(VkInstance instance,
- VkPhysicalDevice physical_device,
- uint32_t instance_extension_count,
- const char* const* instance_extensions) {
+VkJsonDevice VkJsonGetDevice(VkPhysicalDevice physical_device) {
VkJsonDevice device;
- PFN_vkGetPhysicalDeviceProperties2KHR vkpGetPhysicalDeviceProperties2KHR =
- nullptr;
- PFN_vkGetPhysicalDeviceFeatures2KHR vkpGetPhysicalDeviceFeatures2KHR =
- nullptr;
- if (instance != VK_NULL_HANDLE &&
- HasExtension("VK_KHR_get_physical_device_properties2",
- instance_extension_count, instance_extensions)) {
- vkpGetPhysicalDeviceProperties2KHR =
- reinterpret_cast<PFN_vkGetPhysicalDeviceProperties2KHR>(
- vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceProperties2KHR"));
- vkpGetPhysicalDeviceFeatures2KHR =
- reinterpret_cast<PFN_vkGetPhysicalDeviceFeatures2KHR>(
- vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceFeatures2KHR"));
- }
-
uint32_t extension_count = 0;
vkEnumerateDeviceExtensionProperties(physical_device, nullptr,
&extension_count, nullptr);
@@ -103,55 +74,48 @@
device.layers.data());
}
- if (HasExtension("VK_KHR_get_physical_device_properties2",
- instance_extension_count, instance_extensions)) {
- VkPhysicalDeviceProperties2KHR properties = {
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
- nullptr,
- {} // properties
- };
- if (HasExtension("VK_KHR_driver_properties", device.extensions)) {
- device.ext_driver_properties.reported = true;
- device.ext_driver_properties.driver_properties_khr.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR;
- device.ext_driver_properties.driver_properties_khr.pNext =
- properties.pNext;
- properties.pNext =
- &device.ext_driver_properties.driver_properties_khr;
- }
- vkpGetPhysicalDeviceProperties2KHR(physical_device, &properties);
- device.properties = properties.properties;
-
- VkPhysicalDeviceFeatures2KHR features = {
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR,
- nullptr,
- {} // features
- };
- if (HasExtension("VK_KHR_variable_pointers", device.extensions)) {
- device.ext_variable_pointer_features.reported = true;
- device.ext_variable_pointer_features.variable_pointer_features_khr.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR;
- device.ext_variable_pointer_features.variable_pointer_features_khr.pNext =
- features.pNext;
- features.pNext =
- &device.ext_variable_pointer_features.variable_pointer_features_khr;
- }
- if (HasExtension("VK_KHR_shader_float16_int8", device.extensions)) {
- device.ext_shader_float16_int8_features.reported = true;
- device.ext_shader_float16_int8_features.shader_float16_int8_features_khr
- .sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR;
- device.ext_shader_float16_int8_features.shader_float16_int8_features_khr
- .pNext = features.pNext;
- features.pNext = &device.ext_shader_float16_int8_features
- .shader_float16_int8_features_khr;
- }
- vkpGetPhysicalDeviceFeatures2KHR(physical_device, &features);
- device.features = features.features;
- } else {
- vkGetPhysicalDeviceProperties(physical_device, &device.properties);
- vkGetPhysicalDeviceFeatures(physical_device, &device.features);
+ VkPhysicalDeviceProperties2 properties = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
+ nullptr,
+ {},
+ };
+ if (HasExtension("VK_KHR_driver_properties", device.extensions)) {
+ device.ext_driver_properties.reported = true;
+ device.ext_driver_properties.driver_properties_khr.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR;
+ device.ext_driver_properties.driver_properties_khr.pNext = properties.pNext;
+ properties.pNext = &device.ext_driver_properties.driver_properties_khr;
}
+ vkGetPhysicalDeviceProperties2(physical_device, &properties);
+ device.properties = properties.properties;
+
+ VkPhysicalDeviceFeatures2 features = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
+ nullptr,
+ {},
+ };
+ if (HasExtension("VK_KHR_variable_pointers", device.extensions)) {
+ device.ext_variable_pointer_features.reported = true;
+ device.ext_variable_pointer_features.variable_pointer_features_khr.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR;
+ device.ext_variable_pointer_features.variable_pointer_features_khr.pNext =
+ features.pNext;
+ features.pNext =
+ &device.ext_variable_pointer_features.variable_pointer_features_khr;
+ }
+ if (HasExtension("VK_KHR_shader_float16_int8", device.extensions)) {
+ device.ext_shader_float16_int8_features.reported = true;
+ device.ext_shader_float16_int8_features.shader_float16_int8_features_khr
+ .sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR;
+ device.ext_shader_float16_int8_features.shader_float16_int8_features_khr
+ .pNext = features.pNext;
+ features.pNext = &device.ext_shader_float16_int8_features
+ .shader_float16_int8_features_khr;
+ }
+ vkGetPhysicalDeviceFeatures2(physical_device, &features);
+ device.features = features.features;
+
vkGetPhysicalDeviceMemoryProperties(physical_device, &device.memory);
uint32_t queue_family_count = 0;
@@ -193,135 +157,117 @@
}
}
- PFN_vkGetPhysicalDeviceProperties2 vkpGetPhysicalDeviceProperties2 =
- reinterpret_cast<PFN_vkGetPhysicalDeviceProperties2>(
- vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceProperties2"));
- if (vkpGetPhysicalDeviceProperties2) {
- VkPhysicalDeviceProperties2 properties2 = {
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, nullptr, {}};
+ VkPhysicalDeviceProperties2 properties2 = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
+ nullptr,
+ {},
+ };
- device.subgroup_properties.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES;
- device.subgroup_properties.pNext = properties2.pNext;
- properties2.pNext = &device.subgroup_properties;
+ device.subgroup_properties.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES;
+ device.subgroup_properties.pNext = properties2.pNext;
+ properties2.pNext = &device.subgroup_properties;
- device.point_clipping_properties.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES;
- device.point_clipping_properties.pNext = properties2.pNext;
- properties2.pNext = &device.point_clipping_properties;
+ device.point_clipping_properties.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES;
+ device.point_clipping_properties.pNext = properties2.pNext;
+ properties2.pNext = &device.point_clipping_properties;
- device.multiview_properties.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES;
- device.multiview_properties.pNext = properties2.pNext;
- properties2.pNext = &device.multiview_properties;
+ device.multiview_properties.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES;
+ device.multiview_properties.pNext = properties2.pNext;
+ properties2.pNext = &device.multiview_properties;
- device.id_properties.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES;
- device.id_properties.pNext = properties2.pNext;
- properties2.pNext = &device.id_properties;
+ device.id_properties.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES;
+ device.id_properties.pNext = properties2.pNext;
+ properties2.pNext = &device.id_properties;
- device.maintenance3_properties.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES;
- device.maintenance3_properties.pNext = properties2.pNext;
- properties2.pNext = &device.maintenance3_properties;
+ device.maintenance3_properties.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES;
+ device.maintenance3_properties.pNext = properties2.pNext;
+ properties2.pNext = &device.maintenance3_properties;
- (*vkpGetPhysicalDeviceProperties2)(physical_device, &properties2);
- }
+ vkGetPhysicalDeviceProperties2(physical_device, &properties2);
- PFN_vkGetPhysicalDeviceFeatures2 vkpGetPhysicalDeviceFeatures2 =
- reinterpret_cast<PFN_vkGetPhysicalDeviceFeatures2>(
- vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceFeatures2"));
- if (vkpGetPhysicalDeviceFeatures2) {
- VkPhysicalDeviceFeatures2 features2 = {
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, nullptr, {}};
+ VkPhysicalDeviceFeatures2 features2 = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
+ nullptr,
+ {},
+ };
- device.bit16_storage_features.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES;
- device.bit16_storage_features.pNext = features2.pNext;
- features2.pNext = &device.bit16_storage_features;
+ device.bit16_storage_features.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES;
+ device.bit16_storage_features.pNext = features2.pNext;
+ features2.pNext = &device.bit16_storage_features;
- device.multiview_features.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES;
- device.multiview_features.pNext = features2.pNext;
- features2.pNext = &device.multiview_features;
+ device.multiview_features.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES;
+ device.multiview_features.pNext = features2.pNext;
+ features2.pNext = &device.multiview_features;
- device.variable_pointer_features.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES;
- device.variable_pointer_features.pNext = features2.pNext;
- features2.pNext = &device.variable_pointer_features;
+ device.variable_pointer_features.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES;
+ device.variable_pointer_features.pNext = features2.pNext;
+ features2.pNext = &device.variable_pointer_features;
- device.protected_memory_features.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES;
- device.protected_memory_features.pNext = features2.pNext;
- features2.pNext = &device.protected_memory_features;
+ device.protected_memory_features.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES;
+ device.protected_memory_features.pNext = features2.pNext;
+ features2.pNext = &device.protected_memory_features;
- device.sampler_ycbcr_conversion_features.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES;
- device.sampler_ycbcr_conversion_features.pNext = features2.pNext;
- features2.pNext = &device.sampler_ycbcr_conversion_features;
+ device.sampler_ycbcr_conversion_features.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES;
+ device.sampler_ycbcr_conversion_features.pNext = features2.pNext;
+ features2.pNext = &device.sampler_ycbcr_conversion_features;
- device.shader_draw_parameter_features.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES;
- device.shader_draw_parameter_features.pNext = features2.pNext;
- features2.pNext = &device.shader_draw_parameter_features;
+ device.shader_draw_parameter_features.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES;
+ device.shader_draw_parameter_features.pNext = features2.pNext;
+ features2.pNext = &device.shader_draw_parameter_features;
- (*vkpGetPhysicalDeviceFeatures2)(physical_device, &features2);
- }
+ vkGetPhysicalDeviceFeatures2(physical_device, &features2);
- PFN_vkGetPhysicalDeviceExternalFenceProperties
- vkpGetPhysicalDeviceExternalFenceProperties =
- reinterpret_cast<PFN_vkGetPhysicalDeviceExternalFenceProperties>(
- vkGetInstanceProcAddr(
- instance, "vkGetPhysicalDeviceExternalFenceProperties"));
- if (vkpGetPhysicalDeviceExternalFenceProperties) {
- VkPhysicalDeviceExternalFenceInfo external_fence_info = {
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO, nullptr,
- VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT};
- VkExternalFenceProperties external_fence_properties = {};
+ VkPhysicalDeviceExternalFenceInfo external_fence_info = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO, nullptr,
+ VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT};
+ VkExternalFenceProperties external_fence_properties = {};
- for (VkExternalFenceHandleTypeFlagBits handle_type =
- VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT;
- handle_type <= VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT;
- handle_type = static_cast<VkExternalFenceHandleTypeFlagBits>(
- handle_type << 1)) {
- external_fence_info.handleType = handle_type;
- (*vkpGetPhysicalDeviceExternalFenceProperties)(
- physical_device, &external_fence_info, &external_fence_properties);
- if (external_fence_properties.exportFromImportedHandleTypes ||
- external_fence_properties.compatibleHandleTypes ||
- external_fence_properties.externalFenceFeatures) {
- device.external_fence_properties.insert(
- std::make_pair(handle_type, external_fence_properties));
- }
+ for (VkExternalFenceHandleTypeFlagBits handle_type =
+ VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT;
+ handle_type <= VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT;
+ handle_type =
+ static_cast<VkExternalFenceHandleTypeFlagBits>(handle_type << 1)) {
+ external_fence_info.handleType = handle_type;
+ vkGetPhysicalDeviceExternalFenceProperties(
+ physical_device, &external_fence_info, &external_fence_properties);
+ if (external_fence_properties.exportFromImportedHandleTypes ||
+ external_fence_properties.compatibleHandleTypes ||
+ external_fence_properties.externalFenceFeatures) {
+ device.external_fence_properties.insert(
+ std::make_pair(handle_type, external_fence_properties));
}
}
- PFN_vkGetPhysicalDeviceExternalSemaphoreProperties
- vkpGetPhysicalDeviceExternalSemaphoreProperties = reinterpret_cast<
- PFN_vkGetPhysicalDeviceExternalSemaphoreProperties>(
- vkGetInstanceProcAddr(
- instance, "vkGetPhysicalDeviceExternalSemaphoreProperties"));
- if (vkpGetPhysicalDeviceExternalSemaphoreProperties) {
- VkPhysicalDeviceExternalSemaphoreInfo external_semaphore_info = {
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, nullptr,
- VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT};
- VkExternalSemaphoreProperties external_semaphore_properties = {};
+ VkPhysicalDeviceExternalSemaphoreInfo external_semaphore_info = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, nullptr,
+ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT};
+ VkExternalSemaphoreProperties external_semaphore_properties = {};
- for (VkExternalSemaphoreHandleTypeFlagBits handle_type =
- VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;
- handle_type <= VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
- handle_type = static_cast<VkExternalSemaphoreHandleTypeFlagBits>(
- handle_type << 1)) {
- external_semaphore_info.handleType = handle_type;
- (*vkpGetPhysicalDeviceExternalSemaphoreProperties)(
- physical_device, &external_semaphore_info,
- &external_semaphore_properties);
- if (external_semaphore_properties.exportFromImportedHandleTypes ||
- external_semaphore_properties.compatibleHandleTypes ||
- external_semaphore_properties.externalSemaphoreFeatures) {
- device.external_semaphore_properties.insert(
- std::make_pair(handle_type, external_semaphore_properties));
- }
+ for (VkExternalSemaphoreHandleTypeFlagBits handle_type =
+ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;
+ handle_type <= VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+ handle_type = static_cast<VkExternalSemaphoreHandleTypeFlagBits>(
+ handle_type << 1)) {
+ external_semaphore_info.handleType = handle_type;
+ vkGetPhysicalDeviceExternalSemaphoreProperties(
+ physical_device, &external_semaphore_info,
+ &external_semaphore_properties);
+ if (external_semaphore_properties.exportFromImportedHandleTypes ||
+ external_semaphore_properties.compatibleHandleTypes ||
+ external_semaphore_properties.externalSemaphoreFeatures) {
+ device.external_semaphore_properties.insert(
+ std::make_pair(handle_type, external_semaphore_properties));
}
}
}
@@ -355,19 +301,15 @@
if (!EnumerateExtensions(nullptr, &instance.extensions))
return VkJsonInstance();
- std::vector<const char*> instance_extensions;
- for (const auto extension : kSupportedInstanceExtensions) {
- if (HasExtension(extension, instance.extensions))
- instance_extensions.push_back(extension);
- }
-
- const VkApplicationInfo app_info = {VK_STRUCTURE_TYPE_APPLICATION_INFO,
- nullptr,
- "vkjson_info",
- 1,
- "",
- 0,
- VK_API_VERSION_1_1};
+ const VkApplicationInfo app_info = {
+ VK_STRUCTURE_TYPE_APPLICATION_INFO,
+ nullptr,
+ "vkjson_info",
+ 1,
+ "",
+ 0,
+ VK_API_VERSION_1_1,
+ };
VkInstanceCreateInfo instance_info = {
VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
nullptr,
@@ -375,8 +317,9 @@
&app_info,
0,
nullptr,
- static_cast<uint32_t>(instance_extensions.size()),
- instance_extensions.data()};
+ 0,
+ nullptr,
+ };
VkInstance vkinstance;
result = vkCreateInstance(&instance_info, nullptr, &vkinstance);
if (result != VK_SUCCESS)
@@ -401,56 +344,42 @@
instance.devices.reserve(sz);
for (uint32_t i = 0; i < sz; ++i) {
device_map.insert(std::make_pair(devices[i], i));
- instance.devices.emplace_back(VkJsonGetDevice(vkinstance, devices[i],
- instance_extensions.size(),
- instance_extensions.data()));
+ instance.devices.emplace_back(VkJsonGetDevice(devices[i]));
}
- PFN_vkEnumerateInstanceVersion vkpEnumerateInstanceVersion =
- reinterpret_cast<PFN_vkEnumerateInstanceVersion>(
- vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceVersion"));
- if (!vkpEnumerateInstanceVersion) {
- instance.api_version = VK_API_VERSION_1_0;
- } else {
- result = (*vkpEnumerateInstanceVersion)(&instance.api_version);
- if (result != VK_SUCCESS) {
- vkDestroyInstance(vkinstance, nullptr);
- return VkJsonInstance();
- }
+ result = vkEnumerateInstanceVersion(&instance.api_version);
+ if (result != VK_SUCCESS) {
+ vkDestroyInstance(vkinstance, nullptr);
+ return VkJsonInstance();
}
- PFN_vkEnumeratePhysicalDeviceGroups vkpEnumeratePhysicalDeviceGroups =
- reinterpret_cast<PFN_vkEnumeratePhysicalDeviceGroups>(
- vkGetInstanceProcAddr(vkinstance, "vkEnumeratePhysicalDeviceGroups"));
- if (vkpEnumeratePhysicalDeviceGroups) {
- count = 0;
- result = (*vkpEnumeratePhysicalDeviceGroups)(vkinstance, &count, nullptr);
- if (result != VK_SUCCESS) {
- vkDestroyInstance(vkinstance, nullptr);
- return VkJsonInstance();
- }
+ count = 0;
+ result = vkEnumeratePhysicalDeviceGroups(vkinstance, &count, nullptr);
+ if (result != VK_SUCCESS) {
+ vkDestroyInstance(vkinstance, nullptr);
+ return VkJsonInstance();
+ }
- VkJsonDeviceGroup device_group;
- std::vector<VkPhysicalDeviceGroupProperties> group_properties;
- group_properties.resize(count);
- for (auto& properties : group_properties) {
- properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES;
- properties.pNext = nullptr;
+ VkJsonDeviceGroup device_group;
+ std::vector<VkPhysicalDeviceGroupProperties> group_properties;
+ group_properties.resize(count);
+ for (auto& properties : group_properties) {
+ properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES;
+ properties.pNext = nullptr;
+ }
+ result = vkEnumeratePhysicalDeviceGroups(vkinstance, &count,
+ group_properties.data());
+ if (result != VK_SUCCESS) {
+ vkDestroyInstance(vkinstance, nullptr);
+ return VkJsonInstance();
+ }
+ for (auto properties : group_properties) {
+ device_group.properties = properties;
+ for (uint32_t i = 0; i < properties.physicalDeviceCount; ++i) {
+ device_group.device_inds.push_back(
+ device_map[properties.physicalDevices[i]]);
}
- result = (*vkpEnumeratePhysicalDeviceGroups)(vkinstance, &count,
- group_properties.data());
- if (result != VK_SUCCESS) {
- vkDestroyInstance(vkinstance, nullptr);
- return VkJsonInstance();
- }
- for (auto properties : group_properties) {
- device_group.properties = properties;
- for (uint32_t i = 0; i < properties.physicalDeviceCount; ++i) {
- device_group.device_inds.push_back(
- device_map[properties.physicalDevices[i]]);
- }
- instance.device_groups.push_back(device_group);
- }
+ instance.device_groups.push_back(device_group);
}
vkDestroyInstance(vkinstance, nullptr);